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

Fix ApproxRoot Infinite Looping #7140

Merged
merged 17 commits into from
Aug 29, 2020
Merged

Fix ApproxRoot Infinite Looping #7140

merged 17 commits into from
Aug 29, 2020

Conversation

migueldingli1997
Copy link
Contributor

@migueldingli1997 migueldingli1997 commented Aug 23, 2020

Description

Closes: #7038

Added a maximum number of iterations (100) to ApproxRoot (and ApproxSqrt) to serve as a hard limit on the number of iterations that the algorithm performs. If the answer converges before the maximum iterations, it returns immediately. Otherwise, if the answer never converges enough to satisfy the primary loop condition, the looping stops after a hundred iterations.

A test case showing a scenario where the answer never converges enough was added, with inline commenting.


Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • Targeted PR against correct branch (see CONTRIBUTING.md)
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the module structure standards.
  • Wrote unit and integration tests
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/)
  • Added relevant godoc comments.
  • Added a relevant changelog entry to the Unreleased section in CHANGELOG.md
  • Re-reviewed Files changed in the Github PR explorer
  • Review Codecov Report in the comment section below once CI passes

@migueldingli1997 migueldingli1997 changed the title Miguel/7038 approx root infinite loop fix Fix ApproxRoot Infinite Looping Aug 23, 2020
@codecov
Copy link

codecov bot commented Aug 23, 2020

Codecov Report

Merging #7140 into master will not change coverage.
The diff coverage is 100.00%.

@@           Coverage Diff           @@
##           master    #7140   +/-   ##
=======================================
  Coverage   54.78%   54.78%           
=======================================
  Files         563      563           
  Lines       38624    38624           
=======================================
  Hits        21162    21162           
  Misses      15730    15730           
  Partials     1732     1732           

@alessio
Copy link
Contributor

alessio commented Aug 23, 2020

If this is accepted as-is, for Launchpad I'll propose a specific non-breaking patch + documentation and appropriate section in the release notes. @clevinson @ethanfrey

types/decimal.go Outdated
func (d Dec) ApproxRoot(root uint64) (guess Dec, err error) {
// The maximum number of iterations is required as a backup boundary condition for
// cases where the answer never converges enough to satisfy the main condition.
func (d Dec) ApproxRoot(root, maxIters uint64) (guess Dec, err error) {
Copy link
Contributor

@alexanderbez alexanderbez Aug 24, 2020

Choose a reason for hiding this comment

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

I personally don't feel comfortable with this code. If this is necessary by the application, why not just define it there? We don't even use this in the SDK or relevant applications (e.g. gaia and simapp).

I would prefer to remove it outright.

Copy link
Contributor Author

@migueldingli1997 migueldingli1997 Aug 24, 2020

Choose a reason for hiding this comment

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

I understand. My reasoning is that it's nice to have root calculation functionality built into the SDK alongside the Dec type, but as you said one could implement it on the app side of things if they need it.

I'm curious if it's the solution approach that you don't feel comfortable with or the function/s in general? I could try a different approach if the former is the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we want to remove it outright, I can get that done instead of the above solution.

Copy link
Member

Choose a reason for hiding this comment

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

I think having a root function is pretty important for a Decimal/Number library, as its a pretty common operation. And implementing a solid version in the library itself would be better than having different applications all having their own potentially broken implementation (oops 😅).

@ethanfrey
Copy link
Contributor

When this is resolved, one way or the other, I agree with merging it into launchpad (after another review pass).
Having a potential infinite loop seems like a good bug to patch.

@sunnya97
Copy link
Member

This seems like a good solution to me. Do we want the end users to provide a MaxIterations, or just hardcode a reasonable default? Requiring them to provide it would need the user to understand the inner workings of how the function calculates roots, which seems unnecessary.

@migueldingli1997
Copy link
Contributor Author

migueldingli1997 commented Aug 25, 2020

This seems like a good solution to me. Do we want the end users to provide a MaxIterations, or just hardcode a reasonable default? Requiring them to provide it would need the user to understand the inner workings of how the function calculates roots, which seems unnecessary.

I had these thoughts as well actually but my reasoning is that with any other function the user will always need to understand the inner workings to some extent. That being said, from my experimentation (and test cases included in the PR) it seems typically the number of iterations required is in the 10's (< 100), so maybe a default that could be used would be around 100.

Not sure if there's a way in Go to allow the default to be bypassed for extra iterations without having to implement an extra function or complicating things elsehow.

@alessio
Copy link
Contributor

alessio commented Aug 25, 2020

For what concerns Launchpad, I'd love to avoid breaking API and ABI, thus the folllowing:

just hardcode a reasonable default?

might actually be a reasonable fix IMHO. Wdyt? @ethanfrey @clevinson

@ethanfrey
Copy link
Contributor

t seems typically the number of iterations required is in the 10's (< 100), so maybe a default that could be used would be around 100.

Sounds good. Same API, just hardcoded max of 100. Avoids an attack vector and I doubt any real case will be too far off reality after 100 steps

@migueldingli1997
Copy link
Contributor Author

I've updated the code and doc to instead use a hard-coded maximum number of iterations. Please check it out :)

types/decimal.go Outdated Show resolved Hide resolved
types/decimal.go Outdated Show resolved Hide resolved
Copy link
Contributor

@alessio alessio left a comment

Choose a reason for hiding this comment

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

Looks good to go! Thanks @migueldingli1997!

@migueldingli1997
Copy link
Contributor Author

Looks good to go! Thanks @migueldingli1997!

No problem. I just fixed 10 to 100

Copy link
Contributor

@amaury1093 amaury1093 left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@alessio
Copy link
Contributor

alessio commented Aug 27, 2020

@alexanderbez is in favor of removing the ApproxRoot function from the SDK altogether, please see here #7140 (comment). His main argument is the SDK does not use it, thus we should remove it [1]

Whilst I acknowledge and partly agree with @alexanderbez's point, I suppose that ApproxRoot could potentially be useful to applications and should be maintained in the SDK as long as the current implementation is fixed. Either way, it's a nice to have and definitely not a big deal.

@aaronc @clevinson I'd like to hear from you guys what you think!

[1] Bez any mischaracterization is purely incidental, so please elaborate further if you believe I misunderstood your message :-)

@clevinson
Copy link
Contributor

Looks good to me, I'm in agreement that an ApproxRoot method is useful to have with a decimal library, but its also confusing to me why cosmos needs its own implementaiton of decimals in general... Not trying to dig issue up again here, but that's my perspective.

Given that we have Dec in the sdk, i think having ApproxRoot there as well (with the solution of hardcoding a max iterations in this PR) feels like the right approach.

@alessio
Copy link
Contributor

alessio commented Aug 28, 2020

@clevinson dixit:

its also confusing to me why cosmos needs its own implementaiton of decimals in general

I hear you brother, I hear you... (cit.)

But that's a way longer conversation...

@aaronc
Copy link
Member

aaronc commented Aug 28, 2020

This sounds fine for now. We should consider something like https://github.com/cockroachdb/apd long term

@alessio
Copy link
Contributor

alessio commented Aug 28, 2020

@alexanderbez Considering #7140 (comment) and #7140 (comment), could we merge this in now then? Is there any other concerns do you reckon we should address before merging this in?

@alexanderbez
Copy link
Contributor

No need to rest on my approval here. I still think we don't need half-baked APIs like this, but if there is a strong need for it, merge away 👍

@alessio alessio merged commit 018915b into cosmos:master Aug 29, 2020
alessio pushed a commit that referenced this pull request Aug 29, 2020
Added a maximum number of iterations (100) to ApproxRoot
(and ApproxSqrt) to serve as a hard limit on the number of
iterations that the algorithm performs. If the answer converges
before the maximum iterations, it returns immediately. Otherwise,
if the answer never converges enough to satisfy the primary loop
condition, the looping stops after a hundred iterations.

Closes: #7038
alessio pushed a commit that referenced this pull request Aug 31, 2020
Added a maximum number of iterations (100) to ApproxRoot
(and ApproxSqrt) to serve as a hard limit on the number of
iterations that the algorithm performs. If the answer converges
before the maximum iterations, it returns immediately. Otherwise,
if the answer never converges enough to satisfy the primary loop
condition, the looping stops after a hundred iterations.

Closes: #7038

Co-authored-by: Miguel Dingli <[email protected]>
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.

ApproxRoot Infinite Looping
8 participants