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 all 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
130 changes: 130 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,73 @@ 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>,
"STAmount is not supported");

static_assert(
std::is_same_v<TTakerGets, IOUAmount> ||
std::is_same_v<TTakerGets, XRPAmount>,
"STAmount is not supported");

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:
// o `TakerPays` is XRP (because of XRP drops granularity) or
// o `TakerPays` and `TakerGets` are both IOU and `TakerPays`<`TakerGets`
constexpr bool const inIsXRP = std::is_same_v<TTakerPays, XRPAmount>;
constexpr bool const outIsXRP = std::is_same_v<TTakerGets, XRPAmount>;

if constexpr (outIsXRP)
{
// If `TakerGets` is XRP, the worst this offer's quality can change is
// to 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;
}();

if (effectiveAmounts.in > TTakerPays::minPositiveAmount())
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 +290,68 @@ TOfferStreamBase<TIn, TOut>::step()
<< "Removing became unfunded offer " << entry->key();
}
offer_ = TOffer<TIn, TOut>{};
// See comment at top of loop for how the offer is removed
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, hence the `if constexpr` guard.
// Note that TIn can be XRPAmount or STAmount, and TOut can be
// IOUAmount or STAmount.
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` rationale
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` rationale
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 tiny offer that became tiny due "
"to reduced quality "
<< entry->key();
}
offer_ = TOffer<TIn, TOut>{};
// See comment at top of loop for how the offer is removed
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
28 changes: 23 additions & 5 deletions src/ripple/protocol/AmountConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,7 @@ toSTAmount(XRPAmount const& xrp, Issue const& iss)

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

template <>
inline STAmount
Expand Down Expand Up @@ -102,6 +98,28 @@ toAmount<XRPAmount>(STAmount const& amt)
return XRPAmount(sMant);
}

template <class T>
T
toAmount(IOUAmount const& amt) = delete;

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

template <class T>
T
toAmount(XRPAmount const& amt) = delete;

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