From 47c16bb5d263cb96ef390c22ae27ed893b449b46 Mon Sep 17 00:00:00 2001 From: JCW Date: Mon, 27 Jul 2026 14:47:50 +0100 Subject: [PATCH] Address comments --- include/xrpl/ledger/helpers/LendingHelpers.h | 2 +- src/libxrpl/tx/invariants/InvariantCheck.cpp | 4 ++-- .../tx/transactors/lending/LoanAccept.cpp | 2 +- .../tx/transactors/lending/LoanDelete.cpp | 4 ++-- .../tx/transactors/lending/LoanManage.cpp | 2 +- src/libxrpl/tx/transactors/lending/LoanPay.cpp | 2 +- src/test/app/Loan_test.cpp | 16 ++++++++++++++++ 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/xrpl/ledger/helpers/LendingHelpers.h b/include/xrpl/ledger/helpers/LendingHelpers.h index 0c474f7125..087b9d759e 100644 --- a/include/xrpl/ledger/helpers/LendingHelpers.h +++ b/include/xrpl/ledger/helpers/LendingHelpers.h @@ -287,7 +287,7 @@ constructLoanState(SLE::const_ref loan); * been accepted by the borrower. */ inline bool -isPendingLoan(SLE::const_ref loan) +isLoanPending(SLE::const_ref loan) { return loan->isFlag(lsfLoanPending); } diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 2049cdf130..c1e9c1bf49 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -1161,6 +1161,7 @@ NoModifiedUnmodifiableFields::finalize( kFieldChanged(before, after, sfSequence) || kFieldChanged(before, after, sfLoanBrokerNode) || kFieldChanged(before, after, sfLoanBrokerID) || + kFieldChanged(before, after, sfBorrower) || kFieldChanged(before, after, sfLoanOriginationFee) || kFieldChanged(before, after, sfLoanServiceFee) || kFieldChanged(before, after, sfLatePaymentFee) || @@ -1176,8 +1177,7 @@ NoModifiedUnmodifiableFields::finalize( kFieldChanged(before, after, sfLoanScale); if (!view.rules().enabled(featureLendingProtocolV1_1)) { - bad = bad || kFieldChanged(before, after, sfBorrower) || - kFieldChanged(before, after, sfOwnerNode); + bad = bad || kFieldChanged(before, after, sfOwnerNode); } break; default: diff --git a/src/libxrpl/tx/transactors/lending/LoanAccept.cpp b/src/libxrpl/tx/transactors/lending/LoanAccept.cpp index 03efe5f38e..62c697d156 100644 --- a/src/libxrpl/tx/transactors/lending/LoanAccept.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanAccept.cpp @@ -50,7 +50,7 @@ LoanAccept::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; } - if (!isPendingLoan(loanSle)) + if (!isLoanPending(loanSle)) { JLOG(ctx.j.warn()) << "Loan is not pending acceptance."; return tecNO_PERMISSION; diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index d91808237c..b3ec025a91 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -170,7 +170,7 @@ LoanDelete::preclaim(PreclaimContext const& ctx) // A pending loan (created in the two-step flow) can be deleted at any time // by either the LoanBroker owner or the Borrower, regardless of remaining // payments. An active loan can only be deleted once it is fully paid. - if (!isPendingLoan(loanSle) && loanSle->at(sfPaymentRemaining) > 0) + if (!isLoanPending(loanSle) && loanSle->at(sfPaymentRemaining) > 0) { JLOG(ctx.j.warn()) << "Active loan can not be deleted."; return tecHAS_OBLIGATIONS; @@ -216,7 +216,7 @@ LoanDelete::doApply() // time and releases the owner reserve charged to the LoanBroker owner. It is // only linked into the broker pseudo-account's directory, and the borrower // was never charged a reserve. - return isPendingLoan(loanSle) ? deletePendingLoan(ctx_, loanSle, brokerSle, vaultSle, j_) + return isLoanPending(loanSle) ? deletePendingLoan(ctx_, loanSle, brokerSle, vaultSle, j_) : deleteActiveLoan(ctx_, loanSle, brokerSle, vaultSle, j_); } diff --git a/src/libxrpl/tx/transactors/lending/LoanManage.cpp b/src/libxrpl/tx/transactors/lending/LoanManage.cpp index a5b2a43c2b..bd27407581 100644 --- a/src/libxrpl/tx/transactors/lending/LoanManage.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanManage.cpp @@ -76,7 +76,7 @@ LoanManage::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; } - if (isPendingLoan(loanSle)) + if (isLoanPending(loanSle)) { JLOG(ctx.j.warn()) << "Loan is pending acceptance. A pending loan can not be managed."; return tecNO_PERMISSION; diff --git a/src/libxrpl/tx/transactors/lending/LoanPay.cpp b/src/libxrpl/tx/transactors/lending/LoanPay.cpp index ad439f9c0a..6e010a4c53 100644 --- a/src/libxrpl/tx/transactors/lending/LoanPay.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanPay.cpp @@ -191,7 +191,7 @@ LoanPay::preclaim(PreclaimContext const& ctx) return tecNO_ENTRY; } - if (isPendingLoan(loanSle)) + if (isLoanPending(loanSle)) { JLOG(ctx.j.warn()) << "Loan is pending acceptance. A pending loan can not be paid."; return tecNO_PERMISSION; diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 5e8446360d..67741de863 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -3840,6 +3840,10 @@ protected: // CounterpartySignature. env(set(lender, broker.brokerID, broker.asset(200).number()), Ter(temBAD_SIGNER)); + // LoanAccept is introduced by the two-step amendment, so with the + // amendment disabled the transaction type itself is rejected. + env(accept(borrower, keylet::loan(broker.brokerID, 1).key), Ter(temDISABLED)); + // Rest of the tests are not applicable return; } @@ -4000,6 +4004,18 @@ protected: // inner transaction, and with no Borrower field matches neither // the one-step nor the two-step (Borrower) flow. env(set(lender, broker.brokerID, broker.asset(200).number()), Ter(temINVALID)); + + // A LoanSet with Borrower but no StartDate matches neither the + // one-step nor the two-step (Borrower) flow. + env(set(lender, broker.brokerID, broker.asset(200).number()), + kBorrower(borrower), + Ter(temINVALID)); + + // A LoanSet with StartDate but no Borrower matches neither the + // one-step nor the two-step (Borrower) flow. + env(set(lender, broker.brokerID, broker.asset(200).number()), + kStartDate((env.now() + 1h).time_since_epoch().count()), + Ter(temINVALID)); } {