From 060b6d32318c7dcb6065e1b7f271ec311f3d2232 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 5 Nov 2025 13:29:04 -0500 Subject: [PATCH] Revert "Remove the shortage code completely" This reverts commit 165478b929e736f037bfff6c69a6fa14724fd56a. --- src/test/app/Loan_test.cpp | 10 ++++ src/xrpld/app/misc/detail/LendingHelpers.cpp | 50 ++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 1778e60b37..f0fc66b704 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -2674,9 +2674,19 @@ protected: state.paymentRemaining, broker.params.managementFeeRate); +#if LOANFILLSHORTAGE + BEAST_EXPECT( + paymentComponents.trackedValueDelta == + roundedPeriodicPayment || + (paymentComponents.specialCase == + detail::PaymentSpecialCase::final && + paymentComponents.trackedValueDelta < + roundedPeriodicPayment)); +#else BEAST_EXPECT( paymentComponents.trackedValueDelta <= roundedPeriodicPayment); +#endif ripple::LoanState const nextTrueState = calculateRawLoanState( diff --git a/src/xrpld/app/misc/detail/LendingHelpers.cpp b/src/xrpld/app/misc/detail/LendingHelpers.cpp index 8c349aafa2..890b8ba347 100644 --- a/src/xrpld/app/misc/detail/LendingHelpers.cpp +++ b/src/xrpld/app/misc/detail/LendingHelpers.cpp @@ -1215,12 +1215,42 @@ computePaymentComponents( "ripple::detail::computePaymentComponents", "excess non-negative"); }; +#if LOANFILLSHORTAGE + auto giveTo = + [](Number& component, Number& shortage, Number const& maximum) { + if (shortage > beast::zero) + { + // Put as much of the shortage as we can into the provided part + // and the total + auto part = std::min(maximum - component, shortage); + component += part; + shortage -= part; + } + // If the shortage goes negative, we put too much, which should be + // impossible + XRPL_ASSERT_PARTS( + shortage >= beast::zero, + "ripple::detail::computePaymentComponents", + "excess non-negative"); + }; +#endif auto addressExcess = [&takeFrom](LoanDeltas& deltas, Number& excess) { // This order is based on where errors are the least problematic takeFrom(deltas.interestDueDelta, excess); takeFrom(deltas.managementFeeDueDelta, excess); takeFrom(deltas.principalDelta, excess); }; +#if LOANFILLSHORTAGE + auto addressShortage = [&giveTo]( + LoanDeltas& deltas, + Number& shortage, + LoanState const& current) { + giveTo(deltas.interestDueDelta, shortage, current.interestDue); + giveTo(deltas.principalDelta, shortage, current.principalOutstanding); + giveTo( + deltas.managementFeeDueDelta, shortage, current.managementFeeDue); + }; +#endif Number totalOverpayment = deltas.valueDelta() - currentLedgerState.valueOutstanding; if (totalOverpayment > beast::zero) @@ -1249,6 +1279,25 @@ computePaymentComponents( shortage = -excess; } +#if LOANFILLSHORTAGE + else if (shortage > beast::zero && totalOverpayment < beast::zero) + { + // If there's a shortage, and there's room in the loan itself, we can + // top up the parts to make the payment correct. + shortage = std::min(-totalOverpayment, shortage); + addressShortage(deltas, shortage, currentLedgerState); + } + + // The shortage should never be negative, which indicates that the parts are + // trying to take more than the whole payment. The shortage should not be + // positive, either, which indicates that we're not going to take the whole + // payment amount. Only the last payment should be allowed to have a + // shortage, and that's handled in a special case above. + XRPL_ASSERT_PARTS( + shortage == beast::zero, + "ripple::detail::computePaymentComponents", + "no shortage or excess"); +#else // The shortage should never be negative, which indicates that the // parts are trying to take more than the whole payment. The // shortage may be positive, which indicates that we're not going to @@ -1257,6 +1306,7 @@ computePaymentComponents( shortage >= beast::zero, "ripple::detail::computePaymentComponents", "no shortage or excess"); +#endif #if LOANCOMPLETE /* // This used to be part of the above assert. It will eventually be removed