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

Introduce MPT support (XLS-33d): #5143

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

Conversation

gregtatcam
Copy link
Collaborator

High Level Overview of Change

New Transactions:

  • MPTokenIssuanceCreate
  • MPTokenIssuanceDestory
  • MPTokenIssuanceSet
  • MPTokenAuthorize

Modified Transactions:

  • Payment
  • Clawback

New Objects:

  • MPTokenIssuance
  • MPToken

API updates:

  • ledger_entry
  • account_objects
  • ledger_data

Refactor:

  • Add new types Asset, MPTIssue, MPTAmount to handle MPT amount.
  • Add SOElement constructor overload to describe the amount fields, which may have MPT amount (currently only sfAmount). The constructors are used in TxFormats to define fields supporting MPT.

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)

Test Plan

Added test for new feature:

  • MPT

Future Tasks

Integrate MPT into XRPL DEX.

Copy link

codecov bot commented Sep 19, 2024

Codecov Report

Attention: Patch coverage is 83.18068% with 202 lines in your changes missing coverage. Please review.

Project coverage is 76.2%. Comparing base (1fbf8da) to head (8af452e).
Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
src/libxrpl/protocol/STAmount.cpp 79.9% 34 Missing ⚠️
src/xrpld/rpc/handlers/LedgerEntry.cpp 5.9% 32 Missing ⚠️
src/xrpld/app/tx/detail/InvariantCheck.cpp 65.5% 30 Missing ⚠️
src/xrpld/app/tx/detail/Payment.cpp 86.5% 21 Missing ⚠️
src/libxrpl/basics/MPTAmount.cpp 12.5% 14 Missing ⚠️
src/xrpld/ledger/detail/View.cpp 91.8% 11 Missing ⚠️
src/libxrpl/protocol/Asset.cpp 70.4% 8 Missing ⚠️
src/xrpld/app/tx/detail/MPTokenAuthorize.cpp 92.5% 8 Missing ⚠️
src/libxrpl/protocol/MPTIssue.cpp 62.5% 6 Missing ⚠️
src/libxrpl/protocol/STParsedJSON.cpp 62.5% 6 Missing ⚠️
... and 13 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #5143     +/-   ##
=========================================
+ Coverage     76.2%   76.2%   +0.1%     
=========================================
  Files          760     775     +15     
  Lines        61568   62513    +945     
  Branches      8126    8212     +86     
=========================================
+ Hits         46909   47661    +752     
- Misses       14659   14852    +193     
Files with missing lines Coverage Δ
include/xrpl/basics/MPTAmount.h 100.0% <100.0%> (ø)
include/xrpl/basics/Number.h 100.0% <100.0%> (ø)
include/xrpl/basics/XRPAmount.h 98.8% <ø> (ø)
include/xrpl/basics/base_uint.h 96.6% <ø> (ø)
include/xrpl/protocol/AmountConversions.h 87.5% <100.0%> (ø)
include/xrpl/protocol/Feature.h 100.0% <ø> (ø)
include/xrpl/protocol/Indexes.h 100.0% <100.0%> (ø)
include/xrpl/protocol/Issue.h 100.0% <100.0%> (ø)
include/xrpl/protocol/MPTIssue.h 100.0% <100.0%> (ø)
include/xrpl/protocol/SField.h 100.0% <ø> (ø)
... and 57 more

... and 6 files with indirect coverage changes

Impacted file tree graph

@gregtatcam gregtatcam force-pushed the feature/mpt-v1-var-issues branch 2 times, most recently from 4ca85a2 to ba5f67c Compare September 19, 2024 14:22
New Transactions:
- MPTokenIssuanceCreate
- MPTokenIssuanceDestory
- MPTokenIssuanceSet
- MPTokenAuthorize

Modified Transactions:
- Payment
- Clawback

New Objects:
- MPTokenIssuance
- MPTokenAuthorize

API updates:
- ledger_entry
- account_objects
- ledger_data

Read full spec: https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0033d-multi-purpose-tokens

---------

Co-authored-by: Shawn Xie <[email protected]>
Co-authored-by: Howard Hinnant <[email protected]>
lsfMPTCanClawback = 0x00000040,

// ltMPTOKEN
lsfMPTAuthorized = 0x00000002,
Copy link
Contributor

Choose a reason for hiding this comment

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

lsfMPTLocked, lsfMPTCanLock and lsfMPTAuthorized do not have unique values within this enum. Is that on purpose?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The LedgerSpecificFlags enum is really just a way to define a bunch of constants. The flags are unique per ledger object type. Those are divided by the comment / label before each one. It might be worthwhile to split them into one per object type, but that's probably beyond the scope of this PR.

Copy link
Collaborator

@thejohnfreeman thejohnfreeman left a comment

Choose a reason for hiding this comment

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

Submitting the first phase of my review, a complete review of the changes to libxrpl.

include/xrpl/basics/MPTAmount.h Outdated Show resolved Hide resolved
include/xrpl/basics/MPTAmount.h Outdated Show resolved Hide resolved
include/xrpl/basics/MPTAmount.h Outdated Show resolved Hide resolved
include/xrpl/protocol/Asset.h Show resolved Hide resolved
include/xrpl/protocol/UintTypes.h Outdated Show resolved Hide resolved
src/libxrpl/protocol/LedgerFormats.cpp Show resolved Hide resolved
src/libxrpl/protocol/LedgerFormats.cpp Show resolved Hide resolved
src/libxrpl/protocol/LedgerFormats.cpp Show resolved Hide resolved
src/libxrpl/protocol/LedgerFormats.cpp Show resolved Hide resolved
src/libxrpl/protocol/STTx.cpp Outdated Show resolved Hide resolved
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.

Partial review:

include/xrpl/basics/MPTAmount.h Outdated Show resolved Hide resolved
src/test/jtx/impl/mpt.cpp Outdated Show resolved Hide resolved
include/xrpl/basics/MPTAmount.h Outdated Show resolved Hide resolved
Comment on lines +90 to +105
template <ValidIssueType TIss>
constexpr TIss const&
Asset::get() const
{
if (!std::holds_alternative<TIss>(issue_))
Throw<std::logic_error>("Asset is not a requested issue");
return std::get<TIss>(issue_);
}

template <ValidIssueType TIss>
TIss&
Asset::get()
{
if (!std::holds_alternative<TIss>(issue_))
Throw<std::logic_error>("Asset is not a requested issue");
return std::get<TIss>(issue_);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would it make sense to have a function like get that doesn't throw, but instead returns a std::optional<TIss>. It would return a nullopt if holds_alternative returned false.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea. It'll be currently unused though. It makes sense to add when needed.

include/xrpl/protocol/LedgerFormats.h Show resolved Hide resolved
include/xrpl/protocol/SOTemplate.h Outdated Show resolved Hide resolved
include/xrpl/protocol/SOTemplate.h Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
gregtatcam and others added 3 commits October 1, 2024 13:25
Update MPT Payment errors to be consistent with IOU Payment.
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.

Partial review


namespace ripple {

class MPTAmount : private boost::totally_ordered<MPTAmount>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seeing how similar MPTAmount is to XRPAmount it's a pity that they aren't implemented with common code. I.e. through a base class, or a template or something. Rather than diving down the potential rabbit hole of implementing it, I'm just going to leave this note here for someone else or for future reference.

Copy link
Collaborator

Choose a reason for hiding this comment

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

My opinion:

  • They should be called MPTNumber and XRPNumber because they represent quantities, while STAmount represents a quantity plus an asset / issue / unit (however you want to think of it). These are more like Number, and convert directly to and from it.
  • All of the arithmetic is moving to Number after the switchover anyway. In due time, when we retire that amendment, effectively locking it in permanently, then I think we can remove MPTAmount and XRPAmount.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should IOUAmount change too? I think that Amount better communicates what the value is. I don't think you'd say number when talking about tokens or currencies even if the values don't have associate unit. And XRPAmount doesn't need an issue, it's implicit. Also doesn't seem like this refactoring has to be done in MPT PR. But this is just my opinion. I'll change if everyone thinks Number is better. There are over 300 instances of XRPAmount, MPTAmount, IOUAmount.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I created a ticket to refactor MPTAmount and XRPAmount to use a common code + renaming.

include/xrpl/protocol/STAmount.h Show resolved Hide resolved
include/xrpl/protocol/STAmount.h Outdated Show resolved Hide resolved
include/xrpl/protocol/STAmount.h Outdated Show resolved Hide resolved
src/xrpld/app/misc/NetworkOPs.cpp Show resolved Hide resolved
src/libxrpl/protocol/LedgerFormats.cpp Show resolved Hide resolved
src/libxrpl/protocol/LedgerFormats.cpp Show resolved Hide resolved
src/libxrpl/protocol/MPTIssue.cpp Outdated Show resolved Hide resolved
@@ -21,6 +21,7 @@
#include <xrpl/basics/contract.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/core/LexicalCast.h>
#include <xrpl/protocol/Indexes.h>
Copy link
Collaborator

Choose a reason for hiding this comment

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

You don't need Indexes.h here, but you do need Protocol.h.

src/xrpld/ledger/detail/View.cpp Outdated Show resolved Hide resolved
Comment on lines +33 to +37
/* Asset is a variant of Issue (XRP and IOU) and MPTIssue (MPT).
* It enables handling of different issues when either one is expected.
* For instance, it extends STAmount class to support either issue
* in a general way. It handles specifics such arithmetics and serialization
* depending on specific issue type held by Asset.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I find it odd that you think of this as "either one" (of two, Issue or MPTIssue) instead of "any one" (of three, XRP, IOU, or MPT). Don't we want our abstract ledger functions to think in terms of 3 issue types instead of 2 specific implementations?

I find the example (STAmount) a little hard to understand, too. The "it" at the beginning of the last sentence refers to STAmount, while the "it" in the sentence before is Asset? I don't think "extends" is the right word either. Is the example just trying to convey that STAmount uses Asset to abstract arithmetic and serialization operations over all 3 issue types?

I'm left wondering why we have this Asset instead of making Issue support 3 issue types. STAmount has a native() -> bool method. Can Asset have that too? Issue has a getIssuer() -> AccountID method. Can Asset have that too? Why does Asset exist if it's not going to offer any helper methods? With no helpers, it could just be a type alias for std::variant.


namespace ripple {

class MPTAmount : private boost::totally_ordered<MPTAmount>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

My opinion:

  • They should be called MPTNumber and XRPNumber because they represent quantities, while STAmount represents a quantity plus an asset / issue / unit (however you want to think of it). These are more like Number, and convert directly to and from it.
  • All of the arithmetic is moving to Number after the switchover anyway. In due time, when we retire that amendment, effectively locking it in permanently, then I think we can remove MPTAmount and XRPAmount.

Comment on lines +91 to +93
friend std::istream&
operator>>(std::istream& s, MPTAmount& val);

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 was left behind.

Suggested change
friend std::istream&
operator>>(std::istream& s, MPTAmount& val);

Comment on lines +354 to +356
MPTID
makeMptID(AccountID const& account, std::uint32_t sequence);

Copy link
Collaborator

Choose a reason for hiding this comment

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

What do you think of putting sequence first in the parameter list? (Here and in the overload of mptIssuance(), and maybe elsewhere.)

mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept;

inline Keylet
mptoken(uint256 const& issuanceKey)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This parameter should be mptokenKey right? I think I confused you with my earlier comment on the other parameters. I was just saying that the issuance parameter name should end with Key like issuanceKey and mptokenKey.


namespace ripple {

class MPTIssue
Copy link
Collaborator

Choose a reason for hiding this comment

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

What does MPTIssue give me that MPTID does not? I'm guessing it's something like "so template code can call issue.getText() regardless of whether the issue object is an Issue or an MPTIssue." Is that right? I want the description to explain why this class exists and is not just a type alias of MPTID.

Comment on lines 100 to 101
JSS(MPTokenIssuance); // ledger type.
JSS(MPTokenIssuanceCreate); // transaction type.
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 worth it so we don't get radical re-formatting every time a few strings are added. We want the change set to highlight meaningful changes. GitHub's whitespace exclusion can hide these changes, but it can mask unwanted changes too. It's fine to restore the previous formatting so that this changeset has only the added strings.

@@ -543,6 +548,32 @@ isAccountFieldOkay(STObject const& st)
return true;
}

static bool
invalidMPTAmountInTx(STObject const& tx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Every transactor (the base Transactor) checks that the Fee field is XRP and non-negative. No extra work is necessary to keep out MPT. If this code and its caller were removed, would any tests succeed that should fail? I'm guessing some different error codes would be returned, but that's fine.

/** MPTID is a 192-bit value representing MPT Issuance ID,
* which is a concatenation of a 32-bit sequence (big endian)
* and a 160-bit account */
using MPTID = base_uint<192>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

@shawnxie999 suggests they are serialized in this order because STAmount is serialized in the order of currency then account ID. Whatever the reason, can you add it to this docstring, please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think there is any relevance between MPTID and how STAmount is serialized. MPTID is serialized as one block of data, there is no distinction between sequence and account in serialization. It could have been account first and then sequence.

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.

Partial review

src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
src/libxrpl/protocol/STAmount.cpp Outdated Show resolved Hide resolved
* Refactor getSNValue/getMPTValue
* Use std::min, std::max
* Fix min/max values for MPT
Comment on lines +207 to +218
// MPTokenIssuanceCreate flags:
constexpr std::uint32_t const tfMPTokenIssuanceCreateMask =
~(tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback | tfUniversal);

// MPTokenIssuanceDestroy flags:
constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal;

// MPTokenAuthorize flags:
constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfMPTUnauthorize | tfUniversal);

// MPTokenIssuanceSet flags:
constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfMPTLock | tfMPTUnlock | tfUniversal);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please move the masks next to their flags, like the pattern for AMM, and please put tfUniversal first in the sequences.

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.

5 participants