From dc4d63b18742bac6675488e2885fc070314231b8 Mon Sep 17 00:00:00 2001 From: Bert Date: Fri, 13 Sep 2024 21:58:58 +0200 Subject: [PATCH 01/46] 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 02/46] 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 03/46] 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 04/46] 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 05/46] 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 06/46] 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 07/46] 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 08/46] 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 09/46] 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 10/46] 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 11/46] 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 12/46] 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 13/46] 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 14/46] 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 15/46] 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 16/46] 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 17/46] 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 18/46] 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 19/46] 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 20/46] 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 21/46] 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 22/46] 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 23/46] 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 24/46] 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 25/46] 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 26/46] 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 27/46] 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 28/46] 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 29/46] 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 30/46] 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 31/46] 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 32/46] 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 33/46] 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 34/46] 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 35/46] 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 36/46] 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 37/46] 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 38/46] 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 39/46] 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 40/46] 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 41/46] 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 42/46] 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 43/46] 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 44/46] 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 45/46] 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 46/46] 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 }