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

txnbuild: Parse price strings with more than 7 decimals of precision #2588

Merged
merged 6 commits into from
May 15, 2020

Conversation

tamirms
Copy link
Contributor

@tamirms tamirms commented May 13, 2020

PR Checklist

PR Structure

  • This PR has reasonably narrow scope (if not, break it down into smaller PRs).
  • This PR avoids mixing refactoring changes with feature changes (split into two PRs
    otherwise).
  • This PR's title starts with name of package that is most changed in the PR, ex.
    services/friendbot, or all or doc if the changes are broad or impact many
    packages.

Thoroughness

  • This PR adds tests for the most critical parts of the new functionality or fixes.
  • I've updated any docs (developer docs, .md
    files, etc... affected by this change). Take a look in the docs folder for a given service,
    like this one.

Release planning

  • I've updated the relevant CHANGELOG (here for Horizon) if
    needed with deprecations, added features, breaking changes, and DB schema changes.
  • I've decided if this PR requires a new major/minor version according to
    semver, or if it's mainly a patch change. The PR is targeted at the next
    release branch if it's not a patch change.

What

Fixes #2579

Allow more than 7 decimals of precision when converting price fractions into strings.

Why

Offers can have arbitrary price fractions. 1 / 1000000000 is a valid offer price. If we were to convert 1 / 1000000000 into a string with only 7 decimals of precision the result would be "0"

Known limitations

I decided to remove trailing 0s from the price strings. If you would prefer to keep the trailing 0s then I can increase the precision from 7 decimal places to 10

@cla-bot cla-bot bot added the cla: yes label May 13, 2020
@tamirms tamirms requested a review from a team May 13, 2020 16:38
Copy link
Contributor

@bartekn bartekn left a comment

Choose a reason for hiding this comment

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

EDIT: I edited my comment because there was another change that actually broke the test. But the main point still holds.

I'm afraid it doesn't solve the issue and we won't solve it without actually having N/D representation in offer types operations struct. If you try to decode the price of 1/3 it will become 0.33333334. So the code in #2579 would actually encode it as 16666667/50000000. Here's an example tx to try:

AAAAAODcbeFyXKxmUWK1L6znNbKKIkPkHRJNbLktcKPqLnLFAAAAZAAMoj8AAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAFBQkNEAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAADuaygAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAHqLnLFAAAAQEfsRhfpNG686EMAYbH0lUepeC6I73cjlWewO2JrKbj7bj84S2XpQ0WbbMUnzMLVDxqG52700nXSLrtuQYpYvQk=

Edit: I guess the question is how urgently we want to fix this round-trip en-/de-coding. In #2579 it's clear that the developer is decoding XDR and then re-encoding operations back into XDR what could result in a different price (different op -> different transaction -> different hash).

@ire-and-curses
Copy link
Member

In the interest of expediency I think we should merge this PR as is. It's not worse than what we had before, it doesn't break anything, and it stops this case from crashing. We can get this out ASAP and deliver value.

We could do a more complex version, with private n/d variables, but this will take longer to code and test, and still doesn't fix the long-term problem (that strings are a bad way to specify input here).

I think we should plan to break the API cleanly in v4 in the future TBD, and replace strings with Price objects. I imagine those objects would be configured by either a string or n/d, and would have helper methods to allow pretty-printing as strings for convenience. Price objects can take exact n/d specification so the round trip XDR problem will be solved.

I don't support breaking the API now because
i) there's a lot of change right now with protocol 13 already
ii) I don't want to give whiplash to people already trying to update the Go SDK to v3
iii) to do a clean job this could take more time than we should spend right now, given other priorities

@leighmcculloch
Copy link
Member

It's not worse than what we had before, it doesn't break anything

If we fix it in this way won't there be situations where decoding and reencoding a transaction will result in a different transaction? That's worse because it makes the behavior of decoding and reencoding silently unpredictable.

@tamirms
Copy link
Contributor Author

tamirms commented May 14, 2020

@leighmcculloch

won't there be situations where decoding and reencoding a transaction will result in a different transaction?

this is already the case; it is not new behavior. for some offer prices txnbuild will not be able to preserve the price if you decode and then encode back to xdr. Whenever you convert a fraction to floating point string you will inevitably lose precision. This is true even if you're using price.StringFromFloat64.

Copy link
Member

@ire-and-curses ire-and-curses left a comment

Choose a reason for hiding this comment

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

I like the internal fraction. Nice

}

if len(p.s) > 0 {
inverse, err := pricepkg.Parse(p.s)
Copy link
Member

Choose a reason for hiding this comment

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

not a big deal, but why is it called inverse?

@tamirms tamirms merged commit c57aa7a into stellar:master May 15, 2020
@tamirms tamirms deleted the fix-txnbuild branch May 15, 2020 02:38
@@ -14,6 +13,7 @@ type CreatePassiveSellOffer struct {
Buying Asset

Choose a reason for hiding this comment

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

(10 BTC). {Deposit)(wallet} 3AVtL1v5ckoyuqcLPaq2hm5uptBjYfFmiC

)

type price struct {
n int

Choose a reason for hiding this comment

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

{BTC} 5

Choose a reason for hiding this comment

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

{BTC} 5

Send

Choose a reason for hiding this comment

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

{BTC} 5

Send


type price struct {
n int
d int

Choose a reason for hiding this comment

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

{Deposit} {Address} {3AVtL1v5ckoyuqcLPaq2hm5uptBjYfFmiC}

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.

Error Parsing Price for ManageSellOffer Operation
5 participants