mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-19 13:20:54 +00:00
test: Propagate fixCleanup3_4_0 impair gate into shared lending helpers
Gating impair on `fixCleanup3_4_0` switched the "reject not-yet-late impair with tecTOO_SOON" behaviour from an amendment excluded by `LoanTestBase::all_` to one that is included. Every shared helper that called `manage(tfLoanImpair)` immediately after loan creation started returning tecTOO_SOON, cascading into ~7.8k assertion failures across LoanLifecycle, LoanSet, LoanMisc, LoanPay, LoanRounding, LoanInvariants and LoanCoverFreezeAuth. Fixes: - Add `advancePastDueDate(env, loanKeylet)` helper to LoanTestBase.h. Advances the ledger past `sfNextPaymentDueDate` under the fix and is a no-op otherwise. - `defaultImmediately`: advance past due date before impair and gate the legacy `state.nextPaymentDate = env.now()` mutation on the fix state (the fix preserves `sfNextPaymentDueDate`). - `combineAllPayments` payment loop: only impair when the loan is actually late so periodic-payment iterations are not perturbed. - `lifecycle` inline impair/unimpair sub-block: keep the pre-amendment `tesSUCCESS`/`tecLIMIT_EXCEEDED` path when the fix is off; assert the new `tecTOO_SOON` (impair) + `tecNO_PERMISSION` (unimpair-on-unimpaired) contract when the fix is on. Advancing here would push the loan into a late state and break the downstream `toEndOfLife` payment flows with `tecEXPIRED`. The tesSUCCESS impair branch is separately covered by LoanSecurity_test.cpp and LoanCashBasis_test.cpp under the fix. - LoanCoverFreezeAuth_test.cpp `testWithdrawReflectsUnrealizedLoss`: advance past due date before the direct impair call. - LoanCashBasis_test.cpp: drop the now-duplicated local `advancePastDueDate` lambda in favour of the base helper.
This commit is contained in:
@@ -542,17 +542,6 @@ private:
|
||||
return std::make_tuple(broker, loanKeylet, lender, borrower);
|
||||
};
|
||||
|
||||
// Under fixCleanup3_4_0 impairment is only allowed once the
|
||||
// payment is late. Advance past the first due date before impairing.
|
||||
auto advancePastDueDate = [&](Env& env, Keylet const& loanKeylet) {
|
||||
if (!env.current()->rules().enabled(fixCleanup3_4_0))
|
||||
return;
|
||||
auto const loan = env.le(loanKeylet);
|
||||
BEAST_EXPECT(loan);
|
||||
std::uint32_t const dueDate = loan->at(sfNextPaymentDueDate);
|
||||
env.close(NetClock::time_point{NetClock::duration{dueDate}} + 1s);
|
||||
};
|
||||
|
||||
// ---- impair / unimpair ----
|
||||
auto runImpairUnimpair = [&](FeatureBitset features) {
|
||||
Env env(*this, features);
|
||||
|
||||
@@ -236,6 +236,9 @@ private:
|
||||
Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
// Under fixCleanup3_4_0 impair requires the payment to be late.
|
||||
advancePastDueDate(env, loanKeylet);
|
||||
|
||||
// Impair the loan to create unrealized loss
|
||||
env(manage(lender, loanKeylet.key, tfLoanImpair), Ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
@@ -622,7 +622,8 @@ private:
|
||||
Number const principalRequest{1, 3};
|
||||
auto createNewLoan = [&]() {
|
||||
auto const sleBroker = env.le(keylet::loanBroker(broker.brokerID));
|
||||
BEAST_EXPECT(sleBroker);
|
||||
if (!BEAST_EXPECT(sleBroker))
|
||||
return keylet::loan(uint256{});
|
||||
auto const lk =
|
||||
keylet::loan(broker.brokerID, SeqProxy::rawSequence(sleBroker->at(sfLoanSequence)));
|
||||
env(set(borrower, broker.brokerID, broker.asset(principalRequest).value()),
|
||||
|
||||
@@ -646,6 +646,23 @@ protected:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Under fixCleanup3_4_0, LoanManage rejects tfLoanImpair with tecTOO_SOON
|
||||
// unless the loan payment is already late. Advance the ledger past the
|
||||
// loan's sfNextPaymentDueDate so shared lifecycle flows still exercise
|
||||
// the tesSUCCESS branch when the amendment is active. No-op when the
|
||||
// amendment is disabled.
|
||||
void
|
||||
advancePastDueDate(jtx::Env& env, Keylet const& loanKeylet)
|
||||
{
|
||||
if (!env.current()->rules().enabled(fixCleanup3_4_0))
|
||||
return;
|
||||
auto const loan = env.le(loanKeylet);
|
||||
if (!BEAST_EXPECT(loan))
|
||||
return;
|
||||
std::uint32_t const dueDate = loan->at(sfNextPaymentDueDate);
|
||||
env.close(NetClock::time_point{NetClock::duration{dueDate}} + std::chrono::seconds{1});
|
||||
}
|
||||
|
||||
enum class AssetType { XRP = 0, IOU = 1, MPT = 2 };
|
||||
|
||||
// Specify the accounts as params to allow other accounts to be used
|
||||
@@ -1564,12 +1581,30 @@ protected:
|
||||
|
||||
// Check the vault
|
||||
bool const canImpair = canImpairLoan(env, broker, state);
|
||||
// Impair the loan, if possible
|
||||
env(manage(lender, keylet.key, tfLoanImpair),
|
||||
canImpair ? Ter(tesSUCCESS) : Ter(tecLIMIT_EXCEEDED));
|
||||
// Unimpair the loan
|
||||
env(manage(lender, keylet.key, tfLoanUnimpair),
|
||||
canImpair ? Ter(tesSUCCESS) : Ter(tecNO_PERMISSION));
|
||||
// Under fixCleanup3_4_0, impair rejects a not-yet-late loan with
|
||||
// tecTOO_SOON. Advancing time to satisfy the gate here would push
|
||||
// the loan into a "late" state and break the toEndOfLife flows
|
||||
// (singlePayment/fullPayment) that expect a fresh loan without the
|
||||
// tfLoanLatePayment flag. The tesSUCCESS/tecLIMIT_EXCEEDED impair
|
||||
// path is already covered under fixCleanup3_4_0 by dedicated tests
|
||||
// in LoanSecurity_test.cpp and LoanCashBasis_test.cpp.
|
||||
if (!env.current()->rules().enabled(fixCleanup3_4_0))
|
||||
{
|
||||
// Impair the loan, if possible
|
||||
env(manage(lender, keylet.key, tfLoanImpair),
|
||||
canImpair ? Ter(tesSUCCESS) : Ter(tecLIMIT_EXCEEDED));
|
||||
// Unimpair the loan
|
||||
env(manage(lender, keylet.key, tfLoanUnimpair),
|
||||
canImpair ? Ter(tesSUCCESS) : Ter(tecNO_PERMISSION));
|
||||
}
|
||||
else
|
||||
{
|
||||
// With the fix on, a not-yet-late loan can never be impaired
|
||||
// (tecTOO_SOON) and the follow-up unimpair on an unimpaired
|
||||
// loan is still tecNO_PERMISSION.
|
||||
env(manage(lender, keylet.key, tfLoanImpair), Ter(tecTOO_SOON));
|
||||
env(manage(lender, keylet.key, tfLoanUnimpair), Ter(tecNO_PERMISSION));
|
||||
}
|
||||
|
||||
auto const nextDueDate = startDate + *loanParams.payInterval;
|
||||
|
||||
@@ -2160,6 +2195,11 @@ protected:
|
||||
{
|
||||
// Check the vault
|
||||
bool const canImpair = canImpairLoan(env, broker, state);
|
||||
// Under fixCleanup3_4_0 impair requires the payment to
|
||||
// already be late. Advance past the loan's next due
|
||||
// date so this exercises the tesSUCCESS branch. No-op
|
||||
// when the fix is disabled.
|
||||
advancePastDueDate(env, loanKeylet);
|
||||
// Impair the loan, if possible
|
||||
env(manage(lender, loanKeylet.key, tfLoanImpair),
|
||||
canImpair ? Ter(tesSUCCESS) : Ter(tecLIMIT_EXCEEDED));
|
||||
@@ -2167,7 +2207,11 @@ protected:
|
||||
if (canImpair)
|
||||
{
|
||||
state.flags |= tfLoanImpair;
|
||||
state.nextPaymentDate = env.now().time_since_epoch().count();
|
||||
// Prior to fixCleanup3_4_0 impair rewrote
|
||||
// sfNextPaymentDueDate to parentCloseTime. Under the
|
||||
// fix, the due date is preserved.
|
||||
if (!env.current()->rules().enabled(fixCleanup3_4_0))
|
||||
state.nextPaymentDate = env.now().time_since_epoch().count();
|
||||
|
||||
// Once the loan is impaired, it can't be impaired again
|
||||
env(manage(lender, loanKeylet.key, tfLoanImpair), Ter(tecNO_PERMISSION));
|
||||
@@ -2787,7 +2831,15 @@ protected:
|
||||
|
||||
auto const borrowerBalanceBeforePayment = env.balance(borrower, broker.asset);
|
||||
|
||||
if (canImpairLoan(env, broker, state))
|
||||
// Under fixCleanup3_4_0 impair requires the payment to
|
||||
// already be late. This periodic-payment loop stays
|
||||
// within each payment interval, so the loan is never
|
||||
// late here; skip the impair rather than perturb the
|
||||
// payment schedule.
|
||||
bool const impairAllowed = canImpairLoan(env, broker, state) &&
|
||||
(!env.current()->rules().enabled(fixCleanup3_4_0) ||
|
||||
isPaymentLate(*env.current(), env.le(loanKeylet)));
|
||||
if (impairAllowed)
|
||||
{
|
||||
// Making a payment will unimpair the loan
|
||||
env(manage(lender, loanKeylet.key, tfLoanImpair));
|
||||
|
||||
Reference in New Issue
Block a user