diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 357e233cea..ea05c6486b 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -6437,6 +6437,67 @@ protected: } } + void + testRIPD3901() + { + testcase("Crash with tfLoanOverpayment"); + using namespace jtx; + using namespace loan; + Account const lender{"lender"}; + Account const issuer{"issuer"}; + Account const borrower{"borrower"}; + Account const depositor{"depositor"}; + auto const txfee = fee(XRP(100)); + + Env env(*this); + Vault vault(env); + + env.fund(XRP(10'000), lender, issuer, borrower, depositor); + env.close(); + + auto [tx, vaultKeyLet] = + vault.create({.owner = lender, .asset = xrpIssue()}); + env(tx, txfee); + env.close(); + + env(vault.deposit( + {.depositor = depositor, + .id = vaultKeyLet.key, + .amount = XRP(1'000)}), + txfee); + env.close(); + + auto const brokerKeyLet = + keylet::loanbroker(lender.id(), env.seq(lender)); + + env(loanBroker::set(lender, vaultKeyLet.key), txfee); + env.close(); + + // BrokerInfo brokerInfo{xrpIssue(), keylet, vaultKeyLet, {}}; + + STAmount const debtMaximumRequest = XRPAmount(200'000); + + env(set(borrower, brokerKeyLet.key, debtMaximumRequest), + sig(sfCounterpartySignature, lender), + interestRate(TenthBips32(50'000)), + paymentTotal(2), + paymentInterval(150), + txflags(tfLoanOverpayment), + txfee); + env.close(); + + std::uint32_t const loanSequence = 1; + auto const loanKeylet = keylet::loan(brokerKeyLet.key, loanSequence); + + if (auto loan = env.le(loanKeylet); env.test.BEAST_EXPECT(loan)) + { + env(loan::pay(borrower, loanKeylet.key, XRPAmount(150'001)), + txflags(tfLoanOverpayment), + txfee); + env.close(); + } + } + public: void run() override @@ -6479,6 +6540,7 @@ public: testRIPD3831(); testRIPD3459(); + testRIPD3901(); } }; diff --git a/src/xrpld/app/misc/detail/LendingHelpers.cpp b/src/xrpld/app/misc/detail/LendingHelpers.cpp index ad0e98b07e..8c349aafa2 100644 --- a/src/xrpld/app/misc/detail/LendingHelpers.cpp +++ b/src/xrpld/app/misc/detail/LendingHelpers.cpp @@ -663,9 +663,9 @@ tryOverpayment( auto const valueChange = newRounded.interestOutstanding() - rounded.interestOutstanding(); XRPL_ASSERT_PARTS( - valueChange < beast::zero, + valueChange <= beast::zero, "ripple::detail::tryOverpayment", - "principal overpayment reduced value of loan"); + "principal overpayment did not increase value of loan"); return LoanPaymentParts{ .principalPaid = diff --git a/src/xrpld/app/tx/detail/LoanPay.cpp b/src/xrpld/app/tx/detail/LoanPay.cpp index 3edc109c80..4cad02d762 100644 --- a/src/xrpld/app/tx/detail/LoanPay.cpp +++ b/src/xrpld/app/tx/detail/LoanPay.cpp @@ -332,7 +332,7 @@ LoanPay::doApply() // It should not be possible to pay 0 total paymentParts->principalPaid + paymentParts->interestPaid > 0, "ripple::LoanPay::doApply", - "valid principal paid"); + "valid total paid"); XRPL_ASSERT_PARTS( paymentParts->feePaid >= 0, "ripple::LoanPay::doApply",