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

Rm some offers where the quality is reduced #3827

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions src/ripple/app/tx/impl/OfferStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include <ripple/app/tx/impl/OfferStream.h>
#include <ripple/basics/Log.h>
#include <ripple/protocol/Feature.h>

namespace ripple {

Expand Down Expand Up @@ -132,6 +133,74 @@ accountFundsHelper(
view, id, issue.currency, issue.account, freezeHandling, j));
}

template <class TIn, class TOut>
template <class TTakerPays, class TTakerGets>
bool
TOfferStreamBase<TIn, TOut>::shouldRmSmallIncreasedQOffer() const
{
static_assert(
std::is_same_v<TTakerPays, IOUAmount> ||
std::is_same_v<TTakerPays, XRPAmount>,
"");
Copy link
Collaborator

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.

Copy link
Collaborator Author

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>,
"");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment as above.

Copy link
Collaborator Author

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<TTakerPays, XRPAmount> ||
!std::is_same_v<TTakerGets, XRPAmount>,
"Cannot have XRP/XRP offers");

if (!view_.rules().enabled(fixRmSmallIncreasedQOffers))
return false;

// Consider removing the offer if `TakerPays` is XRP or `TakerPays` and
// `TakerGets` are both IOU and `TakerPays` < `TakerGets`
Copy link
Collaborator

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)

Copy link
Collaborator Author

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

constexpr bool const inIsXRP = std::is_same_v<TTakerPays, XRPAmount>;
constexpr bool const outIsXRP = std::is_same_v<TTakerGets, XRPAmount>;

if (outIsXRP)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could be if constexpr.

Copy link
Collaborator Author

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

{
// If `TakerGets` is XRP, worse this offer's quality can change is to
Copy link
Collaborator

Choose a reason for hiding this comment

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

Possibly, "worse" -> "the worst"?

Copy link
Collaborator Author

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

// about 10^-81 `TakerPays` and 1 drop `TakerGets`. This will be
// remarkably good quality for any realistic asset, so these offers
// don't need this extra check.
return false;
}

TAmounts<TTakerPays, TTakerGets> const ofrAmts{
toAmount<TTakerPays>(offer_.amount().in),
toAmount<TTakerGets>(offer_.amount().out)};

if constexpr (!inIsXRP && !outIsXRP)
{
if (ofrAmts.in >= ofrAmts.out)
return false;
}

TTakerGets const ownerFunds = toAmount<TTakerGets>(*ownerFunds_);

auto const effectiveAmounts = [&] {
if (offer_.owner() != offer_.issueOut().account &&
ownerFunds < ofrAmts.out)
{
// adjust the amounts by owner funds
return offer_.quality().ceil_out(ofrAmts, ownerFunds);
}
return ofrAmts;
}();

TTakerPays const thresh = TTakerPays::minPositiveAmount();

if (effectiveAmounts.in > thresh)
Copy link
Contributor

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())

Copy link
Collaborator Author

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)

return false;

Quality const effectiveQuality{effectiveAmounts};
return effectiveQuality < offer_.quality();
}

template <class TIn, class TOut>
bool
TOfferStreamBase<TIn, TOut>::step()
Expand Down Expand Up @@ -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
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 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 ...".

Copy link
Collaborator Author

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

continue;
}

bool const rmSmallIncreasedQOffer = [&] {
bool const inIsXRP = isXRP(offer_.issueIn());
bool const outIsXRP = isXRP(offer_.issueOut());
if (inIsXRP && !outIsXRP)
{
// 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.
Copy link
Collaborator

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'?

Copy link
Collaborator Author

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

if constexpr (!(std::is_same_v<TIn, IOUAmount> ||
std::is_same_v<TOut, XRPAmount>))
return shouldRmSmallIncreasedQOffer<XRPAmount, IOUAmount>();
Comment on lines +308 to +310
Copy link
Contributor

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?

Copy link
Collaborator Author

@seelabs seelabs May 6, 2021

Choose a reason for hiding this comment

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

There are three questions:

  1. 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.

  2. Why not (std::is_same_v<TIn, XRPAmount> && std::is_same_v<TOut, IOUAmount>)?
    Because TIn can be XRPAmount or STAmount, and TOut can be IOUAmount or STAmount.

  3. 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.The if 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
Copy link
Contributor

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.

Copy link
Collaborator Author

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, XRPAmount> ||
std::is_same_v<TOut, IOUAmount>))
return shouldRmSmallIncreasedQOffer<IOUAmount, XRPAmount>();
}
if (!inIsXRP && !outIsXRP)
{
// See comment above for `if constexpr` rational
if constexpr (!(std::is_same_v<TIn, XRPAmount> ||
std::is_same_v<TOut, XRPAmount>))
return shouldRmSmallIncreasedQOffer<IOUAmount, IOUAmount>();
}
assert(0); // xrp/xrp offer!?! should never happen
return false;
}();

if (rmSmallIncreasedQOffer)
{
auto const original_funds = accountFundsHelper(
cancelView_,
offer_.owner(),
amount.out,
offer_.issueOut(),
fhZERO_IF_FROZEN,
j_);

if (original_funds == *ownerFunds_)
{
permRmOffer(entry->key());
JLOG(j_.trace())
<< "Removing tiny offer due to reduced quality "
<< entry->key();
}
else
{
JLOG(j_.trace()) << "Removing became tiny offer due to "
Copy link
Contributor

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.

Copy link
Collaborator Author

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)

"reduced quality "
<< entry->key();
}
offer_ = TOffer<TIn, TOut>{};
// See comment at top of loop for why the offer is removed
Copy link
Collaborator

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 ...".

Copy link
Collaborator Author

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

continue;
}

Expand Down
4 changes: 4 additions & 0 deletions src/ripple/app/tx/impl/OfferStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ class TOfferStreamBase
virtual void
permRmOffer(uint256 const& offerIndex) = 0;

template <class TTakerPays, class TTakerGets>
bool
shouldRmSmallIncreasedQOffer() const;

public:
TOfferStreamBase(
ApplyView& view,
Expand Down
3 changes: 3 additions & 0 deletions src/ripple/basics/IOUAmount.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ class IOUAmount : private boost::totally_ordered<IOUAmount>,
{
return mantissa_;
}

static IOUAmount
minPositiveAmount();
Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

};

std::string
Expand Down
6 changes: 6 additions & 0 deletions src/ripple/basics/XRPAmount.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ class XRPAmount : private boost::totally_ordered<XRPAmount>,
s >> val.drops_;
return s;
}

static XRPAmount
minPositiveAmount()
{
return XRPAmount{1};
}
};

/** Number of drops per 1 XRP */
Expand Down
8 changes: 7 additions & 1 deletion src/ripple/basics/impl/IOUAmount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ static std::int64_t const maxMantissa = 9999999999999999ull;
static int const minExponent = -96;
static int const maxExponent = 80;

IOUAmount
IOUAmount::minPositiveAmount()
{
return IOUAmount(minMantissa, minExponent);
}

void
IOUAmount::normalize()
{
Expand Down Expand Up @@ -353,7 +359,7 @@ mulRatio(
{
if (!result)
{
return IOUAmount(minMantissa, minExponent);
return IOUAmount::minPositiveAmount();
}
// This addition cannot overflow because the mantissa is already
// normalized
Expand Down
30 changes: 30 additions & 0 deletions src/ripple/protocol/AmountConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,36 @@ toAmount<XRPAmount>(STAmount const& amt)
return XRPAmount(sMant);
}

template <class T>
T
toAmount(IOUAmount const& amt)
{
static_assert(sizeof(T) == -1, "Must use specialized function");
return T(0);
}
Copy link
Contributor

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?

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 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)


template <>
inline IOUAmount
toAmount<IOUAmount>(IOUAmount const& amt)
{
return amt;
}

template <class T>
T
toAmount(XRPAmount const& amt)
{
static_assert(sizeof(T) == -1, "Must use specialized function");
return T(0);
}

template <>
inline XRPAmount
toAmount<XRPAmount>(XRPAmount const& amt)
{
return amt;
}

} // namespace ripple

#endif
2 changes: 2 additions & 0 deletions src/ripple/protocol/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class FeatureCollections
"TicketBatch",
"FlowSortStrands",
"fixSTAmountCanonicalize",
"fixRmSmallIncreasedQOffers",
};

std::vector<uint256> features;
Expand Down Expand Up @@ -376,6 +377,7 @@ extern uint256 const featureNegativeUNL;
extern uint256 const featureTicketBatch;
extern uint256 const featureFlowSortStrands;
extern uint256 const fixSTAmountCanonicalize;
extern uint256 const fixRmSmallIncreasedQOffers;

} // namespace ripple

Expand Down
7 changes: 7 additions & 0 deletions src/ripple/protocol/Quality.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ class Quality
/** Create a quality from the ratio of two amounts. */
explicit Quality(Amounts const& amount);

/** Create a quality from the ratio of two amounts. */
template <class In, class Out>
Quality(TAmounts<In, Out> const& amount)
: Quality(Amounts(toSTAmount(amount.in), toSTAmount(amount.out)))
{
}

/** Create a quality from the ratio of two amounts. */
template <class In, class Out>
Quality(Out const& out, In const& in)
Expand Down
4 changes: 3 additions & 1 deletion src/ripple/protocol/impl/Feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ detail::supportedAmendments()
"TicketBatch",
"FlowSortStrands",
"fixSTAmountCanonicalize",
"fixRmSmallIncreasedQOffers",
};
return supported;
}
Expand Down Expand Up @@ -190,7 +191,8 @@ uint256 const
featureNegativeUNL = *getRegisteredFeature("NegativeUNL"),
featureTicketBatch = *getRegisteredFeature("TicketBatch"),
featureFlowSortStrands = *getRegisteredFeature("FlowSortStrands"),
fixSTAmountCanonicalize = *getRegisteredFeature("fixSTAmountCanonicalize");
fixSTAmountCanonicalize = *getRegisteredFeature("fixSTAmountCanonicalize"),
fixRmSmallIncreasedQOffers = *getRegisteredFeature("fixRmSmallIncreasedQOffers");

// The following amendments have been active for at least two years. Their
// pre-amendment code has been removed and the identifiers are deprecated.
Expand Down
Loading