-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Amendment fixReducedOffersV2 fixes issue #4937 #5032
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #5032 +/- ##
=======================================
Coverage 71.3% 71.3%
=======================================
Files 796 796
Lines 66883 66887 +4
Branches 10889 10871 -18
=======================================
+ Hits 47658 47682 +24
+ Misses 19225 19205 -20
|
src/test/app/Flow_test.cpp
Outdated
|
||
testLineQuality(features); | ||
testFalseDry(features); | ||
testDirectStep(features - reducedOffersV2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why need to run this test with reducedOffersV2
enabled? There is no change to testDirectStep
with/without the amendment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, it's not needed. I was just following a pre-existing pattern where testDirectStep
and testBookStep
were having their amendments adjusted as a pair. I'd be fine with removing this test iteration if you'd like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be removed if it doesn't accomplish anything. It just increases the tests running time without any benefit.
{ | ||
env.require(balance(carol, EUR(1))); | ||
env.require(balance(bob, USD(0.4))); | ||
env.require(balance(bob, EUR(999))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to extend this test to show that the order book is actually blocked without the amendment?
{ | ||
if (limit < stpAmt.in) | ||
{ | ||
stpAmt.in = limit; | ||
auto const inLmt = | ||
mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn, /*roundUp*/ false); | ||
ofrAmt = offer.limitIn(ofrAmt, inLmt); | ||
// It turns out we can prevent order book blocking by (strictly) | ||
// rounding down the ceil_in() result. This adjustment changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be beneficial to add a comment here and in limitStepOut
explaining why the choice of the rounding in each case prevents order book blocking?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Maybe something like...
// It turns out we can prevent order book blocking by (strictly)
// rounding down the ceil_in() result. By rounding down we
// guarantee that the quality of an offer left in the ledger is
// as good or better than the quality of the containing order
// book page.
} | ||
else | ||
{ | ||
BEAST_EXPECT(blockedCount > 80); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we check for the exact count of the blocking rates since the same tests are executed on every run? If this count changes in the next release then doesn't it indicate some change that needs to be investigated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could. But the other, similar, tests in this file also just check for a threshold in the failing case. When I was initially developing the similar tests I found that small changes to the test would result in these numbers bouncing a bit. So just checking a threshold made development easier. The goal here is to verify that there was indeed a problem and that this test exercises the problem. Even one blocking offer would still be a problem, but we're verifying that there's at least a certain number.
The check where the number really matters in the one on line 782. We really want that value to be zero. That's how we have confidence that the problem is fully addressed, at least for this case.
So, yeah, I could put in an exact number. But my preference is not to. You okay with that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Looks good. I left a few minor comments for you to consider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review @gregtatcam. I've addressed a couple of your comments. There were also two that I didn't do anything with, but I left notes about why I they are the way they are. I'm open to continuing the conversation on those if you'd like.
{ | ||
if (limit < stpAmt.in) | ||
{ | ||
stpAmt.in = limit; | ||
auto const inLmt = | ||
mulRatio(stpAmt.in, QUALITY_ONE, transferRateIn, /*roundUp*/ false); | ||
ofrAmt = offer.limitIn(ofrAmt, inLmt); | ||
// It turns out we can prevent order book blocking by (strictly) | ||
// rounding down the ceil_in() result. This adjustment changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Maybe something like...
// It turns out we can prevent order book blocking by (strictly)
// rounding down the ceil_in() result. By rounding down we
// guarantee that the quality of an offer left in the ledger is
// as good or better than the quality of the containing order
// book page.
src/test/app/Flow_test.cpp
Outdated
|
||
testLineQuality(features); | ||
testFalseDry(features); | ||
testDirectStep(features - reducedOffersV2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, it's not needed. I was just following a pre-existing pattern where testDirectStep
and testBookStep
were having their amendments adjusted as a pair. I'd be fine with removing this test iteration if you'd like.
} | ||
else | ||
{ | ||
BEAST_EXPECT(blockedCount > 80); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could. But the other, similar, tests in this file also just check for a threshold in the failing case. When I was initially developing the similar tests I found that small changes to the test would result in these numbers bouncing a bit. So just checking a threshold made development easier. The goal here is to verify that there was indeed a problem and that this test exercises the problem. Even one blocking offer would still be a problem, but we're verifying that there's at least a certain number.
The check where the number really matters in the one on line 782. We really want that value to be zero. That's how we have confidence that the problem is fully addressed, at least for this case.
So, yeah, I could put in an exact number. But my preference is not to. You okay with that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is ready to go, but I left a couple of nits for you to consider. I'll approve it after you take a look. I'm fine with however you decide to resolve the nits (including keeping them as they are).
src/ripple/app/paths/AMMOffer.h
Outdated
@@ -103,15 +104,19 @@ class AMMOffer | |||
limitOut( | |||
TAmounts<TIn, TOut> const& offrAmt, | |||
TOut const& limit, | |||
bool fixReducedOffers, | |||
Rules const& rules, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using the globally available rules rather than passing this as a parameter. (also fine as-is, but I think I'd like to get away from passing rules around as much as we do and just using the new global).
src/ripple/app/paths/AMMOffer.h
Outdated
limitIn( | ||
TAmounts<TIn, TOut> const& offrAmt, | ||
TIn const& limit, | ||
Rules const& rules, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment above about passing rules as a param.
@@ -200,6 +200,29 @@ class Quality | |||
toAmount<In>(stRes.in), toAmount<Out>(stRes.out)); | |||
} | |||
|
|||
Amounts | |||
ceil_in_strict(Amounts const& amount, STAmount const& limit, bool roundUp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use a comment for how "strict" differs from the non-strict function.
|
||
template <class In, class Out> | ||
TAmounts<In, Out> | ||
ceil_in_strict( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually don't define functions inline in the class. I know this is a member template and that gets a little tedious outside of the class. I'd prefer it was defined out of the class, but it's a minor nit.
src/ripple/protocol/Quality.h
Outdated
TAmounts<In, Out> const& amount, | ||
In const& limit, | ||
bool roundUp) const | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a copy of ceil_in
with one function called changed.
If ceil_in
is being deprecated and will be replaced with ceil_in_strict
then I actually prefer the code duplication. And I think this is the case. If this is not the case, then let's look at merging the two.
On the same topic, if the ceil_in
is being deprecated, consider renaming ceil_in
to ceil_in_deprecated
or somesuch and renaming ceil_in_strict
to ceil_in
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about ceil_in
being deprecated. But it certainly has an odd behavior regarding rounding. So we might eventually want to replace all uses of it with ceil_in_strict
. But I'd like to hold off on renaming either of them until we know that we're actually going to make that change (which would have to go under an amendment). There are a few things in this code base that have been "deprecated" for quite a few years without ever being removed. I'd like to not add to that confusion.
Amounts | ||
Quality::ceil_in(Amounts const& amount, STAmount const& limit) const | ||
template <STAmount ( | ||
*DivRoundFunc)(STAmount const&, STAmount const&, Issue const&, bool)> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I usually like passing the callback as a parameter rather than as a template. But I'm also fine with this as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Proposed commit message:
|
* upstream/develop: (32 commits) fixInnerObjTemplate2 amendment (XRPLF#5047) Set version to 2.3.0-b1 Ignore restructuring commits (XRPLF#4997) Recompute loops (XRPLF#4997) Rewrite includes (XRPLF#4997) Rearrange sources (XRPLF#4997) Move CMake directory (XRPLF#4997) Add bin/physical.sh (XRPLF#4997) Prepare to rearrange sources: (XRPLF#4997) Change order of checks in amm_info: (XRPLF#4924) Add the fixEnforceNFTokenTrustline amendment: (XRPLF#4946) Replaces the usage of boost::string_view with std::string_view (XRPLF#4509) docs: explain how to find a clang-format patch generated by CI (XRPLF#4521) XLS-52d: NFTokenMintOffer (XRPLF#4845) chore: remove repeat words (XRPLF#5041) Expose all amendments known by libxrpl (XRPLF#5026) fixReducedOffersV2: prevent offers from blocking order books: (XRPLF#5032) Additional unit tests for testing deletion of trust lines (XRPLF#4886) Fix conan typo: (XRPLF#5044) Add new command line option to make replaying transactions easier: (XRPLF#5027) ...
…5032) Fixes issue XRPLF#4937. The fixReducedOffersV1 amendment fixed certain forms of offer modification that could lead to blocked order books. Reduced offers can block order books if the effective quality of the reduced offer is worse than the quality of the original offer (from the perspective of the taker). It turns out that, for small values, the quality of the reduced offer can be significantly affected by the rounding mode used during scaling computations. Issue XRPLF#4937 identified an additional code path that modified offers in a way that could lead to blocked order books. This commit changes the rounding in that newly located code path so the quality of the modified offer is never worse than the quality of the offer as it was originally placed. It is possible that additional ways of producing blocking offers will come to light. Therefore there may be a future need for a V3 amendment.
High Level Overview of Change
In issue #4937 @shortthefomo reported additional instances of order book blocking since the
fixReducedOffersV1
amendment went live. Thanks to that report I was able to identify an additional case where blocking offers could be generated. This pull request fixes the newly identified case.Context of Change
Having order books occasionally blocked by offers that were modified so that their quality was worse than the originally placed quality has been a problem for a while. Three different ways that this could occur were identified and addressed by amendment
fixReducedOffersV1
. All three of those case were addressed by carefully looking at and fixing the numeric rounding applied during calculations.This new amendment,
fixReducedOffersV2
, identifies and fixes an additional rounding case that was missed during the research forfixReducedOffersV1
.Beyond the fix itself, there are two kinds of unit test additions in this pull request.
There's a new test that directly exercises the failing case both with and without the amendment. It demonstrates that the old rounding behavior could lead to blocked order books. It also shows that with the new rounding behavior the order books are no longer blocked for this range of cases.
A few pre-existing test cases failed due to the changed rounding. Those test cases were examined and the tests patched to handle the new rounding behavior.
Type of Change
API Impact
None.