Skip to content

fix: Add assorted Lending Protocol fixes#6678

Merged
bthomee merged 13 commits intodevelopfrom
tapanito/lending-assorted-fixes
Apr 3, 2026
Merged

fix: Add assorted Lending Protocol fixes#6678
bthomee merged 13 commits intodevelopfrom
tapanito/lending-assorted-fixes

Conversation

@Tapanito
Copy link
Copy Markdown
Collaborator

@Tapanito Tapanito commented Mar 27, 2026

##Summary

  • LoanManage: call associateAsset on all flag paths — The previous code returned early from defaultLoan/impairLoan/unimpairLoan, skipping the associateAsset calls. Restructured doApply to capture the result and always call associateAsset on the Loan, LoanBroker, and Vault SLEs afterward (gated behind fixSecurity3_1_3).

  • LoanPay: return tecNO_PERMISSION instead of temINVALID_FLAG — The overpayment-on-non-overpayable-loan check in preclaim returned temINVALID_FLAG, but the flag itself is valid — the loan just doesn't permit it. Changed to tecNO_PERMISSION under fixSecurity3_1_3 to preserve backward compatibility.

  • LoanBroker invariant: add upper-bound check on cover available — The existing invariant only verified sfCoverAvailable >= accountHolds(pseudo-account). Added a complementary sfCoverAvailable <= accountHolds check (gated behind fixSecurity3_1_3) to enforce exact equality. The check is skipped for LoanBrokerDelete transactions because the broker SLE is erased without zeroing sfCoverAvailable. Also cached the accountHolds result to avoid a duplicate call.

High Level Overview of Change

Context of Change

API Impact

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

@Tapanito Tapanito added Amendment AI Triage Bugs and fixes that have been triaged via AI initiatives labels Mar 27, 2026
Copy link
Copy Markdown
Contributor

@xrplf-ai-reviewer xrplf-ai-reviewer bot left a comment

Choose a reason for hiding this comment

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

Two correctness issues flagged inline: associateAsset called on error paths in LoanManage.cpp, and a silent test-coverage gap when fixSecurity3_1_3 is not enabled in the invariant test.

Review by Claude Opus 4.6 · Prompt: V12

doInvariantCheck(
{{"Loan Broker cover available is greater than pseudo-account asset balance"}},
[&](Account const&, Account const&, ApplyContext& ac) {
auto sle = ac.view().peek(loanBrokerKeylet);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Test silently passes if fixSecurity3_1_3 isn't enabled — invariant block is never entered. Explicitly enable the amendment before this doInvariantCheck call.

Comment thread src/libxrpl/tx/transactors/lending/LoanManage.cpp Outdated
The existing invariant only checks that sfCoverAvailable is not less
than the pseudo-account balance. Add a complementary check, gated
behind fixSecurity3_1_3, that sfCoverAvailable is also not greater
than the balance. This ensures exact equality between the two values
after every transaction.

Add invariant tests for both the lower-bound and upper-bound checks
across all three asset types (XRP, IOU, MPT).
@Tapanito Tapanito force-pushed the tapanito/lending-assorted-fixes branch from 5153b12 to 4ea3e45 Compare March 27, 2026 13:29
Copy link
Copy Markdown
Contributor

@xrplf-ai-reviewer xrplf-ai-reviewer bot left a comment

Choose a reason for hiding this comment

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

Went over the changes

Four issues flagged inline: unconditional associateAsset on failed TER (LoanManage.cpp), a financial invariant only enforced post-amendment (LoanInvariant.cpp), a fee-semantic change from tem→tec that needs documentation (LoanPay.cpp), and a test state leak from a non-RAII feature-flag pattern (Loan_test.cpp). Also raised a question about whether the noop-path behavior change on the non-amended code path is intentional.


Review by ReviewBot 🤖

Review by Claude Opus 4.6 · Prompt: V12

Comment thread src/test/app/Loan_test.cpp
Comment thread src/libxrpl/tx/transactors/lending/LoanManage.cpp Outdated
Comment thread src/libxrpl/tx/transactors/lending/LoanManage.cpp Outdated
Comment thread src/libxrpl/tx/transactors/lending/LoanPay.cpp
Comment thread src/libxrpl/tx/invariants/LoanInvariant.cpp Outdated
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 27, 2026

Codecov Report

❌ Patch coverage is 95.83333% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 81.4%. Comparing base (02fa55d) to head (44a8123).
⚠️ Report is 22 commits behind head on develop.

Files with missing lines Patch % Lines
src/libxrpl/tx/transactors/lending/LoanManage.cpp 92.9% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #6678     +/-   ##
=========================================
- Coverage     81.4%   81.4%   -0.0%     
=========================================
  Files          999     999             
  Lines        74439   74450     +11     
  Branches      7564    7558      -6     
=========================================
+ Hits         60621   60626      +5     
- Misses       13818   13824      +6     
Files with missing lines Coverage Δ
src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp 95.4% <100.0%> (+4.2%) ⬆️
src/libxrpl/tx/transactors/lending/LoanPay.cpp 95.3% <100.0%> (ø)
src/libxrpl/tx/transactors/lending/LoanManage.cpp 89.7% <92.9%> (-0.4%) ⬇️

... and 2 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses several Lending Protocol correctness issues by aligning transaction outcomes and invariants with intended semantics, while adding regression tests and amendment-gated behavior for backward compatibility.

Changes:

  • LoanManage: restructure doApply to always run associateAsset after flag-specific actions when fixSecurity3_1_3 is enabled.
  • LoanPay: return tecNO_PERMISSION (amendment-gated) when tfLoanOverpayment is requested on a loan that doesn’t allow overpayment.
  • LoanBroker invariant: add an amendment-gated upper-bound check to enforce sfCoverAvailable matches the pseudo-account’s actual holdings.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/libxrpl/tx/transactors/lending/LoanManage.cpp Refactors control flow so post-action asset association runs consistently under fixSecurity3_1_3.
src/libxrpl/tx/transactors/lending/LoanPay.cpp Adjusts preclaim TER for non-permitted overpayment requests with amendment gating.
src/libxrpl/tx/invariants/LoanInvariant.cpp Adds amendment-gated upper-bound invariant check for sfCoverAvailable vs pseudo-account holdings.
src/test/app/Loan_test.cpp Updates/extends tests to validate new TER behavior and backward compatibility when disabling fixSecurity3_1_3.
src/test/app/Invariants_test.cpp Adds invariant regression tests for cover-available imbalance cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/libxrpl/tx/transactors/lending/LoanManage.cpp Outdated
Comment thread src/libxrpl/tx/invariants/LoanInvariant.cpp Outdated
Comment thread src/test/app/Invariants_test.cpp
@github-actions
Copy link
Copy Markdown

This PR has conflicts, please resolve them in order for the PR to be reviewed.

Copy link
Copy Markdown
Contributor

@xrplf-ai-reviewer xrplf-ai-reviewer bot left a comment

Choose a reason for hiding this comment

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

Two high-severity issues flagged inline: a pre-amendment behavioral regression in LoanManage.cpp where the noop path silently drops associateAsset calls when fixSecurity3_1_3 is off, and a related concern about state mutation on failed transactions in the legacy branch. A medium-severity pre-amendment DoS vector via fee-free tem* submissions in LoanPay.cpp is noted (fix is correct, defence-in-depth suggested). Lower-severity items: the ttLOAN_BROKER_DELETE carve-out in the invariant needs explicit justification, and the invariant test suite is missing a negative case for the amendment-disabled path.


Review by ReviewBot 🤖

Review by Claude Opus 4.6 · Prompt: V12

Comment thread src/libxrpl/tx/transactors/lending/LoanManage.cpp
Comment thread src/libxrpl/tx/transactors/lending/LoanManage.cpp
Comment thread src/libxrpl/tx/transactors/lending/LoanPay.cpp
Comment thread src/libxrpl/tx/invariants/LoanInvariant.cpp Outdated
Comment thread src/test/app/Invariants_test.cpp
@github-actions
Copy link
Copy Markdown

All conflicts have been resolved. Assigned reviewers can now start or resume their review.

Copy link
Copy Markdown
Contributor

@xrplf-ai-reviewer xrplf-ai-reviewer bot left a comment

Choose a reason for hiding this comment

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

Two issues flagged inline: a pre-amendment behavioral regression in LoanManage.cpp where associateAsset calls are silently dropped (potential security impact), and a test state leak in Loan_test.cpp where a missing RAII guard can corrupt subsequent test cases.

Review by Claude Opus 4.6 · Prompt: V12

Comment thread src/libxrpl/tx/transactors/lending/LoanManage.cpp
Comment thread src/test/app/Loan_test.cpp
Comment thread src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp
Comment thread src/libxrpl/tx/invariants/LoanBrokerInvariant.cpp Outdated
Copy link
Copy Markdown
Collaborator

@shawnxie999 shawnxie999 left a comment

Choose a reason for hiding this comment

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

LGTM - left minor comment

Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
Comment thread src/test/app/Invariants_test.cpp Outdated
if (!BEAST_EXPECT(sle))
return false;
// Pseudo-account holds 10 units, set cover to 5
sle->at(sfCoverAvailable) = Number(5);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe move the constants to beginning of the test and describe the pre-conditions, tests and testing strategy?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Invariant test constants are inline, I think refactoring this for all tests is out of scope for this PR, and refactoring only some would make it confusing "why are some inline and other not"

}();

return tesSUCCESS;
if (view.rules().enabled(fixSecurity3_1_3) && isTesSuccess(result))
Copy link
Copy Markdown
Contributor

@pratikmankawde pratikmankawde Mar 31, 2026

Choose a reason for hiding this comment

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

The summary says:

LoanManage: call associateAsset on all flag paths — The previous code returned early from defaultLoan/impairLoan/unimpairLoan, skipping the associateAsset calls. Restructured doApply to capture the result and always call associateAsset on the Loan, LoanBroker, and Vault SLEs afterward (gated behind fixSecurity3_1_3).

The old code always called associateAsset on the path where no flags are set. The new code calls them only when amendment is set and no flags are set. Is that correct? I believe AI flagged the same thing. The summary is a bit confusing to me. It says call associateAsset on all flag paths, does that mean, call associateAsset on the path where all flags are set?

This changes behavior for amendment inactive flows. Maybe document that this is intentional?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

    auto const result = [&]() -> TER {
        // Valid flag combinations are checked in preflight. No flags is valid -
        // just a noop.
        if (tx.isFlag(tfLoanDefault))
            return defaultLoan(view, loanSle, brokerSle, vaultSle, vaultAsset, j_);
        if (tx.isFlag(tfLoanImpair))
            return impairLoan(view, loanSle, vaultSle, vaultAsset, j_);
        if (tx.isFlag(tfLoanUnimpair))
            return unimpairLoan(view, loanSle, vaultSle, vaultAsset, j_);
        // Noop, as described above.
        return tesSUCCESS;
    }();

    // Pre-amendment, associateAsset was only called on the noop (no flags)
    // path. The flag paths returned early from defaultLoan/impairLoan/
    // unimpairLoan before reaching the calls. Post-amendment, we call
    // associateAsset on all successful paths — including the flag paths that
    // previously skipped it.
    if (view.rules().enabled(fixSecurity3_1_3) && isTesSuccess(result))
    {
        associateAsset(*loanSle, vaultAsset);
        associateAsset(*brokerSle, vaultAsset);
        associateAsset(*vaultSle, vaultAsset);
    }
    ```
    
    
    Now `associateAsset` is called an all successful paths.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is a "successful" path?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

A tesSUCCESS return code. I don't think that we need to call associateAsset on failure conditions as a failure does not modify the ledger state.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Right. I believe I had some confusion because of the no-op comment. Since it wasn't clear to me that all of these defaultLoan/impairLoan/unimpairLoan functions can return tesSUCCESS. So, that means we are now calling associateAsset even when these functions return success (earlier we weren't, as flagged by AI as well). But the no-op comment followed by return tesSUCCESS suggested that the lambda returns tesSUCCESS when there's no-op (no flag set) only.

Copy link
Copy Markdown
Contributor

@pratikmankawde pratikmankawde left a comment

Choose a reason for hiding this comment

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

Left a comment otherwise LGTM

@bthomee
Copy link
Copy Markdown
Collaborator

bthomee commented Apr 2, 2026

Left a comment otherwise LGTM

@pratikmankawde I see your comment received a response - are any follow-ups needed or is this PR good to go?

Copy link
Copy Markdown
Contributor

@pratikmankawde pratikmankawde left a comment

Choose a reason for hiding this comment

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

LGTM

@bthomee bthomee changed the title fix: Assorted Lending Protocol bug fixes fix: Make assorted Lending Protocol bug fixes Apr 2, 2026
@pratikmankawde
Copy link
Copy Markdown
Contributor

Left a comment otherwise LGTM

@pratikmankawde I see your comment received a response - are any follow-ups needed or is this PR good to go?

Approved.

@bthomee bthomee added Critical and removed Critical labels Apr 2, 2026
@bthomee bthomee requested a review from a1q123456 April 2, 2026 13:46
Copy link
Copy Markdown
Contributor

@a1q123456 a1q123456 left a comment

Choose a reason for hiding this comment

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

lgtm

@Tapanito Tapanito changed the title fix: Make assorted Lending Protocol bug fixes fix: Assorted Lending Protocol bug fixes Apr 3, 2026
@Tapanito Tapanito added the Ready to merge *PR author* thinks it's ready to merge. Has passed code review. Perf sign-off may still be required. label Apr 3, 2026
@bthomee bthomee changed the title fix: Assorted Lending Protocol bug fixes fix: Add assorted Lending Protocol fixes Apr 3, 2026
@bthomee bthomee added this pull request to the merge queue Apr 3, 2026
Merged via the queue into develop with commit c0ee813 Apr 3, 2026
39 checks passed
@bthomee bthomee deleted the tapanito/lending-assorted-fixes branch April 3, 2026 18:03
Kassaking7 pushed a commit to Kassaking7/rippled that referenced this pull request Apr 6, 2026
Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
Kassaking7 pushed a commit to Kassaking7/rippled that referenced this pull request Apr 6, 2026
Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
mvadari pushed a commit that referenced this pull request Apr 7, 2026
Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
bthomee pushed a commit that referenced this pull request Apr 8, 2026
Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
ximinez pushed a commit that referenced this pull request Apr 14, 2026
Co-authored-by: Shawn Xie <35279399+shawnxie999@users.noreply.github.com>
@ximinez ximinez added this to the 3.1.3 milestone Apr 15, 2026
@ximinez ximinez modified the milestones: 3.1.3, 3.1.3 (develop) Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Triage Bugs and fixes that have been triaged via AI initiatives Amendment Ready to merge *PR author* thinks it's ready to merge. Has passed code review. Perf sign-off may still be required.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants