Skip to content

feat: XLS-68: Sponsor, #5887 continuation #7350

Open
oleks-rip wants to merge 302 commits into
developfrom
xrplf/sponsor
Open

feat: XLS-68: Sponsor, #5887 continuation #7350
oleks-rip wants to merge 302 commits into
developfrom
xrplf/sponsor

Conversation

@oleks-rip

Copy link
Copy Markdown
Collaborator

Implementation of XLS-0068-sponsored-fees-and-reserves

High Level Overview of Change

Sponsor functionality allow any account to became Sponsor for another account and pay fee and reserve cost for that account

Context of Change

Added SponsorshipSet transactions
Added SponsorshipTransfer transactions
Added Sponsorship object
Added sponsor support for other transactions

tequdev and others added 30 commits September 26, 2025 18:21
- Restructures `STTx` signature checking code to be able to handle
  a `sigObject`, which may be the full transaction, or may be an object
  field containing a separate signature. Either way, the `sigObject` can
  be a single- or multi-sign signature.
- This is distinct from 550f90a (#5594), which changed the check in
  Transactor, which validates whether a given account is allowed to sign
  for the given transaction. This cryptographically checks the signature
  validity.
@github-actions

github-actions Bot commented Jun 3, 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

@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.86356% with 96 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.2%. Comparing base (480676d) to head (d1a77b4).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
...rpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp 90.1% 31 Missing ⚠️
src/libxrpl/ledger/helpers/AccountRootHelpers.cpp 83.3% 19 Missing ⚠️
src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp 0.0% 10 Missing ⚠️
src/libxrpl/tx/invariants/SponsorshipInvariant.cpp 92.9% 6 Missing ⚠️
src/libxrpl/tx/transactors/vault/VaultCreate.cpp 68.4% 6 Missing ⚠️
.../libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp 98.0% 4 Missing ⚠️
src/libxrpl/ledger/helpers/NFTokenHelpers.cpp 92.5% 3 Missing ⚠️
...xrpl/tx/invariants/PermissionedDomainInvariant.cpp 62.5% 3 Missing ⚠️
src/libxrpl/tx/transactors/dex/AMMCreate.cpp 93.1% 2 Missing ⚠️
src/libxrpl/tx/transactors/payment/Payment.cpp 92.0% 2 Missing ⚠️
... and 9 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #7350     +/-   ##
=========================================
+ Coverage     82.0%   82.2%   +0.2%     
=========================================
  Files         1007    1016      +9     
  Lines        76876   78401   +1525     
  Branches      8981    9012     +31     
=========================================
+ Hits         63001   64440   +1439     
- Misses       13866   13952     +86     
  Partials         9       9             
Files with missing lines Coverage Δ
include/xrpl/ledger/helpers/AccountRootHelpers.h 100.0% <100.0%> (ø)
include/xrpl/ledger/helpers/EscrowHelpers.h 98.9% <100.0%> (+0.2%) ⬆️
include/xrpl/ledger/helpers/NFTokenHelpers.h 100.0% <ø> (ø)
include/xrpl/ledger/helpers/RippleStateHelpers.h 100.0% <ø> (ø)
include/xrpl/protocol/Fees.h 100.0% <ø> (ø)
include/xrpl/protocol/Indexes.h 100.0% <ø> (ø)
include/xrpl/protocol/LedgerFormats.h 100.0% <ø> (ø)
include/xrpl/protocol/TER.h 100.0% <ø> (ø)
include/xrpl/protocol/TxFlags.h 100.0% <ø> (ø)
include/xrpl/protocol/detail/ledger_entries.macro 100.0% <100.0%> (ø)
... and 93 more

... and 4 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.

@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

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

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

@github-actions

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.

ℹ️ Note: This is a large diff (632,854 chars). Complex issues deep in the diff may receive less attention.

Multiple correctness bugs flagged inline — critical crash in isReserveSponsored, inverted flag check rejecting all valid SponsorshipTransfer txs, missing view().update() dropping XRP refunds, wrong sponsor passed in several reserve checks, sponsor-change path silently skipped when counts are equal, dead test suite, and parseSponsorship never wired into the dispatch table.

Review by Claude Sonnet 4.6 · Prompt: V15

Comment thread include/xrpl/ledger/helpers/SponsorHelpers.h
adjustOwnerCountHlp(view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j);

auto sponsorObjSle = view.peek(keylet::sponsor(sponsorID, accountID));
if (sponsorObjSle && adjustment > 0)

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.

sfReserveCount is only decremented on creation (adjustment > 0) but never restored on deletion (adjustment < 0). Sponsor reserve slots are permanently consumed even after objects are deleted. The condition should be if (sponsorObjSle) to also call adjustOwnerCountHlp(..., -adjustment, ...) on delete:

Suggested change
if (sponsorObjSle && adjustment > 0)
if (sponsorObjSle)

Throw<std::runtime_error>(
"NFTokenPage directory for " + to_string(owner) +
" cannot be repaired. Unexpected link problem.");
" cannot be repaired. std::unexpected link problem.");

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.

Corrupted error message — std::unexpected (the C++ type) was accidentally substituted for Unexpected during refactoring:

Suggested change
" cannot be repaired. std::unexpected link problem.");
" cannot be repaired. Unexpected link problem.");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fixed by #7569


auto const hasSponsorSignature = tx.isFieldPresent(sfSponsorSignature);

if (hasSponsorSignature)

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.

Early tesSUCCESS return on co-signed path skips the sponsorship object check. A tx with a validly-signed sfSponsorSignature from a non-existent sponsor account bypasses checkSponsor entirely — the sponsor SLE nullness check at line 380 only covers Expected errors, not a missing account.


if (((sponsorFlags & spfSponsorFee) != 0u) &&
sponsorshipSle->isFlag(lsfSponsorshipRequireSignForFee))
return terNO_SPONSORSHIP;

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.

Wrong error code: terNO_SPONSORSHIP implies the sponsorship doesn't exist, but here it was found — authorization is missing. Use tecNO_SPONSOR_PERMISSION to correctly distinguish missing permission from missing object:

Suggested change
return terNO_SPONSORSHIP;
return tecNO_SPONSOR_PERMISSION;

return sponsorSle.error(); // LCOV_EXCL_LINE

auto const insertRet =
nft::insertToken(view(), ctx_.tx, buyer, *sponsorSle, std::move(tokenAndPage->token));

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.

Sponsor is passed to insertToken for the buyer unconditionally. When the seller submits (accountID_ != buyer), the buyer's NFT page gets the seller's sponsor attributed, incorrectly charging the buyer's reserve to the seller's sponsor:

Suggested change
nft::insertToken(view(), ctx_.tx, buyer, *sponsorSle, std::move(tokenAndPage->token));
nft::insertToken(view(), ctx_.tx, buyer, accountID_ == buyer ? *sponsorSle : nullptr, std::move(tokenAndPage->token));

view,
tx,
sleAccount,
std::max(priorBalance, balance),

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.

The isIssue guard was dropped: old code used isIssue ? std::max(priorBalance, balance) : priorBalance, but now std::max is always passed. For non-isIssue paths the reserve check is more permissive, potentially allowing withdrawals that should return tecINSUFFICIENT_RESERVE.

}

void
testSponsorship()

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.

testSponsorship() is defined but never called in run(). The entire sponsorship invariant test suite is dead code. Add testSponsorship(); to the run() body alongside the other test*() calls.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fixed by #7569


if (txJson.isMember(sfSponsorSignature.jsonName))
{
if (auto error = autofillSignature(txJson[sfSponsorSignature.jsonName]))

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.

Missing isObject() guard — if SponsorSignature is null or a scalar, autofillSignature calls operator[] on a non-object json::Value, throwing an unhandled exception:

    if (txJson.isMember(sfSponsorSignature.jsonName))
    {
        if (!txJson[sfSponsorSignature.jsonName].isObject())
            return RPC::invalidFieldError(sfSponsorSignature.jsonName.c_str());
        if (auto error = autofillSignature(txJson[sfSponsorSignature.jsonName]))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Fixed by #7557

Comment thread src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp

@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

std::int32_t reserveCountAdj = 0);

[[nodiscard]] inline XRPAmount
accountReserve(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This helper only seems to be used in tests, so I think we should remove it

@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

@github-actions

Copy link
Copy Markdown

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

/** Insert the token in the owner's token directory. */
TER
insertToken(ApplyView& view, AccountID owner, STObject&& nft)
insertToken(ApplyView& view, STTx const& tx, AccountID owner, SLE::ref sponsorSle, STObject&& nft)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Regarding so many inline changes in the base helper functions everywhere. Even small behavior changes in these base helpers could become consensus-sensitive. Just passing existing tests does not mean they are safe and accurate.
Making these extensive inline changes across so many helper functions feels highly error-prone.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants