Skip to content

feat: XLS-93d: Token-Enabled Payment Channels - #5448

Closed
dangell7 wants to merge 13 commits into
XRPLF:developfrom
Transia-RnD:token-paychan
Closed

feat: XLS-93d: Token-Enabled Payment Channels#5448
dangell7 wants to merge 13 commits into
XRPLF:developfrom
Transia-RnD:token-paychan

Conversation

@dangell7

@dangell7 dangell7 commented May 24, 2025

Copy link
Copy Markdown
Contributor

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)

@dangell7
dangell7 force-pushed the token-paychan branch 2 times, most recently from dabe68d to 5c1949a Compare May 24, 2025 16:58
@mvadari mvadari changed the title XLS-103d: Token-Enabled Payment Channels XLS-93d: Token-Enabled Payment Channels May 27, 2025
@dangell7
dangell7 force-pushed the token-paychan branch 3 times, most recently from 5afa17d to db65118 Compare August 16, 2025 12:20
@dangell7
dangell7 marked this pull request as ready for review August 16, 2025 12:21
@dangell7
dangell7 requested a review from a team August 16, 2025 12:21
@codecov

codecov Bot commented Aug 17, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.07843% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.8%. Comparing base (2b4d067) to head (df1458a).
⚠️ Report is 12 commits behind head on develop.

Files with missing lines Patch % Lines
include/xrpl/protocol/PayChan.h 87.9% 4 Missing ⚠️
...ansactors/payment_channel/PaymentChannelCreate.cpp 94.1% 4 Missing ⚠️
src/xrpld/rpc/handlers/account/GatewayBalances.cpp 66.7% 4 Missing ⚠️
...transactors/payment_channel/PaymentChannelFund.cpp 97.2% 2 Missing ⚠️
...c/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp 96.3% 1 Missing ⚠️
...ransactors/payment_channel/PaymentChannelClaim.cpp 98.5% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #5448     +/-   ##
=========================================
+ Coverage     82.6%   82.8%   +0.2%     
=========================================
  Files         1036    1036             
  Lines        79870   80244    +374     
  Branches      8949    8923     -26     
=========================================
+ Hits         65956   66415    +459     
+ Misses       13905   13820     -85     
  Partials         9       9             
Files with missing lines Coverage Δ
include/xrpl/ledger/helpers/EscrowHelpers.h 99.5% <100.0%> (+0.6%) ⬆️
include/xrpl/protocol/detail/ledger_entries.macro 100.0% <ø> (ø)
include/xrpl/protocol/detail/transactions.macro 100.0% <ø> (ø)
.../xrpl/protocol_autogen/ledger_entries/PayChannel.h 100.0% <100.0%> (ø)
...rotocol_autogen/transactions/PaymentChannelClaim.h 100.0% <ø> (ø)
...otocol_autogen/transactions/PaymentChannelCreate.h 100.0% <ø> (ø)
...protocol_autogen/transactions/PaymentChannelFund.h 100.0% <ø> (ø)
...transactors/payment_channel/PaymentChannelCreate.h 100.0% <ø> (ø)
...x/transactors/payment_channel/PaymentChannelFund.h 100.0% <ø> (ø)
src/libxrpl/tx/invariants/InvariantCheck.cpp 98.4% <100.0%> (+2.3%) ⬆️
... and 10 more

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

@vlntb vlntb added the Triaged Issue/PR has been triaged for viability, liveliness, etc. label Oct 17, 2025
@mvadari mvadari added the Blocked: Needs Draft XLS The corresponding draft XLS must have been merged before this PR is merged. label Jan 28, 2026
@github-actions

Copy link
Copy Markdown

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

@godexsoft

Copy link
Copy Markdown
Contributor

We recently merged a refactor to develop that enables clang-tidy's readability-identifier-naming. Your branch now has heavy conflicts that are largely mechanical. Below is a workflow that aligns your branch's naming with develop before merging, which should minimize the merge conflicts.

One-time setup

If you don't already have clang-tidy working in your env, on macOS:

brew install llvm@21
# Follow brew's hint to put $(brew --prefix llvm@21)/bin on PATH so run-clang-tidy is found.

Workflow on your branch (before merging develop)

1. Grab the new .clang-tidy from develop without pulling anything else. Sync your fork on GitHub first, then:

git remote -v   # should show 'upstream' among others; if not:
# git remote set-url upstream git@github.com:XRPLF/rippled.git
git fetch upstream
git checkout upstream/develop -- .clang-tidy

2. Reconfigure conan/cmake so compile_commands.json is fresh.

3. Apply renames for the files modified in your PR:

git diff --name-only $(git merge-base HEAD upstream/develop) HEAD \
  | grep -E '\.(cpp|h|hpp|ipp)$' \
  | xargs run-clang-tidy -p build -fix -allow-no-checks
# or -p .build, or whatever your build dir is called

4. Build + test, then commit as a single dedicated commit:

cmake --build build -j8
git commit -am "refactor: Align identifier naming with develop"

5. Now merge develop:

git merge upstream/develop

Extra

Run clang-tidy once more after the merge to catch any stragglers introduced from develop's side:

run-clang-tidy -p build -fix -allow-no-checks src tests
# or -p .build, or whatever your build dir is called

@dangell7

dangell7 commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Copilot AI review requested due to automatic review settings May 6, 2026 18:52
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

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

@dangell7 dangell7 changed the title XLS-93d: Token-Enabled Payment Channels feat: XLS-93d: Token-Enabled Payment Channels May 6, 2026

Copilot AI 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.

Pull request overview

This PR implements XLS-93d token-enabled Payment Channels, extending payment channels to support non-XRP assets (IOUs and MPTs) behind a new TokenPaychan amendment, and refactors/extends related ledger helpers and tests to cover the new behavior.

Changes:

  • Add IOU/MPT support to PaymentChannelCreate, PaymentChannelFund, and PaymentChannelClaim, including transfer-rate capture and escrow-style locking/unlocking.
  • Extend paychan claim signing to serialize non-XRP amounts, and add new ledger fields (sfTransferRate, sfIssuerNode) to paychan entries.
  • Add extensive unit test coverage and new jtx helpers for paychan operations and balance/escrow assertions.

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/xrpld/rpc/handlers/account/GatewayBalances.cpp Adds paychan amounts into gateway_balances “locked” aggregation.
src/xrpld/app/misc/EscrowUtils.h Introduces new app-level escrow/paychan helper templates (currently appears unused).
src/test/jtx/TestHelpers.h Declares new test helpers for escrowed balances (IOU/MPT).
src/test/jtx/paychan.h Adds jtx paychan transaction builders/utilities.
src/test/jtx/impl/TestHelpers.cpp Implements new gateway balance / escrowed amount helpers for tests.
src/test/jtx/impl/paychan.cpp Implements the new jtx paychan helpers.
src/test/jtx.h Exposes the new jtx paychan helpers.
src/test/app/PayChanToken_test.cpp Adds comprehensive IOU/MPT paychan test suite.
src/test/app/PayChan_test.cpp Migrates existing paychan tests to new jtx::paychan::* helpers and adds coverage for feature interactions.
src/test/app/MPToken_test.cpp Expands MPT JSON field-validation coverage to more txn types.
src/test/app/EscrowToken_test.cpp Minor formatting cleanup.
src/test/app/AMM_test.cpp Formatting adjustment for paychan create call.
src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp Extends fund to support token amounts via escrow-style locking.
src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp Extends create to support token amounts, transfer rate capture, and issuer owner-dir tracking.
src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp Extends claim to support token payouts via escrow-style unlock/apply logic and new auth serialization.
src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp Refactors to reuse shared escrow unlock preclaim helper.
src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp Refactors to reuse shared escrow create preflight/preclaim helpers.
src/libxrpl/tx/transactors/escrow/EscrowCancel.cpp Refactors to reuse shared escrow unlock preclaim helper.
src/libxrpl/tx/invariants/MPTInvariant.cpp Updates invariant handling to accommodate paychan claim flows.
src/libxrpl/tx/invariants/InvariantCheck.cpp Adjusts XRP-not-created invariant to account for non-XRP paychans.
src/libxrpl/ledger/helpers/PaymentChannelHelpers.cpp Extends close logic to return token funds and clean up issuer owner-dir link.
include/xrpl/protocol/PayChan.h Adds non-XRP paychan authorization serialization.
include/xrpl/protocol/jss.h Adds jss::escrowed JSON key.
include/xrpl/protocol/detail/transactions.macro Marks paychan amount/balance fields as MPT-supported.
include/xrpl/protocol/detail/ledger_entries.macro Adds optional fields to paychan ledger entry format.
include/xrpl/protocol/detail/features.macro Introduces the TokenPaychan amendment.
include/xrpl/ledger/helpers/PaymentChannelHelpers.h Updates closeChannel signature to include submitting account.
include/xrpl/ledger/helpers/EscrowHelpers.h Adds shared escrow/paychan create/lock/unlock helpers for IOU/MPT flows.
Comments suppressed due to low confidence (2)

src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp:60

  • sfBalance/sfAmount are no longer restricted to XRP, but this preflight still compares *bal > *amt directly. STAmount comparisons throw std::runtime_error when the two amounts are not comparable (different asset types/currencies), which would make preflight throw instead of returning a TER. Add explicit validation that bal and amt (when both present) are comparable / same asset before comparing, and reject invalid combinations with temBAD_AMOUNT.
    auto const bal = ctx.tx[~sfBalance];
    if (bal && *bal <= beast::kZERO)
        return temBAD_AMOUNT;

    auto const amt = ctx.tx[~sfAmount];
    if (amt && *amt <= beast::kZERO)
        return temBAD_AMOUNT;

    if (bal && amt && *bal > *amt)
        return temBAD_AMOUNT;

src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp:176

  • In doApply, reqBalance > chanFunds and reqBalance - chanBalance will throw if the claim supplies an amount/balance with an asset that doesn't match the channel's sfAmount/sfBalance (comparisons/arithmetic on non-comparable STAmount throw). Please validate in preclaim (after loading the channel) that any provided sfBalance/sfAmount are the same asset type as the channel (XRP vs IOU currency/issuer vs MPT issuance) before doing comparisons/arithmetic, and return temBAD_AMOUNT (or similar) on mismatch.
    if (ctx_.tx[~sfBalance])
    {
        auto const chanBalance = slep->getFieldAmount(sfBalance);
        auto const chanFunds = slep->getFieldAmount(sfAmount);
        auto const reqBalance = ctx_.tx[sfBalance];

        if (account_ == dst && !ctx_.tx[~sfSignature])
            return temBAD_SIGNATURE;

        if (ctx_.tx[~sfSignature])
        {
            PublicKey const pk((*slep)[sfPublicKey]);
            if (ctx_.tx[sfPublicKey] != pk)
                return temBAD_SIGNER;
        }

        if (reqBalance > chanFunds)
            return tecUNFUNDED_PAYMENT;


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

Comment thread src/xrpld/rpc/handlers/account/GatewayBalances.cpp Outdated
Comment thread src/xrpld/rpc/handlers/account/GatewayBalances.cpp
Comment thread include/xrpl/protocol/PayChan.h Outdated
Comment thread src/libxrpl/tx/invariants/InvariantCheck.cpp
Comment thread src/libxrpl/tx/invariants/MPTInvariant.cpp
Comment thread src/xrpld/app/misc/EscrowUtils.h Outdated
Comment thread src/xrpld/app/misc/EscrowUtils.h Outdated
Copilot AI review requested due to automatic review settings May 10, 2026 16:10

Copilot AI 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.

Pull request overview

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

Comment thread src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp Outdated
Comment thread src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp
Comment thread src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp
@github-actions

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.

@github-actions

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.

Comment thread src/libxrpl/tx/transactors/payment_channel/PaymentChannelClaim.cpp Outdated
Comment thread src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp Outdated
@github-actions

Copy link
Copy Markdown

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

Includes post-merge compile fixes (build-verified).
@github-actions

Copy link
Copy Markdown

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

beast::Journal j)
{
auto const& issue = amount.get<Issue>();
AccountID const& issuer = amount.getIssuer();

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.

nit: use auto to remain consistent with the rest of this function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. resolved: 3c85a01

STAmount const balance = (*sleRippleState)[sfBalance];

// If balance is positive, issuer must have higher address than account
if (balance > beast::kZero && issuer < account)

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.

I didn't see this failure condition within the spec

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a guard on the internal RippleState representation, the balance sign must match the low/high account ordering. Unreachable for a well formed line (hence the LCOV_EXCL), not protocol behavior, so its not in the spec.

return tecNO_PERMISSION; // LCOV_EXCL_LINE

// If balance is negative, issuer must have lower address than account
if (balance < beast::kZero && issuer > account)

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.

I didn't see this failure condition within the spec

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same as above, internal representation guard, not spec material.

return tecLOCKED;

// If the mpt cannot be transferred, return tecNO_AUTH
if (auto const ter = canTransfer(view, mptIssue, account, dest); !isTesSuccess(ter))

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.

I am not quite sure if this failure case is mentioned in the spec.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This one is in the spec, PaymentChannelCreate failure conditions: lacking lsfMPTCanTransfer fails with tecNO_AUTH unless the destination is the issuer.

return tesSUCCESS;

if (!ctx.view.exists(trustLineKey) && createAsset)
if (!lineExisted && createAsset)

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.

Why the need for this change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Leftover hoist from an earlier revision, functionally identical. Reverted to the develop form. resolved: 3c85a01

}
else
{
if (amount <= beast::kZero)

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.

Can this conditional be rolled up into the outer conditional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. resolved: 3c85a01

if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE

STAmount const amount{ctx_.tx[sfAmount]};

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.

Reference type could be used here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

auto const, tx[sfAmount] is by value. resolved: 3c85a01

return tefINTERNAL; // LCOV_EXCL_LINE

STAmount const amount{ctx_.tx[sfAmount]};
STAmount const chanAmt{(*slep)[sfAmount]};

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.

Reference type could be used here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, getFieldAmount reference. resolved: 3c85a01

{
if (ctx.view.rules().enabled(featureTokenPaychan))
{
Keylet const k(ltPAYCHAN, ctx.tx[sfChannel]);

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.

Nit: Can we use uniform initialization instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. resolved: 3c85a01

Comment thread src/xrpld/rpc/detail/RPCCall.cpp Outdated
for (json::Value::ArrayIndex i = 0; i < jv.size(); ++i)
if (!isValidJson2(jv[i]))
return false;
return true;

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.

These four lines could be collapsed into

return std::all_of(std::begin(jv), std::end(jv), isValidJson2);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That was a bad merge resolution on my side, restored develop's std::all_of form verbatim. resolved: 096fa96

@github-actions

Copy link
Copy Markdown

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

@dangell7

dangell7 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #7935 (moved to an XRPLF branch for native CI and review). PaymentChannelClawback is split out into #7936, stacked on #7935.

@dangell7 dangell7 closed this Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Amendment Blocked: Needs Final XLS The corresponding final XLS must have been merged before this PR is merged. PR: has conflicts Triaged Issue/PR has been triaged for viability, liveliness, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants