Skip to content

feat: Make VaultID conditional on LoanBrokerSet#6528

Open
Tapanito wants to merge 10 commits into
tapanito/lending-fix-amendmentfrom
tapanito/loan-broker-set
Open

feat: Make VaultID conditional on LoanBrokerSet#6528
Tapanito wants to merge 10 commits into
tapanito/lending-fix-amendmentfrom
tapanito/loan-broker-set

Conversation

@Tapanito

@Tapanito Tapanito commented Mar 11, 2026

Copy link
Copy Markdown
Collaborator

When updating an existing LoanBroker (LoanBrokerID present), VaultID must not be provided the vault association is read from the broker object on ledger. VaultID remains required when creating a new LoanBroker. This change is gated behind fixLendingProtocolV1_1; pre-amendment behavior is preserved for historical transaction replay.

  • Change sfVaultID from soeREQUIRED to soeOPTIONAL
  • Gate VaultID field presence rules in preflight by amendment
  • Refactor preclaim into readVault/preclaimUpdate/preclaimCreate
  • Add pre- and post-amendment unit test coverage

Specification: XRPLF/XRPL-Standards#497

High Level Overview of Change

Context of Change

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Performance (increase or change in throughput and/or latency)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)
  • Documentation update
  • Chore (no impact to binary, e.g. .gitignore, formatting, dropping support for older tooling)
  • Release

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 requested review from a1q123456 and ximinez March 11, 2026 14:25
@codecov

codecov Bot commented Mar 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.82716% with 5 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (tapanito/lending-fix-amendment@189f2d6). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...c/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp 93.4% 5 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                       Coverage Diff                        @@
##             tapanito/lending-fix-amendment   #6528   +/-   ##
================================================================
  Coverage                                  ?   82.4%           
================================================================
  Files                                     ?    1011           
  Lines                                     ?   76974           
  Branches                                  ?    8966           
================================================================
  Hits                                      ?   63403           
  Misses                                    ?   13562           
  Partials                                  ?       9           
Files with missing lines Coverage Δ
include/xrpl/protocol/detail/transactions.macro 100.0% <ø> (ø)
...xrpl/protocol_autogen/transactions/LoanBrokerSet.h 100.0% <100.0%> (ø)
...c/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp 96.5% <93.4%> (ø)

Impacted file tree graph

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

@github-actions

Copy link
Copy Markdown

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

Comment thread src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp Outdated
Comment thread src/test/jtx/impl/TestHelpers.cpp Outdated
@github-actions

Copy link
Copy Markdown

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

@github-actions

Copy link
Copy Markdown

⚠️ This PR contains unsigned commits. To get your PR merged, please sign them. ⚠️

If only the most recent commit is unsigned, you can run:

  1. Amend the commit: git commit --amend --no-edit -n -S
  2. Overwrite the commit: git push --force-with-lease

If multiple commits are unsigned, you can run:

  1. Go into interactive rebase mode: git rebase --interactive HEAD~<NUM_OF_COMMITS>,
    where NUM_OF_COMMITS is the number of most recent commits that will be available
    to edit.
  2. Change "pick" to "edit" for the commits you need to sign, and then save and exit.
  3. For each commit, run: git commit --amend --no-edit -n -S
  4. Continue the rebase: git rebase --continue
  5. Overwrite the commit(s): git push --force-with-lease

If you're new to commit signing, there are different ways to set it up:

Sign commits with gpg

Follow the steps below to set up commit signing with gpg:

  1. Generate a GPG key
  2. Add the GPG key to your GitHub account
  3. Configure git to use your GPG key for commit signing
Sign commits with ssh-agent

Follow the steps below to set up commit signing with ssh-agent:

  1. Generate an SSH key and add it to ssh-agent
  2. Add the SSH key to your GitHub account
  3. Configure git to use your SSH key for commit signing
Sign commits with 1Password

You can also sign commits using 1Password, which lets you sign commits with biometrics without the signing key leaving the local 1Password process.
See use 1Password to sign your commits.

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

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.

Three correctness issues and one information-disclosure risk flagged inline — see comments on LoanBrokerSet.cpp (lines 125, 155, 203) and the test file (line 687).

Review by Claude Opus 4.6 · Prompt: V12

Comment thread src/test/app/LoanBroker_test.cpp Outdated
Comment thread src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp
Comment thread src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp
Comment thread src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp
When updating an existing LoanBroker (LoanBrokerID present), VaultID
must not be provided — the vault association is read from the broker
object on ledger. VaultID remains required when creating a new
LoanBroker. This change is gated behind fixLendingProtocolV1_1;
pre-amendment behavior is preserved for historical transaction replay.

- Change sfVaultID from soeREQUIRED to soeOPTIONAL
- Gate VaultID field presence rules in preflight by amendment
- Refactor preclaim into readVault/preclaimUpdate/preclaimCreate
- Add pre- and post-amendment unit test coverage
# Conflicts:
#	src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp
#	src/test/app/LoanBroker_test.cpp
@Tapanito Tapanito force-pushed the tapanito/loan-broker-set branch from a59433f to 20d6e93 Compare March 31, 2026 12:10

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

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.

Went through the changes

One potential crash risk in the pre-amendment path of preclaimUpdate — unguarded access to optional sfVaultID can throw. See inline.


Review by ReviewBot 🤖

Review by Claude Opus 4.6 · Prompt: V12

Comment thread src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp
Merge two `loanBroker::set` overloads into one using
`std::optional<uint256>` for the VaultID parameter. Fix variable
rename typo (`vault` → `maybeVault`) in LoanBrokerSet::preclaim.
Update Lifecycle tests to match featureLendingProtocolV1_1 semantics:
update transactions must not include VaultID.
Mark readVault, preclaimUpdate, and preclaimCreate with [[nodiscard]]
to match project convention for error-bearing return types. Add test
for the pre-amendment preclaimUpdate path where the vault doesn't
exist (readVault returning tecNO_ENTRY).

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

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.

Two correctness/safety concerns flagged inline: a missing zero-value guard for sfLoanBrokerID in preflight, and an unvalidated assumption that vault and broker owner fields always agree in preclaimUpdate.

Review by Claude Opus 4.6 · Prompt: V12

@@ -43,9 +46,27 @@ LoanBrokerSet::preflight(PreflightContext const& ctx)
return temINVALID;

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.

No explicit zero-value guard for sfLoanBrokerID in the update path — confirm this is covered elsewhere.

The isLoanBrokerUpdate flag is set when sfLoanBrokerID is present, but a zero uint256 value would pass this check and route into preclaimUpdate, calling ctx.view.read(keylet::loanbroker(uint256{})) on an unintended ledger key. Line 59 guards sfVaultID against beast::zero explicitly — sfLoanBrokerID deserves the same treatment. If STTx deserialization already rejects a zero field value, add a comment documenting that invariant; otherwise add an explicit guard:

Consider adding above this line:

        if (tx[sfLoanBrokerID] == beast::zero)
            return temINVALID;


XRPL_ASSERT(sleVault, "xrpl::LoanBrokerSet::preclaimUpdate : sleVault is initialized");

if (account != sleBroker->at(sfOwner))

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.

Vault owner and broker owner are checked independently — add a defensive cross-validation.

In the fixEnabled path, readVault validates that account == vault.sfOwner, and then line 176 separately checks account != sleBroker->at(sfOwner). These two owner fields could theoretically diverge due to ledger state corruption (e.g. if vault ownership is ever transferable). Consider adding an assertion or an explicit cross-check to catch any such divergence early:

// Defensive invariant: vault owner and broker owner must agree
if (sleVault->at(sfOwner) != sleBroker->at(sfOwner))
{
    JLOG(ctx.j.warn()) << "Vault and LoanBroker owner mismatch (ledger corruption?)";
    return Unexpected(tefINTERNAL);
}

Insert this block before the existing account != sleBroker->at(sfOwner) check at line 176.

*/
[[nodiscard]] static Expected<std::shared_ptr<SLE const>, TER>
preclaimUpdate(PreclaimContext const& ctx, AccountID const& account, uint256 const& brokerID)
{

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.

Inconsistent ownership validation order in post-amendment update path

In preclaimUpdate with fixEnabled = true, the code first validates vault ownership via readVault(ctx, account, sleBroker->at(sfVaultID)) and then separately checks account != sleBroker->at(sfOwner). If vault ownership can be transferred (a vault-transfer feature exists or is added later), a new vault owner could be blocked from modifying a broker they indirectly own via the vault — or conversely, an attacker who gains vault ownership could pass the first check but fail the second. More critically, the two checks test different objects: the first checks the vault's owner field, the second checks the broker's owner field. These could theoretically diverge.

Suggested fix: Add an explicit assertion or comment documenting that the vault owner and broker owner are expected to be identical (invariant established at creation time). Consider also adding a check that sleVault->at(sfOwner) == sleBroker->at(sfOwner) to defensively detect any ledger state corruption where these diverge, returning tefINTERNAL if they differ.

@a1q123456 a1q123456 left a comment

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.

lgtm

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

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

@Tapanito Tapanito added this to the Lending Protocol 1.1 milestone Jun 8, 2026
Tapanito added 2 commits June 8, 2026 13:14
Resolved conflicts in LoanBrokerSet.cpp, transactions.macro, LoanBrokerSet.h,
TestHelpers (h/cpp), and LoanBroker_test.cpp.

Key decisions:
- sfVaultID remains SoeOptional (our change) with new capitalization style
- Amendment-gated preflight logic preserved alongside kZero renames
- testZeroVaultID lambda removed (field is optional on update under V1_1)
- Both testLoanBrokerSetVaultIDAmendment and testCoverPrecisionGuard included
- All k-prefix helper renames from lending-fix-amendment applied
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

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

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

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.

No issues.

Review by Claude Sonnet 4.6 · Prompt: V15

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

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.

No issues.

Review by Claude Sonnet 4.6 · Prompt: V15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants