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

incorporate DeliverMax in Payment transactions #684

Merged
merged 25 commits into from
Jun 4, 2024
Merged

incorporate DeliverMax in Payment transactions #684

merged 25 commits into from
Jun 4, 2024

Conversation

ckeshava
Copy link
Collaborator

@ckeshava ckeshava commented Feb 8, 2024

High Level Overview of Change

Add support for DeliverMax field in the Payment transactions. The following links will give you more context on the DeliverMax field:
XRPLF/rippled#4733, XRPLF/rippled#3484

Context of Change

Payment transactions accept a new field DeliverMax, which serves as an alias to the Amount field. This is intended to prevent misinterpretation of the response of Payment transactions and the consequent loss of funds.

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)
  • Tests (You added tests for code that already exists, or your new feature included in this PR)
  • Documentation Updates
  • Release

Did you update CHANGELOG.md?

  • Yes
  • No, this change does not impact library users

Test Plan

Additional unit tests have been added to validate the usage of Payment transactions.

@ckeshava
Copy link
Collaborator Author

ckeshava commented Feb 8, 2024

@mvadari I felt it is more appropriate to make the deliver max changes in the from_dict overload. The autofill and other utility functions work with a Transaction object. Since DeliverMax is not a part of protocol, it is not a recognized field in the Payment request.

@mvadari
Copy link
Collaborator

mvadari commented Feb 13, 2024

@mvadari I felt it is more appropriate to make the deliver max changes in the from_dict overload. The autofill and other utility functions work with a Transaction object. Since DeliverMax is not a part of protocol, it is not a recognized field in the Payment request.

This won't work in a lot of cases (including internal uses of from_dict). A lot of people use Transaction.from_dict instead of the specific transaction type.

@ckeshava
Copy link
Collaborator Author

@mvadari I agree that there are limitations in my current design.

If I were to make this change in autofill method, how can I go about it? autofill takes in a Transaction object and I cannot address any field titled deliver_max because its not a part of the Payment transaction model here:

class Payment(Transaction):

I'd like to test inputs such as

transaction_dict = {
            "account": _ACCOUNT,
            "fee": _FEE,
            "sequence": _SEQUENCE,
            "deliver_max": _ISSUED_CURRENCY_AMOUNT,
            "send_max": _XRP_AMOUNT,
            "destination": _DESTINATION,
        }
# convert the above JSON into a Payment object, without throwing any errors.

We are passing along Transaction object to methods like autofill, autofill_and_sign, sign_and_submit etc. Where can I get the opportunity to modify the JSON version of a Transaction?

@mvadari
Copy link
Collaborator

mvadari commented Feb 21, 2024

@mvadari I agree that there are limitations in my current design.

If I were to make this change in autofill method, how can I go about it? autofill takes in a Transaction object and I cannot address any field titled deliver_max because its not a part of the Payment transaction model here:

That's a fair point. In that case, perhaps from_xrpl is the right place to have this change - but it should be in Transaction.from_xrpl, not Payment.from_xrpl.

@ckeshava
Copy link
Collaborator Author

I have moved the changes to Transaction.from_dict from the Payment class. from_xrpl invokes from_dict in the course of its actions. I felt the former is most appropriate to accomodate these changes.

@mvadari
Copy link
Collaborator

mvadari commented Feb 21, 2024

I felt the former is most appropriate to accomodate these changes.

I disagree, because it's only ever going to be necessary when pulling from XRPL data, not from any other dictionary.

xrpl/models/base_model.py Outdated Show resolved Hide resolved
xrpl/models/base_model.py Outdated Show resolved Hide resolved
xrpl/models/base_model.py Outdated Show resolved Hide resolved
@@ -468,3 +472,42 @@ def from_blob(tx_blob: str) -> Transaction:
The formatted transaction.
"""
return Transaction.from_xrpl(decode(tx_blob))

@classmethod
def from_xrpl(cls: Type[T], inp_params: Union[str, Dict[str, Any]]) -> T:
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
def from_xrpl(cls: Type[T], inp_params: Union[str, Dict[str, Any]]) -> T:
def from_xrpl(cls: Type[T], value: Union[str, Dict[str, Any]]) -> T:

This should use the same formatting as the function it's overriding.

ckeshava and others added 5 commits February 24, 2024 21:41
Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
@ckeshava
Copy link
Collaborator Author

I've addressed your comments @mvadari 👍

Co-authored-by: Mayukha Vadari <mvadari@ripple.com>
@ckeshava
Copy link
Collaborator Author

@mvadari does this design look okay? Can I do the same for JS too?

Copy link
Collaborator

@justinr1234 justinr1234 left a comment

Choose a reason for hiding this comment

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

nit changes only

@@ -73,6 +73,28 @@ def _value_to_json(value: XRPL_VALUE_TYPE) -> XRPL_VALUE_TYPE:
return value


def process_xrpl_json(value: Union[str, Dict[str, Any]]) -> Dict[str, Any]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems like this function can return exceptions which we should note and/or handle

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 know which exceptions might be thrown by this code. _key_to_json and _value_to_json do not throw any exceptions themselves

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do you suspect that json.load could throw a json.JSONDecodeError exception?

Copy link
Collaborator

Choose a reason for hiding this comment

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

This function will never be directly used by end users.

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 Yes, I agree. Do you mean to say we don't have to document all possible exceptions?

XRPLModelException: If Payment transactions have different values for
amount and deliver_max fields
"""
processed_value = process_xrpl_json(value)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This may not return successfully so we should likely note that in the docs

xrpl/models/transactions/transaction.py Outdated Show resolved Hide resolved
xrpl/models/transactions/transaction.py Outdated Show resolved Hide resolved
if "amount" in processed_value:
if processed_value["amount"] != processed_value["deliver_max"]:
raise XRPLModelException(
"Error: Amount and DeliverMax fields are not identical"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there maybe a link we can give to explain why they should be equal in the error message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm, I can point to the issue discussion here: XRPLF/rippled#3484

Here is an implementation commit to the rippled codebase: XRPLF/rippled@3972683

I'm not aware of any other documents summarizing this issue.

DeliverMax is an alias for the Amount field. So if they contain different values, it introduces semantic ambiguity. Which one should we use? Both of them refer to the same logical quantity -- the value of transferred money

Copy link
Collaborator

Choose a reason for hiding this comment

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

If both fields are passed in, then they have to be equal. We can update the error message for clarity: "Error: Amount and DeliverMax fields must be equal if both are provided"

Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't link rippled issues from the libraries, point to the xrpl.org documentation. It's not useful to do so in error messages, because that makes the error message really long, but it can be in the docstring.

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 was brainstorming ideas for resource pages for this issue, I won't include it in the error message 👍

I couldn't find a statement that says "If present, DeliverMax field must equal Amount" in the Payments docs. https://xrpl.org/docs/references/protocol/transactions/types/payment#payment-fields

I feel it's a better developer experience to provide an elaborate error message, instead of making them search through a website.

Copy link
Collaborator

Choose a reason for hiding this comment

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

DeliverMax is only an RPC field, rippled doesn't care about it. It's only ever returned. That documentation should probably elaborate on that a bit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, documentation will help. DeliverMax can also be used as an input in APIv2 rippled.

Every instance of DeliverMax, at the protocol (or) binary-codec-level, is translated into Amount.

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, it was not changed at the protocol level, just at the RPC level.

xrpl/models/transactions/transaction.py Outdated Show resolved Hide resolved
Co-authored-by: Omar Khan <khancodegt@gmail.com>
@ckeshava ckeshava requested a review from khancode March 22, 2024 00:08
@@ -73,6 +73,28 @@ def _value_to_json(value: XRPL_VALUE_TYPE) -> XRPL_VALUE_TYPE:
return value


def process_xrpl_json(value: Union[str, Dict[str, Any]]) -> Dict[str, Any]:
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd recommend making this a helper class method, instead of a separate function - it saves you some imports. Similar to _get_only_init_args.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

okay, I have done this. But I dislike the fact that the self/cls argument is not useful inside the process_xrpl_json function. I'm only using it to hook into this function

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 the tradeoff is worth it given that it makes it easier to use, but if someone strongly objects I'll withdraw

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah, I think it's a reasonable tradeoff 👍

@ckeshava ckeshava requested a review from mvadari March 22, 2024 17:16
@ckeshava
Copy link
Collaborator Author

@khancode can you please take a look at this PR? does this design look alright?

@justinr1234 I need some clarification on your prior comments. Let me know if I need to make any more changes.

thnks

Copy link
Collaborator

@khancode khancode left a comment

Choose a reason for hiding this comment

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

LGTM, just need to change a method to be private.

xrpl/models/base_model.py Outdated Show resolved Hide resolved
@ckeshava
Copy link
Collaborator Author

the snippets tests are failing due to an unfunded account (We believe it lacks "USD" tokens). It will need to be addressed in a separate PR

@ckeshava
Copy link
Collaborator Author

ckeshava commented Jun 4, 2024

If there are no objections, I'd like to merge this PR.

Thank you all for your feedback

@ckeshava ckeshava merged commit 8f7472a into main Jun 4, 2024
21 checks passed
@ckeshava ckeshava deleted the dMax branch June 4, 2024 22:13
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.

4 participants