Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-711: Completion of fundamental Payment Adjuster and its peripheries #442

Open
wants to merge 199 commits into
base: master
Choose a base branch
from

Conversation

bertllll
Copy link
Contributor

No description provided.

Bert added 30 commits May 4, 2023 18:32
… tests for BlockchainInterfaceClandestine should be made first
Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node/src/accountant/payment_adjuster/miscellaneous/account_stages_conversions.rs:26

const BLANK_SPACE: &str = "";

pub fn format_brief_adjustment_summary(
original_account_balances_mapped: HashMap<Wallet, u128>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use Address instead of Wallet, we would end up optimising it further.

logger: &Logger,
account: &DisqualificationSuspectedAccount,
) {
info!(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that it could be overwhelming to user to see so many info notifications for each account. Ideally, it should be a summary along the lines, "X accounts haven't been paid yet, due to low MASQ balance."

) {
warning!(
logger,
"Transaction fee balance of {} wei is not going to cover the anticipated fee to send {} \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's mention how much balance the user needs to maintain to pass the tx fee barrier. Similar with the above service fee.

account.balance_wei = non_finalized_adjustment.proposed_adjusted_balance_minor;
account
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to review the comments in this file to make them more explanatory.


// 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just the disqualification limit and not the full balance

use crate::accountant::AnalyzedPayableAccount;

#[test]
fn conversion_between_non_finalized_account_and_payable_account_is_implemented() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test name inconsistent with those others

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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might look better if that 111_222_333 was assigned to a variable with a self-explaining name.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node/src/accountant/payment_adjuster/adjustment_runners.rs:118

return payment_adjuster
.begin_with_adjustment_by_transaction_fee(weighted_accounts, limit)
}
None => (),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change it to if let.

fn initialize_payment_adjuster(
now: SystemTime,
service_fee_balance: u128,
largest_exceeding_balance_recently_qualified: u128,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the name max_balance_over_threshold_of_qualified_payables.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node/src/accountant/payment_adjuster/logging_and_diagnostics/diagnostics.rs:103

payable_2: WeightedPayable,
cw_service_fee_balance_minor: u128,
expected_proposed_balance_1: u128,
expected_proposed_balance_2: u128,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use arrays for payables and expected balance with 2 elements instead of arguments.

payment_adjuster: &mut PaymentAdjusterReal,
weighted_accounts: Vec<WeightedPayable>,
) -> Self::ReturnType;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using it as a trait, it could be used as an enum.

It would make an impact on the other function:

    fn propose_adjustments_recursively<AR, RT>(
        &mut self,
        unresolved_accounts: Vec<WeightedPayable>,
        adjustment_runner: AR, // It would become an enum
    ) -> RT
    where
        AR: AdjustmentRunner<ReturnType = RT>,
    {
        diagnostics!(
            "\nUNRESOLVED QUALIFIED ACCOUNTS IN CURRENT ITERATION:",
            &unresolved_accounts
        );

        adjustment_runner.adjust_accounts(self, unresolved_accounts) // we would make it a method of self and will pass the variant
    }

n: u64,
initial_balance_minor: u128,
) -> WeightedPayable {
let mut account = make_weighed_payable(n, initial_balance_minor);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want make_weighted_payable, the t is missing.

}

#[test]
fn means_equal_requested_money_after_dsq_in_previous_iteration_to_return_capped_accounts() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A suggested new name:

fn untaken_cw_balance_equals_full_two_debts_after_loosing_an_account_results_in_constrained_balances()

Note: We had a nice discussion and it turns out you'd prefer untaken over unallocated. I think that's a great change.

#[test]
fn means_equal_requested_money_after_dsq_in_previous_iteration_to_return_capped_accounts() {
let subject = ServiceFeeOnlyAdjustmentRunner {};
let cw_service_fee_balance_minor = 10_000_000_000;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the test rename is done, please rename this variable to include untaken.

let mut account_2 = account;
account_2.bare_account.wallet = wallet_2.clone();
let adjustment = Adjustment::TransactionFeeInPriority {
affordable_transaction_count: 1,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment here to say that the limit is set here. Maybe you want to add more explanation.

let adjustment = Adjustment::TransactionFeeInPriority {
affordable_transaction_count: 1,
};
let service_fee_balance_minor = (10 * balance) / 8;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's consuming wallet balance.

@bertllll was puzzled by this number and it was enough to make the test pass.

let mut payment_adjuster = PaymentAdjusterReal::new();
payment_adjuster.initialize_inner(
service_fee_balance_minor,
adjustment,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it tx_fee_adjustment

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)];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can create the weighted_accounts in the beginning of the test.

use masq_lib::constants::WALLET_ADDRESS_LENGTH;
use std::fmt::Debug;

const PRINT_RESULTS_OF_PARTIAL_COMPUTATIONS: bool = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can say RUN_DIAGNOSTICS.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node/src/accountant/payment_adjuster/disqualification_arbiter.rs:383


#[derive(Debug, PartialEq, Eq)]
pub struct DisqualificationSuspectedAccount<'account> {
pub wallet: &'account Wallet,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you store an Address here (which implements Copy), can help you get rid of unnecessary lifetimes.

Ordering::Equal => with_smallest_weight_so_far,
},
)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better alternative to fold() is min_by_key().

https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.min_by_key

In case min_by_key fails to serve you, you can use min_by - https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.min_by

pub disqualification_limit_minor: u128,
}

impl<'unconfirmed_accounts> From<&'unconfirmed_accounts UnconfirmedAdjustment>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move it to the file which contains all the from implementations.

permanent_debt_allowed_minor: u128,
) -> u128 {
if threshold_intercept_minor == permanent_debt_allowed_minor {
return account_balance_minor;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning permanent_debt_allowed_minor is better.

return account_balance_minor;
}
let exceeding_debt_part = account_balance_minor - threshold_intercept_minor;
if DisqualificationGaugeReal::qualifies_for_double_margin(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you'd merge the if condition with the function qualifies_for_double_margin (and rename it too), then you can return the required u128.

let minimal_payment_accepted = exceeding_threshold + permanent_debt_allowed_minor;

let first_condition =
minimal_payment_accepted >= Self::FIRST_CONDITION_COEFFICIENT * considered_forgiven;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better if we make this condition lighter, by changing the constant's value to 1.

exceeding_debt_part + permanent_debt_allowed_minor
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can add the diagrams using a tool which converts "diagram into code comments", that would make the logic easier to understand.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/miscellaneous/helper_functions.rs:120


unconfirmed_adjustments.iter().for_each(|payable| {
// Condition of disqualification at the horizontal threshold
assert!(payable.proposed_adjusted_balance_minor < 120_000_000_000)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's assert on the exact proposed balance. It's preferable if you sort it and you directly put the numbers in the assert.

}

fn make_dsq_suspected_accounts(
accounts_and_dsq_edges: &[UnconfirmedAdjustment],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename it to accounts


#[derive(Debug, PartialEq, Eq)]
pub struct AdjustmentIterationResult {
pub decided_accounts: DecidedAccounts,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you'd call this field decided_accounts_opt and also make it an Option. Then we can safely eliminate the type DecidedAccounts.

}
}

pub struct TransactionCountsBy16bits {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the first glance, By16bits seemed like a mandatory constrain, since it's not, we decided to keep the name TransactionCounts.

let result = corrections_from_u16_max
.into_iter()
.map(plus_minus_correction_for_u16_max)
.map(U256::from)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's collect this vector first inside a variable and then use it to calculate result.

pub fn sum_as<N, T, F>(collection: &[T], arranger: F) -> N
where
N: From<u128>,
F: Fn(&T) -> u128,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can make it more generic and it doesn't care about u128 it'll be even better.

collection.iter().map(arranger).sum::<u128>().into()
}

pub fn weights_total(weights_and_accounts: &[WeightedPayable]) -> u128 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd prefer to use the guts directly instead of this wrapper function.

.collect()
}

fn sort_in_descendant_order_by_weights(unsorted: Vec<WeightedPayable>) -> Vec<WeightedPayable> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to use descending instead.

})
.collect::<Vec<u128>>();
find_largest_u128(&diffs)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may like to use the max() instead.

account_info.proposed_adjusted_balance_minor
});

let cw_reminder = original_cw_service_fee_balance_minor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can call it cw_remaining.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:87

} else {
not_exhausting_cw_balance_diagnostics(&non_finalized_account);

status.add(non_finalized_account)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of add(), let's call it append(), and also for the handle fn, we can say update_balance_and_append().

If we split the duties of the handle_balance_update_and_add, such that we only update the balance inside the if clause and append the account to the vector outside the if-else, it would be better.

}

struct ConsumingWalletExhaustingStatus {
remainder: u128,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided to call this remaining_cw_balance in the other place, so let's use that here too.

use std::time::SystemTime;

#[test]
fn found_zero_affordable_accounts_found_returns_true_for_non_finalized_accounts() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word found in the test name is an extra.

}

#[test]
fn found_zero_affordable_accounts_returns_true_for_finalized_accounts() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too.

}

#[test]
fn found_zero_affordable_accounts_returns_false_for_finalized_accounts() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here 😢

}

#[test]
fn three_non_exhaustive_accounts_all_refilled() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A suggested name: proposed_balance_refills_to_original_balance_for_all_three_non_exhaustive_accounts.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of In the end, we have, you'd like to say Therefore, we have.

Result<Either<Vec<QualifiedPayableAccount>, AdjustmentAnalysis>, PaymentAdjusterError>;

pub trait PaymentAdjuster {
fn search_for_indispensable_adjustment(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd like to call it consider_adjustment. That's an exceptionally simple name and I love it.

// and sticking it inside the vector that stores them.

pub type AdjustmentAnalysisResult =
Result<Either<Vec<QualifiedPayableAccount>, AdjustmentAnalysis>, PaymentAdjusterError>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You wanted to give an alias to Vec<QualifiedPayableAccount>. You may use the name UnadjustedAccounts.

now: SystemTime,
) -> Result<OutboundPaymentsInstructions, PaymentAdjusterError>;

as_any_ref_in_trait!();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't need to keep this anymore, go for it.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:203

self.initialize_inner(
initial_service_fee_balance_minor,
required_adjustment,
largest_exceeding_balance_recently_qualified,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a point to rename this, if you do rename it, reuse it.

PS: I've made peace with this name, I'd prefer if you'd not change it at all.

let largest_exceeding_balance_recently_qualified =
find_largest_exceeding_balance(&analyzed_payables);

self.initialize_inner(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we wrap the inner with a Refcell, we won't need to use &mut self, we can use &self. Since, this function returns something it's ideal.

now,
);

let sketched_debug_info_opt = self.sketch_debug_info_opt(&analyzed_payables);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to confuse info with the log level here, so prefer a rename sketch_debug_log_opt. We want to do the same on line 133 with the function complete_debug_info_if_enabled().

Also, don't forget to rename the variable name.

now: SystemTime,
) {
let transaction_fee_limitation_opt = match required_adjustment {
Adjustment::TransactionFeeInPriority {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it Adjustment::BeginByTxFee

let weighted_accounts = self.calculate_weights(analyzed_accounts);
let processed_accounts = self.propose_adjustments_recursively(
weighted_accounts,
TransactionAndServiceFeeAdjustmentRunner {},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take a look into the code to identify if we can keep either the enum Adjustment or this struct. They both are differentiating between the same thing but resulting in different combinations.

A proposed idea was to not use this TransactionAndServiceFeeAdjustmentRunner {} at all and instead use a boolean (checking if it's an initial call) with the old enum. You might need to use an Either.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:277

fn propose_adjustments_recursively<AR, RT>(
&mut self,
unresolved_accounts: Vec<WeightedPayable>,
adjustment_runner: AR,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will go away.

Have a nice day! 👨‍💻👽


fn propose_adjustments_recursively<AR, RT>(
&mut self,
unresolved_accounts: Vec<WeightedPayable>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it weighted_accounts.

fn begin_with_adjustment_by_transaction_fee(
&mut self,
weighted_accounts: Vec<WeightedPayable>,
already_known_affordable_transaction_count: u16,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it transaction_count_limit.

cw_service_fee_balance_minor,
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certain suggestions to names here:

    fn make(
        &self,
        number_of_accounts: usize, // accounts_allowed_by_tx_fee_check
        _required_service_fee_total: u128,
        cw_service_fee_balance_minor: u128,
    ) -> PaymentAdjusterError {
        PaymentAdjusterError::LateNotEnoughFeeForSingleTransaction {
            // InsufficientServiceFeePostTxFeeAdjustment
            original_number_of_accounts: self.original_number_of_accounts,
            number_of_accounts,
            original_service_fee_required_total_minor: self
                .original_service_fee_required_total_minor,
            cw_service_fee_balance_minor,
        }
    }

let error_factory = LateServiceFeeSingleTxErrorFactory::new(&weighted_accounts);

let weighted_accounts_affordable_by_transaction_fee =
dump_unaffordable_accounts_by_transaction_fee(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may use the name:

eliminate_accounts_by_tx_fee_limit()

)? {
diagnostics!("STILL NECESSARY TO CONTINUE BY ADJUSTMENT IN BALANCES");

let adjustment_result_before_verification = self
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A suggestion:

result_before_exhausting_cw_balance

fn propose_possible_adjustment_recursively(
&mut self,
weighed_accounts: Vec<WeightedPayable>,
) -> Vec<AdjustedAccountBeforeFinalization> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a vague comment. I apologise for that.

It's here because I feel the need to refactor this function such that the recursive calls are visible inside the scope of the function.

If the function follows this criteria:

fn recursive_fn() -> Something {
   if breaking_condition {
       return Something;
   } else {
       recursive_fn()
   }
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit of refactoring got us here, I hope you'll find it useful during your changes:

    fn propose_possible_adjustment_recursively_new(
        &mut self,
        weighed_accounts: Vec<WeightedPayable>,
    ) -> Vec<AdjustedAccountBeforeFinalization> {
        let disqualification_arbiter = &self.disqualification_arbiter;
        let unallocated_cw_service_fee_balance =
            self.inner.unallocated_cw_service_fee_balance_minor();
        let logger = &self.logger;

        let current_iteration_result = self.service_fee_adjuster.perform_adjustment_by_service_fee(
            weighed_accounts,
            disqualification_arbiter,
            unallocated_cw_service_fee_balance,
            logger,
        );

        let remaining_undecided_accounts = current_iteration_result.remaining_undecided_accounts;
        let decided_accounts: Vec<AdjustedAccountBeforeFinalization> =
            current_iteration_result.decided_accounts.into();
        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_under_limit(&remaining_undecided_accounts) {
            // Fast return after a direct conversion into the expected type
            Self::add_decided_accounts(
                convert_collection(remaining_undecided_accounts),
                decided_accounts,
            )
        } else {
            Self::add_decided_accounts(
                self.propose_possible_adjustment_recursively(remaining_undecided_accounts),
                decided_accounts,
            )
        };

        diagnostics!(
            "\nFINAL SET OF ADJUSTED ACCOUNTS IN CURRENT ITERATION:",
            &merged
        );

        merged
    }

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:422

.iter()
.map(|payable| {
(
payable.qualified_as.bare_account.wallet.clone(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope you'll use the Address here in case of the Wallet, it'll prevent clone().

fn complete_debug_info_if_enabled(
&self,
sketched_debug_info_opt: Option<HashMap<Wallet, u128>>,
affordable_accounts: &[PayableAccount],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may call it fully_processed_accounts instead.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:807

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mentioned that the , in this sentence is wrong.

#[test]
#[should_panic(expected = "Broken code: Called the null implementation of \
the unallocated_cw_service_fee_balance_minor() method in PaymentAdjusterInner")]
fn payment_adjuster_new_is_created_with_inner_null() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change the panic message to say:

The PaymentAdjuster Inner is uninitialised. It was detected while executing unallocated_cw_service_fee_balance_minor().

Also, inside the test, you want to replace result with subject.

#[test]
fn search_for_indispensable_adjustment_happy_path() {
init_test_logging();
let test_name = "search_for_indispensable_adjustment_gives_negative_answer";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test name is different.

Some(TestConfigForServiceFeeBalances {
account_balances: Either::Right(vec![
gwei_to_wei::<u128, u64>(85),
gwei_to_wei::<u128, u64>(15) - 1,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The u128 seems unnecessary here. There's a suggestion to use the _u64 with the numbers that are provided as arguments.

let input_3 = make_input_for_initial_check_tests(
None,
Some(TestConfigForTransactionFees {
agreed_transaction_fee_per_computed_unit_major: 100,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be gas_price.

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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mentioned that you'd prefer ( here to isolate the - 1.


let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent);

assert_eq!(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The analyzed_payables should be declared right above this assertion.

Some(TestConfigForServiceFeeBalances {
account_balances: Either::Right(vec![
gwei_to_wei::<u128, u64>(85),
gwei_to_wei::<u128, u64>(15) + 1,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more time, we can use the _u64.


let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent);

assert_eq!(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to keep analysed_payables above this assertion.

}

#[test]
fn transaction_fee_balance_is_unbearably_low_but_service_fee_balance_is_fine() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you want throws_err_for_tx_fee_but_service_fee_balance_is_fine()

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:1045

let number_of_accounts = 3;
let (qualified_payables, agent) = make_input_for_initial_check_tests(
Some(TestConfigForServiceFeeBalances {
account_balances: Either::Left(vec![123]),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're expecting 3 accounts, and the balance here is for 1 account. Maybe None is good enough.

agreed_transaction_fee_per_computed_unit_major: 100,
number_of_accounts,
estimated_transaction_fee_units_per_transaction: 55_000,
cw_transaction_fee_balance_major: 54_000 * 100,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make it closer to the edge with (55_000 * 100) - 1.

let result = subject.search_for_indispensable_adjustment(qualified_payables, &*agent);

let per_transaction_requirement_minor =
TRANSACTION_FEE_MARGIN.add_percent_to(55_000 * gwei_to_wei::<u128, u64>(100));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is noticed in different tests, and you'd want to change it everywhere in the codebase. The idea is to pass the type along with the numerals.

Let's rename TRANSACTION_FEE_MARGIN -> TX_FEE_MARGIN_IN_PERCENT.

cw_balance_minor: cw_service_fee_balance_minor,
});
let (qualified_payables, agent) =
make_input_for_initial_check_tests(service_fee_balances_config_opt, None);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to revisit how the disqualification limit is picked. You should iterate through qualified_payables and choose some value for each. Make sure that PaymentThresholdIntercept == PermanentDebtAllowed. Let's add a comment too. 😉

cw_transaction_fee_balance_minor: U256::zero(),
}),
service_fee_opt: Some(ServiceFeeImmoderateInsufficiency {
total_service_fee_required_minor: multiple_by_billion(500),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another Idea would be to create a constant:

const BILLION = 1_000_000_000;

service_fee_opt: None
},
"Current transaction fee balance is not enough to pay a single payment. Number of \
canceled payments: 4. Transaction fee per payment: 70,000,000,000,000 wei, while \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create a card for this (not a high priority, since it's just cosmetics). The intention is to use a better method to display these numbers such that a higher unit is picked, such that the number can be represented using multiple zeros.

(
PaymentAdjusterError::AllAccountsEliminated,
"The adjustment algorithm had to eliminate each payable from the recently urged \
payment due to lack of resources.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call the error: RecursionDrainedAllAccounts.

And the string to: "The payment adjuster wasn't able to compose any combination of payables that can be paid immediately with provided finances."

}

#[test]
fn tinier_but_larger_in_weight_account_is_prioritized_and_gains_up_to_its_disqualification_limit(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to discuss the name change or the test removal.

In case name change is picked, then a suggestion: account_with_larger_weight_is_prioritized_and_gains_up_to_its_disqualification_limit().

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:1194

let mut account_2 = make_analyzed_account_by_wallet("def");
let wallet_2 = account_2.qualified_as.bare_account.wallet.clone();
let balance_2 = multiple_by_billion(2_500_000);
let disqualification_limit_2 = multiple_by_billion(1_800_000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better if you create variables for weights here and you can supply them on line 1077.

None,
Some(largest_exceeding_balance),
None,
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: I think this could be done using a Builder Pattern. It'll prevent us from supplying None values.

.determine_limit_result(disqualification_limit_1)
.determine_limit_params(&determine_limit_params_arc);
subject.disqualification_arbiter =
DisqualificationArbiter::new(Box::new(disqualification_gauge));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole gauge can go away.

.unwrap();

// Let's have an example to explain why this test is important.
prove_that_proposed_adjusted_balance_could_have_exceeded_the_original_value(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new name suggestion:

illustrate_that_we_need_to_prevent_exceeding_the_original_value

unconfirmed_adjustments[1].wallet(),
&wallet_of_expected_outweighed
);
// The weight of this account grew progressively due to the additional criterion added
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't want to use the before additional criterion. Instead you'd prefer due to an additional criterion used to calculate the sum.

// 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A modified version:

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.

);
assert_eq!(
second_returned_account.proposed_adjusted_balance_minor,
2_300_000_000_000_000
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, we want this value to be equal to the disqualification_limit. All you need to do is to use the variable disqualification_limit_1 here.

balance_wei: balance_3,
last_paid_timestamp: now.checked_sub(Duration::from_secs(70_000)).unwrap(),
pending_payable_opt: None,
};
Copy link
Collaborator

@utkarshg6 utkarshg6 Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better if you explain the disqualification process using a comment.

The way you explained this test to me verbally was more explanatory than the current state of the test (along with the comments), so please add them.

Note: While restating or removing and replacing the comment below with a new one, it would help a lot if we keep it more specific that which account outplays which one (at what stage) and the reason behind it.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:1258

let calculator_mock = CriterionCalculatorMock::default()
.calculate_result(multiple_by_billion(2_000_000_000))
.calculate_result(0)
.calculate_result(0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another explanation for how this mock is being differently.

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A typo here: You want multiply.

x
),
};
let agent = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, it seems doable to use the old agent. Also, we're not even asserting on the id stamp, since all we want to look at is the error.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs: 1664

balance_wei: balance_3,
last_paid_timestamp: now.checked_sub(Duration::from_secs(160_000)).unwrap(),
pending_payable_opt: None,
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the timestamps should be the same, when I read the test, I thought these will cause some side effects that you want and think are necessary for the test. On the other hand, it just adds more confusion when figuring out the prioirity of the accounts.

I'd suggest you also create the variables for weights together with the accounts.

.calculate_result(multiple_by_billion(50_000_000_000))
.calculate_result(multiple_by_billion(50_000_000_000));
let mut subject = PaymentAdjusterReal::new();
subject.calculators.push(Box::new(calculator_mock));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd like to replace the real calculator with the mock.

You should try to refactor the test such that it is more explicit which account has the lowest priority. Also, you'd like to check how the values that are being generated for disqualification limit. So, you might want to hardcode them instead. Feel free to customise it according to your needs.

You want to check what happens if you remove the condition, this test is meant for. You'd like to change the act, so that it could be closer to the level where condition lives.

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd want to see it like this:

let service_fee_balance_in_minor_units = balance_2 + balance_3 + ((balance_1 * 10) / 100);

}

#[test]
fn overloading_with_exaggerated_debt_conditions_to_see_if_we_can_pass_through_safely() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of exaggerated_debt_conditions we can say impossible_debt_sizes.

let mut subject = PaymentAdjusterReal::new();
subject.logger = Logger::new(test_name);
// In turn, tiny cw balance
let cw_service_fee_balance = 1_000;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd like to add minor in the name of the variable.

CreditorThresholds::new(multiple_by_billion(permanent_debt_allowed_major)),
)
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want a line of demarcation here to separate the tests which have a happy path.

let balance_2 = multiple_by_billion(6_000_000_000);
let qualified_account_2 =
make_plucked_qualified_account("def", balance_2, 2_500_000_000, 2_000_000_000);
let balance_3 = multiple_by_billion(6_666_666_666);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me it is quite appealing that balance_1 + balance_3 = 2 * balance_2.

I'd be spending a lot of time figuring out the relation if I'd be reading this test on my own. If this pattern is meaningless to you, then maybe eliminate it.

wallet_addr_fragment: &str,
balance_minor: u128,
threshold_intercept_major: u128,
permanent_debt_allowed_major: u128,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An amazing idea would be if you pass the disqualification_limit instead of threshold_intercept_major and permanent_debt_allowed_major, and you'll compute these values inside the function.

balance_1.separate_with_commas(),
expected_adjusted_balance_1.separate_with_commas()
);
TestLogHandler::new().exists_log_containing(&log_msg.replace("|", ""));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's really cool. 🎉

agent: Box::new(agent),
adjustment_analysis: AdjustmentAnalysis::new(
Adjustment::TransactionFeeInPriority {
affordable_transaction_count: 2,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create a variable for the count somewhere during the preparatory statements of the test.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:1917

let balance_2 = multiple_by_billion(111_000_000);
let account_2 = make_plucked_qualified_account("def", balance_2, 50_000_000, 10_000_000);
// Account to be disqualified
let balance_3 = multiple_by_billion(600_000_000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use these points as comments for the balances. I hope it'll be easier to figure out.

  1. Second Highest Weight, Partially Paid
  2. Highest Weight, Fully Paid
  3. Third Highest Weight, Disqualified

Note: Since you'd be moving the weights to preparatory statements in the other tests, you'd prefer to do the same to this test.

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some function like:

let balance_1 = to_quintillion(100);

}

#[test]
fn late_error_after_transaction_fee_adjustment_but_rechecked_transaction_fee_found_fatally_insufficient(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new test name suggestion:

late_error_after_tx_fee_adjusted_but_rechecked_service_fee_found_fatally_insufficient

subject.logger = Logger::new(test_name);
let disqualification_arbiter = DisqualificationArbiter::default();
let disqualification_limit_2 =
disqualification_arbiter.calculate_disqualification_edge(&account_2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to calculate it by using the arbiter, instead you can take it out of the account.

let err = match result {
Ok(_) => panic!("expected an error but got Ok()"),
Err(e) => e,
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should revisit the declaration of the agent, to make it use debug. It would allow us to use unwrap_err() here.


struct TestConfigForServiceFeeBalances {
// Either gwei or wei
account_balances: Either<Vec<u64>, Vec<u128>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can only use one unit, that'll be better, maybe Wei, because precision is important.

number_of_accounts: usize,
estimated_transaction_fee_units_per_transaction: u64,
cw_transaction_fee_balance_major: u64,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change agreed_transaction_fee_per_computed_unit_major -> gas_price and estimated_transaction_fee_units_per_transaction -> tx_computation_units

Note: It's preferable if the rename can be done globally.


fn make_input_for_initial_check_tests(
service_fee_balances_config_opt: Option<TestConfigForServiceFeeBalances>,
transaction_fee_config_opt: Option<TestConfigForTransactionFees>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can just call them:

service_fee_config_opt and tx_fee_config_opt

It'll be symmetric.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/mod.rs:2060

account_balances: Either::Left(vec![1, 1]),
cw_balance_minor: u64::MAX as u128,
})
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you implement the Default for TestConfigForServiceFeeBalances. Then we could use the default() directly inside the tests, and there won't be need of this function.

estimated_transaction_fee_units_per_transaction: 55_000,
cw_transaction_fee_balance_major: u64::MAX,
})
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think inside the tests, we can do something like this.

let config = config_opt.unwrap_or(default_tx_fee_config(accounts_count));

})
.collect()
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might like to refactor it using a new function:

fn prepare_payable_accounts_from(Either<Vec<u128>, u64>);

You may call this new function in both of the scopes of if-else statement.

.collect()
}

fn make_agent(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mentioned, it would be better if we use prepare_agent instead of make_agent.

let min = (current_result * 97) / 100;
let max = (current_result * 97) / 100;
assert_ne!(current_result, 0);
assert!(min <= previous_result || previous_result <= max);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two changes that we need here:

  • The min and max should be different (97 and 103)
  • We can assert for not in the range by using assert_ne!(min <= previous_result && previous_result <= max);

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node/src/accountant/payment_adjuster/mod.rs:2191

let input_matrix: InputMatrixConfigurator =
|(nominal_account_1, nominal_account_2, _now)| {
vec![
// First test case: BalanceCalculator
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe here you can say as a comment, "Test Case for only 1st Calculator"


#[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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to make this sentence more explicit, "you need to make an array of inputs" -> "you need to make a 2-element array"


type InputMatrixConfigurator = fn(
(QualifiedPayableAccount, QualifiedPayableAccount, SystemTime),
) -> Vec<[(QualifiedPayableAccount, u128); 2]>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you prefer to convert this into a struct, I think it's good.

struct CalculatorTestScenario {
   account: QualifiedPayableAccount,
   expected_weight: u128
}
type InputMatrixConfigurator = fn([QualifiedPayableAccount; 2], SystemTime) -> Vec<[CalculatorTestScenario; 2]>

let mut account_2 = nominal_account_2;
account_2.bare_account.balance_wei += 999_999_999;
[(account_1, 8000001876543209), (account_2, 8000000999999999)]
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to add a comment here:

// Your new scenario for a unique calculator will live here

}

struct ExpectedWeightWithWallet {
wallet: Wallet,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I'd like Address here instead of Wallet.

now,
cw_service_fee_balance_minor,
);
assert_eq!(template_computed_weight.common_weight, nominal_weight);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was an idea that you mentioned that you'd like to make nominal_weight a constant. I think it's a great idea. Let's do that.

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 "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're asserting on equality this statement will make sense when the length of matrix is less than the calculators_count but if it's greater, the statement will loose it meaning.

Either change the assertion or the message according to your convenience.

cw_service_fee_balance_minor: u128,
template_computed_weight: TemplateComputedWeight,
) {
fn prepare_args_expected_weights_for_comparison(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd like to rename this to prepare_inputs_with_expected_weights.

);

assert_results(
weighted_accounts,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call this actual_weighted_accounts.

}

fn assert_results(
weighted_accounts: Vec<WeightedPayable>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here, actual_weighted_accounts.


pub trait DisqualificationLimitProvidingAccount {
fn disqualification_limit(&self) -> u128;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can combine the trait, it'll be more concise:

So, if you think generic name is the only limitation, then this is the name we both came up with: AccountWithBalanceAndDisqLimit

where
Product: BalanceProvidingAccount + DisqualificationLimitProvidingAccount,
{
fn prepare_analyzable_account(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provide_account_with_disqualification_limit is a new name suggestion by @bertllll

}
}

// The thin term "outweighed account" coma from a phenomenon with an account whose weight
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want comes instead of coma.

// 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have some ideas to improve it, go ahead and do it...

fn handle_winning_accounts(
unconfirmed_adjustments: Vec<UnconfirmedAdjustment>,
) -> Either<Vec<UnconfirmedAdjustment>, AdjustmentIterationResult> {
let (thriving_competitors, losing_competitors) =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call them accounts_above_disq_limit and accounts_below_disq_limit.

}

#[derive(Default)]
pub struct AdjustmentComputer {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is no field stored in this struct (and in case we plan not to add it in a near future), I'd suggest, we convert this function implemented in the impl block into a pure function.

.into_iter()
.map(|weighted_account| {
let proposed_adjusted_balance =
compute_proposed_adjusted_balance(weighted_account.weight);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer if we limit the balance to the disqualification limit inside this function here.

If you want to keep the calculations for historical reasons, maybe prefer printing the diagnostics inside this function with both the over-computed and amended values.


lazy_static! {
pub static ref MAX_POSSIBLE_SERVICE_FEE_BALANCE_IN_MINOR: u128 =
MASQ_TOTAL_SUPPLY as u128 * 10_u128.pow(18);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd like to use the new to_quintillion() function here.

pub static ref ONE_MONTH_LONG_DEBT_SEC: u64 = 30 * 24 * 60 * 60;
}

pub fn make_initialized_subject(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be better if we use the builder method here.

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead multiply by a billion.

fn estimated_transaction_fee_total(&self, _number_of_transactions: usize) -> u128 {
self.log_function_call("estimated_transaction_fee_total()");
fn estimated_transaction_fee_per_transaction_minor(&self) -> u128 {
self.log_function_call("estimated_transaction_fee_per_transaction_minor()");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd like to change it to gas_price.

let gwei_amount = ((77_777 + WEB3_MAXIMAL_GAS_LIMIT_MARGIN) as u128) * 244;
gwei_to_wei(gwei_amount)
};
assert_eq!(result, expected_result);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you'd provide a number here, it's appreciated.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue from node/src/accountant/payment_adjuster/non_unit_tests/mod.rs:295

(thresholds.maturity_threshold_sec + thresholds.threshold_interval_sec) as usize,
) / 2
};
let debt_age = generate_age_segment() + generate_age_segment();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You told that generating the age from starting a number fairly different from 1 isn't right and instead you should start from maturity_threshold_sec.

Instead of dividing by 2, you'd prefer to multiply it with 2/3 or 3/4.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/non_unit_tests/mod.rs:628

use thousands::Separable;
use web3::types::U256;

#[test]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the location of the generated file:

node/generated/test/payment_adjuster/tests/home/loading_test_output.txt

Regarding adding a library, I don't want you to get in trouble with that. It might not produces anything substantial that you're happy with.

) / 2
};
let debt_age = generate_age_segment() + generate_age_segment();
let service_fee_balance_minor = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Computing the service fee balance raises a lot of questions. So a comment could be helpful here explaining that it basically provides different magnitudes in the produced numbers.

thresholds: Either<Vec<PaymentThresholds>, HashMap<Wallet, PaymentThresholds>>,
}

fn try_make_qualified_payables_by_applied_thresholds(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd like to move this fn closer to make_payable_account.

With 'AllAccountsEliminated':.......... {}\n\
With late insufficient balance errors:. {}\n\n\
Legend\n\
Partially adjusted accounts mark:...... {}",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding an explanation of X would be a good idea.

let balance_average = {
let sum: u128 = sum_as(&qualified_payables, |account| account.balance_minor());
sum / accounts_count as u128
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The numbers are picked by experimenting different values. (Empirical Values)

accounts
.iter()
.map(|account| AccountInfo {
wallet: account.qualified_as.bare_account.wallet.clone(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Address will be more efficient.

let test_overall_output_collector =
scenario_results
.into_iter()
.fold(overall_output_collector, |acc, scenario_result| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, let's use the same name as the variable by using shadowing.

.fold(overall_output_collector, |acc, scenario_result| {
do_final_processing_of_single_scenario(&mut file, acc, scenario_result)
});
let total_scenarios_evaluated = test_overall_output_collector
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you could make a method of this structure, it would be better.

+ test_overall_output_collector.oks
+ test_overall_output_collector.all_accounts_eliminated
+ test_overall_output_collector.late_immoderately_insufficient_service_fee_balance;
write_brief_test_summary_into_file(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may include the word tail here.

let ok_adjustment_percentage = (test_overall_output_collector.oks * 100)
/ (total_scenarios_evaluated
- test_overall_output_collector.scenarios_denied_before_adjustment_started);
let required_success_rate = 70;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's replace rate with percent here and also in the required_pass_rate. Maybe all the places.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs

file: &mut File,
overall_output_collector: &TestOverallOutputCollector,
number_of_requested_scenarios: usize,
total_of_scenarios_evaluated: usize,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you'd like to shorten the names with scenarios_requated and scenarios_evaluated.

) -> TestOverallOutputCollector {
match scenario.result {
Ok(positive) => {
if positive.were_no_accounts_eliminated {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you'd like to just say, no_accounts_eliminated.

.collected_fulfillment_percentages
.push(positive.portion_of_cw_cumulatively_used_percents)
}
if positive.common.required_adjustment == Adjustment::ByServiceFee {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use matches!() here too, for symmetry. - @bertllll

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\
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found txt a bit confusing, so please change it to tx wherever possible.

Another suggestion by @bertllll - For symmetry add indentation so that all the data is in a column.

.separate_with_commas(),
portion_of_cw_used_percents,
resolve_affordable_transaction_count(&scenario_common.required_adjustment),
resolve_comment_on_thresholds(&scenario_common.used_thresholds)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two fns can be converted into methods.

account,
set_of_individual_thresholds_opt.map(|thresholds| {
thresholds
.get(&account.wallet())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the relation in HashMap is Address -> PaymentThresholds, it would be a bit more efficient.


const NON_EXHAUSTED_ACCOUNT_MARKER: &str = "# # # # # # # #";

fn resolve_account_ending_status_graphically(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of ending_status it could be fulfilment_status.

result_b.info.initially_requested_service_fee_minor,
),
)
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to read. Please add the comment explaining that:

The priority order will be such that - Percentage Descending, Service Fee Ascending

Ord::cmp(
&result_a.info.initially_requested_service_fee_minor,
&result_b.info.initially_requested_service_fee_minor,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Descending order would be more appropriate here.

}

impl AppliedThresholds {
fn fix_individual_thresholds_if_needed(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it switch_to_hashmap_if_needed - @bertllll

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/payment_adjuster/preparatory_analyser/mod.rs:535

{
let number_of_accounts = qualified_payables.len();
let cw_transaction_fee_balance_minor = agent.transaction_fee_balance_minor();
let per_transaction_requirement_minor =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call it gas_price.

number_of_accounts: usize,
transaction_fee_check_result: Result<Option<u16>, TransactionFeeImmoderateInsufficiency>,
service_fee_check_result: Result<(), ServiceFeeImmoderateInsufficiency>,
) -> Result<Option<u16>, PaymentAdjusterError> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we create an alias here to identify ConstrainedTxCount, it would make code more readable.

analyzed_accounts_count,
required_service_fee_total,
cw_service_fee_balance_minor,
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you could pass the prepared_accounts in the make() and would perform the computations there.

) {
init_test_logging();
let determine_limit_params_arc = Arc::new(Mutex::new(vec![]));
let disqualification_gauge = double_mock_results_queue(disqualification_gauge)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A different name: make_mock_results_doubled (suggested by @bertllll )

.agreed_transaction_fee_margin_result(*TRANSACTION_FEE_MARGIN)
.transaction_fee_balance_minor_result(U256::MAX)
.estimated_transaction_fee_per_transaction_minor_result(123456)
.service_fee_balance_minor_result(cw_service_fee_balance);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it could be migrated into a function, go ahead! 🫡


fn test_not_enough_for_even_the_least_demanding_account_causes_error<F, ErrorFactory, Error>(
error_factory: ErrorFactory,
expected_error_preparer: F,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another suggestion by @bertllll - prepare_expected_error.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue at node/src/accountant/scanners/mod.rs:325

}

#[test]
fn not_enough_for_even_the_least_demanding_account_error_right_after_positive_tx_fee_check() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new name by @bertllll -

least_demanding_account_error_in_pre_adjustment_stage

fn not_enough_for_even_the_least_demanding_account_error_right_after_positive_tx_fee_check() {
let error_factory = EarlyServiceFeeSingleTXErrorFactory::default();

let expected_error_preparer =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a better name

let original_number_of_accounts = original_accounts.len();
let initial_sum = sum_as(&original_accounts, |account| account.balance_minor());
let error_factory = LateServiceFeeSingleTxErrorFactory::new(&original_accounts);
let expected_error_preparer = |number_of_accounts, _, cw_service_fee_balance_minor| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename the act here to say,prepare_expected_error.

Please fix it in the previous test too.

}

#[test]
fn not_enough_for_even_the_least_demanding_account_error_right_after_tx_fee_accounts_dump() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here also, you want to start with just the least_demanding_account_...

#[test]
fn not_enough_for_even_the_least_demanding_account_error_right_after_tx_fee_accounts_dump() {
let original_accounts = vec![
make_weighed_account(123),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small typo here - make_weighted_account

.balance_wei = balance_2;
let accounts = vec![weighted_account_1, weighted_account_2];
let service_fee_totally_required_minor = balance_1 + balance_2;
let cw_service_fee_balance_minor = service_fee_totally_required_minor + 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a comment explaining why we're adding 1 here.

}

fn double_mock_results_queue(mock: DisqualificationGaugeMock) -> DisqualificationGaugeMock {
let originally_prepared_results = (0..2)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call the variable popped_results

2 -> len_of_queue
4 -> 2 * len_of_queue

your balances can cover neither reasonable portion of any of those payables \
recently qualified for an imminent payment. You must add more funds into your \
consuming wallet in order to stay off delinquency bans that your creditors may \
apply against you otherwise. Details: {}.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use ChatGPT to reword into a single sentence.

Copy link
Collaborator

@utkarshg6 utkarshg6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally, it's done! 👏

You don't know how happy I am.

Good Luck with the changes, I wish you'll enjoy the process.

account,
&self.common.payment_thresholds,
now,
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm particularly not fond of the idea to waste your time on asking you to refactor this change but if you'd like to spare some time on this function then please take a look whether we can construct PayableInspector with PaymentThresholds so that we don't need to pass a reference here.

@@ -801,7 +835,7 @@ impl PendingPayableScanner {
}
}

fn add_to_the_total_of_paid_payable(
fn add_percent_to_the_total_of_paid_payable(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's revert it back to it's original name here.

@@ -1276,7 +1325,7 @@ mod tests {
let test_name = "payable_scanner_can_initiate_a_scan";
let now = SystemTime::now();
let (qualified_payable_accounts, _, all_non_pending_payables) =
make_payables(now, &PaymentThresholds::default());
make_unqualified_and_qualified_payables(now, &PaymentThresholds::default());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please call this function to make_qualified_and_unqualified_payables because the first argument of the returned tuple is qualified payables.

self
)
&mut self.receivable_dao_factory,
ReceivableDaoFactoryMock::new(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two changes:

  1. Let's append _opt in the name of the factory.
  2. You can convert the argument of this function to Either so that it accepts only one.

@@ -1086,6 +1099,9 @@ impl PayableScannerBuilder {
payable_dao: PayableDaoMock::new(),
pending_payable_dao: PendingPayableDaoMock::new(),
payment_thresholds: PaymentThresholds::default(),
payable_inspector: PayableInspector::new(Box::new(
PayableThresholdsGaugeReal::default(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's provide a Mock instead.

adjust_payments_results: RefCell<Vec<OutboundPaymentsInstructions>>,
Arc<Mutex<Vec<(Vec<QualifiedPayableAccount>, ArbitraryIdStamp)>>>,
search_for_indispensable_adjustment_results: RefCell<
Vec<Result<Either<Vec<QualifiedPayableAccount>, AdjustmentAnalysis>, PaymentAdjusterError>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you create a Type for the Either part, then don't forget to modify the code in the mocks too.

@@ -1699,3 +1734,88 @@ impl ScanSchedulers {
}
}
}

pub fn make_non_guaranteed_qualified_payable(n: u64) -> QualifiedPayableAccount {
// Without guarantee that the generated payable would cross the given thresholds
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// It's not guaranteed that the payables would cross the given thresholds.

QualifiedPayableAccount::new(
make_payable_account(n),
n as u128 * 12345,
CreditorThresholds::new(111_111_111),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't be hardcoding the creditor thresholds ans instead you should be using the n.

}

pub fn make_analyzed_account(n: u64) -> AnalyzedPayableAccount {
AnalyzedPayableAccount::new(make_non_guaranteed_qualified_payable(n), 123456789)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too, use the n for the second argument.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants