-
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
Rm some offers where the quality is reduced #3827
Conversation
return offer_.amount(); | ||
}(); | ||
|
||
XRPAmount const thresholdToRm{2}; |
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.
A comment to explain why this is 2 rather than 1 would be helpful in understanding the issue.
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.
@donovanhide I changed it back to 1. Anything with values larger than 1 should give a quality that's reasonably in line with the initial quality. The reason I made it larger is I was thinking "removing some more tiny offers is a good thing", but they should be removed with regular operation. There's no need for the larger threshold.
Fixed in the patch 7474063b72 [fold] Change the threshold and minor refactor
I made some changes to support IOU/IOU offers and squashed (squashed because the implementation was different enough that it wasn't useful to look at the older patch). |
Substantial reductions in an offer's effective quality from its initial quality may clog offer books.
I've just encountered this blocking offer, as seen through
Is the definite proof that this offer is a blocker the |
I haven't finished my review yet, but the results are very promising. I just finished replaying the transaction that started this hunt. With the new code in place both Taker and FlowCross have the same results for that transaction. I think that's great news! |
@donovanhide I don't think we can look at the offer alone and find definite proof that it's a blocker. We need to look at the original offer quality and see how owner funds effect it. So I could concoct an offer with tiny owner funds and a reported Having said that, a reported XRP amount of As an aside, that |
@seelabs So the correct way to calculate the "blockerness" is to do |
@donovanhide A decent check would be: 1) only look at offers with Having said all that, a good "quick and dirty check" is to look for funded offers with a reported One more quick note: A small payment that sends |
@seelabs That's great, thanks for the details! |
I'm still working on the code review, but I'm almost done. Nice work! I noted the concern that we're not removing underfunded offers when the payment/offer crossing fails. So I decided to see how important that concern might be. I kept a record of Offer Crossing and Payments for an hour to see the ratio of successes to
I was surprised that the percentage of failing payments was that high. But even at that, if we look a the sum of offer crossing and payments success vs |
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.
Nice changes and great unit tests. I left a few thoughts for you to consider, but none of them are show stoppers for me.
src/test/app/Offer_test.cpp
Outdated
env.close(); | ||
env.trust(USD(1000), alice, bob, carol); | ||
// underfund carol's offer | ||
auto initialCarolUSD = USD(0.1); |
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.
FWIW, this value can be raised to USD(0.499)
and still have the test behave as expected. At USD(0.5)
the case that enables all amendments other than rmSmallIncreasedQOffers
fails (which is what we'd hope for).
My inclination would be to use USD(0.499)
for the test. It's nice to set the test conditions near an inflection point so we are more sensitive to conditions changing in the future. But that's just a suggestion.
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.
fixed in d77ad06020 [fold] Change test params so they're closer to behavior change thresholds
src/test/app/Offer_test.cpp
Outdated
env.close(); | ||
env.trust(USD(1000), alice, bob, carol); | ||
env.close(); | ||
auto const initialCarolUSD = USD(0.1); |
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.
Oh! Interesting! Here we can run this value all the way up to USD(0.999)
and have the test pass as expected under the various enabled amendments. If I take the value to USD(1.0)
then the test fails the case that enables all amendments other than rmSmallIncreasedQOffers
.
So my inclination would be to use USD(0.999)
here, since it's close to the cusp where the test would start to fail. That way if conditions change in the future we're more likely to see it. That's just a suggestion, however.
It's interesting that the critical point is different between offer crossing and payments. I suspect it's a difference in rounding behaviors, but I didn't chase it down. I think the difference is interesting, but not critical.
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.
fixed in d77ad06020 [fold] Change test params so they're closer to behavior change thresholds
static_assert( | ||
std::is_same_v<TTakerPays, IOUAmount> || | ||
std::is_same_v<TTakerPays, XRPAmount>, | ||
""); |
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.
Rather than leave the quotes empty, I might be inclined to say "STAmount is not supported"
, since that's the most likely alternative. But your call.
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.
fixed in 9b8c050cb3 [fold] Minor cleanups
static_assert( | ||
std::is_same_v<TTakerGets, IOUAmount> || | ||
std::is_same_v<TTakerGets, XRPAmount>, | ||
""); |
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.
Same comment as above.
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.
fixed in 9b8c050cb3 [fold] Minor cleanups
return false; | ||
|
||
// Consider removing the offer if `TakerPays` is XRP or `TakerPays` and | ||
// `TakerGets` are both IOU and `TakerPays` < `TakerGets` |
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.
It took quite a while for me to make sense out the comment regarding IOUs. Everything here is correct, but the consequences did not jump out at me. Consider rewriting the comment something like this:
// Consider removing the offer...
// o If `TakerPays` is XRP (because of XRP drops granularity) or
// o If...
// - `TakerPays` and `TakerGets` are both IOU and
// - `TakerPays` < `TakerGets` (numerator < denominator)
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.
fixed in 9b8c050cb3 [fold] Minor cleanups
@@ -129,6 +129,9 @@ class IOUAmount : private boost::totally_ordered<IOUAmount>, | |||
{ | |||
return mantissa_; | |||
} | |||
|
|||
static IOUAmount | |||
minPositiveAmount(); |
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.
Long term, this could be made constexpr
. But that will require making IOUAmount::normalize()
constexpr
, which is too big a change to make right now.
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.
If it were easy I'd do it, but this is going to be a rarely called function; making this constexpr
isn't going to to make much difference one way or the other.
} | ||
} | ||
} | ||
|
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'll mention that there's a test case in this file (around line 5700) that displays "false assert". Taken out of context that name is a little scary. I chased it briefly until I figured out where it was coming from. As a drive-by change consider changing
testcase("false assert");
to
testcase("incorrect assert fixed");
Just a thought.
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.
fixed in 9b8c050cb3 [fold] Minor cleanups
@@ -222,6 +291,66 @@ TOfferStreamBase<TIn, TOut>::step() | |||
<< "Removing became unfunded offer " << entry->key(); | |||
} | |||
offer_ = TOffer<TIn, TOut>{}; | |||
// See comment at top of loop for why the offer is removed |
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 the comments in the block describe why we're removing the offer, what's not quite clear at this point is how. The comment at the top of the loop makes that clear. Consider changing this comment to "... for how the ...".
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.
fixed in 9b8c050cb3 [fold] Minor cleanups
// Without the `if constexpr`, the | ||
// `shouldRmSmallIncreasedQOffer` template will be instantiated | ||
// even if it is never used. This can cause compiler errors in | ||
// some cases, hense the `if constexpr` guard. |
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.
Great comment! Thanks! BTW, perhaps change "hense" to "hence'?
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.
fixed in 9b8c050cb3 [fold] Minor cleanups
<< entry->key(); | ||
} | ||
offer_ = TOffer<TIn, TOut>{}; | ||
// See comment at top of loop for why the offer is removed |
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.
Similar to earlier comment. Consider "... for how the ...".
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.
fixed in 9b8c050cb3 [fold] Minor cleanups
Thanks for the great view @scottschurr! I think I addressed all the comments. |
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.
Looks reasonable. Left some comments that I'd like to see answered/addressed before signing off.
template <class T> | ||
T | ||
toAmount(IOUAmount const& amt) | ||
{ | ||
static_assert(sizeof(T) == -1, "Must use specialized function"); | ||
return T(0); | ||
} |
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 not just = delete
this instead of using the curious sizeof(T) == -1
construct?
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 never think of using delete
for anything but constructors and assignment operators. But you're right, this is a perfect use for them. Thanks!
Fixed in a06ca4c951 Misc cleanups (Nik's review)
|
||
// Carol doesn't quite have enough funds for this offer | ||
// The amount left after this offer is taken will cause | ||
// STAmount to incorrectly round to zero when the next offer | ||
// (at a good quality) is considered. (when the | ||
// stAmountCalcSwitchover2 patch is inactive) | ||
env(offer(carol, drops(1), USD(1))); | ||
env(offer(carol, drops(1), USD(0.99999))); |
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 stAmountCalcSwitchover2
was removed, so while here we should update the comment.
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 didn't want to lose the reference to stAmountCalcSwitchover2
. I modified the comment to say that is "now removed". Keeping that reference is a reminder of the motivation for this test case.
Fixed in a06ca4c951 Misc cleanups (Nik's review)
|
||
TTakerPays const thresh = TTakerPays::minPositiveAmount(); | ||
|
||
if (effectiveAmounts.in > thresh) |
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.
At your option, micronit: thresh
is only ever used here and could just be inlined: if (effectiveAmounts.in > TTakerPays::minPositiveAmount())
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.
Fixed in a06ca4c951 Misc cleanups (Nik's review)
if constexpr (!(std::is_same_v<TIn, IOUAmount> || | ||
std::is_same_v<TOut, XRPAmount>)) | ||
return shouldRmSmallIncreasedQOffer<XRPAmount, IOUAmount>(); |
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.
The logic here just confuses me. First, is there a reason we don't use (!std::is_same_v<TIn, IOUAmount> && !std::is_same_v<TOut, XRPAmount>)
? For that matter, why not just say what we, apparently, mean, which I think is: (std::is_same_v<TIn, XRPAmount> && std::is_same_v<TOut, IOUAmount>)
?
Second, and maybe it's the fact that it's almost midnight, but I'm confused why the conditional is even necessary? Under what circumstances do we enter this codepath where inIsXRP == true
but std::is_same_v<TIn, XRPAmount> == false
?
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 are three questions:
-
Why not
(!std::is_same_v<TIn, IOUAmount> && !std::is_same_v<TOut, XRPAmount>)
?
Sometimes I write!(error_condition1 || error_condition2)
instead of(!error_condition1 && !error_conditions2)
. Think of the huge savings by saving one fewer!
. More seriously, I don't have a strong preference; I find both about equally readable. -
Why not
(std::is_same_v<TIn, XRPAmount> && std::is_same_v<TOut, IOUAmount>)
?
BecauseTIn
can beXRPAmount
orSTAmount
, andTOut
can beIOUAmount
orSTAmount
. -
Why is the conditional even needed? Under what circumstances do we enter this codepath...?
While the codepath would never be executed, the template would still be instantiated, and we'd get compiler errors. In particular, we'd end up calling things like:toAmount<IOUAmount>(xrpAmount);
Even though we know this will never be executed, the compiler will still try to compile that and we'd get a compiler error.Theif constexpr
prevents the template from being instantiated at all.
I added some minor clarifying comments, but kept the code essentially the same. I can revisit if you still think this is confusing.
} | ||
if (!inIsXRP && outIsXRP) | ||
{ | ||
// See comment above for `if constexpr` rational |
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.
Typo: rational
should be rationale
.
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.
Fixed in a06ca4c951 Misc cleanups (Nik's review)
} | ||
else | ||
{ | ||
JLOG(j_.trace()) << "Removing became tiny offer due to " |
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.
"Removing became" is gramatically incorrect.
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.
It mirrors the "became unfunded" terminology we use for offers. Having said that, I don't mind rewording it. Changed to "removing tiny offer that became tiny due to..."
Fixed in a06ca4c951 Misc cleanups (Nik's review)
* Inline minPositiveAmount * Add some clarifying comments * Fix mispelling * Use deleted function instead of static_assert
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 to me!
Unfortunately, looks like this is still happening:
|
Might not be related, but this offer is uncrossable.
|
If relevant, please feel free to copy the above examples to #4081 (new issue where this problem is being worked on) |
High Level Overview of Change
Substantial reductions in an offer's effective quality from its initial quality may clog offer books. This patch removes such offers, much like unfunded offers are removed.
Type of Change