Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support PermissionDelegation Amendment XLS-74d XLS-75d #5354

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from

Conversation

yinyiqian1
Copy link
Collaborator

@yinyiqian1 yinyiqian1 commented Mar 19, 2025

There's a change in the spec, so please refer the new spec:
The PR for spec change: XRPLF/XRPL-Standards#257

Original spec:
XRPLF/XRPL-Standards#217
XRPLF/XRPL-Standards#218

This amendment is called PermissionDelegation

  1. DelegateSet transaction is added so that a delegating account can give permission to delegated account to send transaction on his behalf.
{
    TransactionType: "DelegateSet",
    Account: "rDelegating......",
    Authorize: "rDelegated......",
    Permissions: [{Permission: {PermissionValue: "Payment"}}],
}
  1. Ledger object Delegate is created, its keylet is hashed from delegating and delegated account.

  2. optional common field Delegate is added, to indicate that a transaction is a delegating transaction.
    The following transaction is a delegating transaction, Delegate account is the sender of the transaction and will sign this transaction, Account is the delegating account,

{
    Transaction: "Payment",
    Account: "rDelegating......",
    Amount: "1000000000",
    Destination: "r......",
    Delegate: "rDelegated......"
}
  1. Added a checkPermission function to check if the delegated account is authorized to send the transaction. And it can be extended in each sub-transactor to check the granular permissions.

  2. the delegated account pays the fee

  3. can query account_objects of the delegating account to see the permissions he owns.

  4. A delegate transaction can be multi-signed by the delegated account's signer list

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)

@yinyiqian1 yinyiqian1 force-pushed the account_permission_new branch 2 times, most recently from 70a6362 to 5b262d1 Compare March 19, 2025 06:37
@yinyiqian1 yinyiqian1 changed the title Support AccountPermission Support AccountPermission XLS-74d XLS-75d Mar 19, 2025
@yinyiqian1 yinyiqian1 force-pushed the account_permission_new branch 5 times, most recently from c8bc5f6 to e8e74f1 Compare March 19, 2025 15:57
Copy link
Collaborator

@ximinez ximinez left a comment

Choose a reason for hiding this comment

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

Preliminary partial review. I know you're still working on this, but a few things jumped out at me, and I wanted to bring them to your attention before you go down a problematic path.

Comment on lines 400 to 402
static_cast<std::uint32_t>(
TxFormats::getInstance().findTypeByName(
strValue) +
1));
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not a fan of modifying the transaction type with a hard coded magic number.

What is the problem with '0'? I haven't seen anything yet that makes '0' special or illegal. If not, then just use the unmodified value.

If there is a problem with '0', then use a new (static) modifier function in Permissions. e.g. TypeToPermission(TxType type). All it needs to do is add 1, but this has the advantage of making it clear what's going on and why.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@mvadari can we avoid adding 1? Since 0 is not used anyway

Copy link

codecov bot commented Mar 19, 2025

Codecov Report

Attention: Patch coverage is 97.01897% with 11 lines in your changes missing coverage. Please review.

Project coverage is 78.2%. Comparing base (7fe81fe) to head (31f4051).
Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
src/xrpld/app/tx/detail/Transactor.cpp 90.7% 4 Missing ⚠️
src/xrpld/app/misc/detail/DelegateUtils.cpp 90.9% 2 Missing ⚠️
src/xrpld/app/tx/detail/SetTrust.cpp 95.0% 2 Missing ⚠️
src/libxrpl/protocol/Permissions.cpp 97.1% 1 Missing ⚠️
src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp 95.5% 1 Missing ⚠️
src/xrpld/app/tx/detail/Payment.cpp 95.5% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #5354     +/-   ##
=========================================
+ Coverage     78.1%   78.2%   +0.1%     
=========================================
  Files          790     795      +5     
  Lines        67911   68340    +429     
  Branches      8234    8254     +20     
=========================================
+ Hits         53025   53429    +404     
- Misses       14886   14911     +25     
Files with missing lines Coverage Δ
include/xrpl/protocol/Indexes.h 100.0% <ø> (ø)
include/xrpl/protocol/detail/ledger_entries.macro 100.0% <100.0%> (ø)
include/xrpl/protocol/detail/permissions.macro 100.0% <100.0%> (ø)
include/xrpl/protocol/detail/transactions.macro 100.0% <100.0%> (ø)
src/libxrpl/protocol/Indexes.cpp 98.1% <100.0%> (+<0.1%) ⬆️
src/libxrpl/protocol/InnerObjectFormats.cpp 100.0% <100.0%> (ø)
src/libxrpl/protocol/STInteger.cpp 86.4% <100.0%> (+1.9%) ⬆️
src/libxrpl/protocol/STParsedJSON.cpp 70.0% <100.0%> (+0.8%) ⬆️
src/libxrpl/protocol/TxFormats.cpp 100.0% <ø> (ø)
src/xrpld/app/tx/detail/DelegateSet.cpp 100.0% <100.0%> (ø)
... and 18 more

... and 8 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@yinyiqian1 yinyiqian1 force-pushed the account_permission_new branch from b1f0387 to 4495956 Compare March 20, 2025 15:41
@yinyiqian1 yinyiqian1 force-pushed the account_permission_new branch from 4495956 to 49ca2b8 Compare March 20, 2025 16:38
@yinyiqian1 yinyiqian1 changed the title Support AccountPermission XLS-74d XLS-75d Support DelegateSet Amendment XLS-74d XLS-75d Mar 20, 2025
@yinyiqian1 yinyiqian1 marked this pull request as ready for review March 20, 2025 16:50
@shawnxie999
Copy link
Collaborator

Also, I propose that we shouldn't need to squash commits. Commits makes it easier for the reviewers to keep track of the changes, and they will all be squashed in the end anyways. Also, the audit relies on a particular commit hash, so it's best to keep the commit history as authentic as possible.

@yinyiqian1 yinyiqian1 requested a review from gregtatcam March 20, 2025 18:20
Comment on lines +89 to +92
if (value == ttSIGNER_LIST_SET || value == ttREGULAR_KEY_SET ||
value == ttACCOUNT_SET || value == ttDELEGATE_SET ||
value == ttACCOUNT_DELETE)
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this list should be updated with batch txn as well when the times comes, since I don't know whether it makes sense to delegate someone else to do a batch. what do you think @mvadari @dangell7

Copy link
Collaborator

Choose a reason for hiding this comment

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

Agreed (though difficult to make it happen without waiting for one of the two PRs to merge first)

Copy link
Collaborator

Choose a reason for hiding this comment

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

I do agree that it should be prohibited but we should wait till batch is merged. Also if ed's comment was implemented it would need to be added in the transactor instead. I like that approach tbh.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@dangell7 ed agrees not to force overridding the checkPermission function. Since it's checked on the DelegateSet level and all transactions need to be authorized by the delegating account through DelegateSet transaction before a delegated account can send delegating transactions

@yinyiqian1 yinyiqian1 force-pushed the account_permission_new branch from 7e3e045 to 4e62a34 Compare March 21, 2025 14:27
@yinyiqian1 yinyiqian1 force-pushed the account_permission_new branch from daa6f19 to ce15383 Compare March 21, 2025 14:54
@shawnxie999
Copy link
Collaborator

shawnxie999 commented Mar 21, 2025

@yinyiqian1 Deserialization of STUint32 to JSON is missing in STInteger.cpp. STUInt32::getJson(JsonOptions) should be updated. you would basically need to reverse what you did in STParsedJSON, - decrement by one if it's a transaction permission. We should add some unit tests later to test the JSON response to ensure it's correct

@yinyiqian1
Copy link
Collaborator Author

@yinyiqian1 Deserialization of STUint32 to JSON is missing in STInteger.cpp. STUInt32::getJson(JsonOptions) should be updated. you would basically need to reverse what you did in STParsedJSON, - decrement by one if it's a transaction permission. We should add some unit tests later to test the JSON response to ensure it's correct

updated

@ximinez
Copy link
Collaborator

ximinez commented Mar 21, 2025

Also, I propose that we shouldn't need to squash commits. Commits makes it easier for the reviewers to keep track of the changes, and they will all be squashed in the end anyways. Also, the audit relies on a particular commit hash, so it's best to keep the commit history as authentic as possible.

This is documented in our contributing guidelines.

@yinyiqian1 yinyiqian1 changed the title Support DelegateSet Amendment XLS-74d XLS-75d Support PermissionDelegation Amendment XLS-74d XLS-75d Mar 21, 2025
@@ -41,6 +41,9 @@ class Delegate_test : public beast::unit_test::suite
// can not set Delegate when feature disabled
env(delegate_set::delegateSet(gw, alice, {"Payment"}),
ter(temDISABLED));

// can not send delegating transaction when feture disabled
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// can not send delegating transaction when feture disabled
// can not send delegating transaction when feature disabled

if (ctx_.tx.isFieldPresent(sfDelegate))
{
// Delegated transactions are paid by the delegated account.
auto const delegate = ctx_.tx.getAccountID(sfDelegate);
Copy link
Collaborator

@shawnxie999 shawnxie999 Mar 24, 2025

Choose a reason for hiding this comment

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

might as well add a check

        if (account_ == delegate)
            Throw<std::logic_error>("Account is same as delegated account");

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this will be unreachable. Because in Transactor::checkPermission, sle can never be found when account=delegate.

Copy link
Collaborator

Choose a reason for hiding this comment

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

it's expected to be unreachable. altho not necessary, it'd be good to add defensive checks

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if added at or before checkPermission, can we leave it out here

Copy link
Collaborator

Choose a reason for hiding this comment

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

Never throw on user input, particularly transaction data, unless it's unavoidable. There are mechanisms already in place to check and handle invalid user data. In this case, the standard behavior is to return tefINTERNAL or tecINTERNAL if a condition that should be impossible is encountered. Since this is in the function that pays the fee, tefINTERNAL is more appropriate.

Copy link
Collaborator

Choose a reason for hiding this comment

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

        if (account_ == delegate)

Also, if you haven't already, be sure to write a unit test covering this specific case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

there's another comment discussing moving this check before check before result = T::checkPermission(ctx.view, ctx.tx);
So the code in applySteps.cpp invoke_preclaim would be:

result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx));

if (result != tesSUCCESS)
        return result;

if (ctx.tx.isFieldPresent(sfDelegate) && *ctx.tx[~sfDelegate] == ctx.tx[sfAccount])
       return tefINTERNAL;

result = T::checkPermission(ctx.view, ctx.tx);

if (result != tesSUCCESS)
        return result;
...

or maybe add it one level up in applySteps.cpp's preclaim or even earlier in preflight?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, preclaim is supposed to be limited to checks that need the ledger. Since you're only looking at the transaction fields, the check should be in preflight. Specifically, preflight1, which would return a tem result indicating the transaction is malformed. Probably temBAD_SIGNER.

@@ -190,6 +195,22 @@ Transactor::Transactor(ApplyContext& ctx)
{
}

TER
Copy link
Collaborator

Choose a reason for hiding this comment

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

can also add a defensive check in Transactor::Transactor(ApplyContext& ctx) to throw a logic error if delegate exists but sfDelegate and sfAccount are the same

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

can we add this check before result = T::checkPermission(ctx.view, ctx.tx);?
If it is added before checkPermission, then I don't need to add it everywhere in checkPermission function

Copy link
Collaborator

Choose a reason for hiding this comment

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

sure

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.

7 participants