Skip to content

Commit

Permalink
[FOLD] Handle IOU/IOU offers
Browse files Browse the repository at this point in the history
  • Loading branch information
seelabs committed Apr 21, 2021
1 parent 7474063 commit 441776a
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 136 deletions.
187 changes: 111 additions & 76 deletions src/ripple/app/tx/impl/OfferStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,68 @@ 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>,
"");

static_assert(
std::is_same_v<TTakerGets, IOUAmount> ||
std::is_same_v<TTakerGets, XRPAmount>,
"");

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`
constexpr bool const inIsXRP = std::is_same_v<TTakerPays, XRPAmount>;
constexpr bool const outIsXRP = std::is_same_v<TTakerGets, XRPAmount>;

if (outIsXRP)
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)
return false;

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

template <class TIn, class TOut>
bool
TOfferStreamBase<TIn, TOut>::step()
Expand Down Expand Up @@ -223,88 +285,61 @@ 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
continue;
}

bool const inIsXRP = isXRP(offer_.issueIn());
bool const outIsXRP = isXRP(offer_.issueOut());
if (view_.rules().enabled(fixRmSmallIncreasedQOffers) &&
(inIsXRP || outIsXRP))
bool const rmSmallIncreasedQOffer = [&] {
bool const inIsXRP = isXRP(offer_.issueIn());
bool const outIsXRP = isXRP(offer_.issueOut());
if (inIsXRP && !outIsXRP)
{
if constexpr (!(std::is_same_v<TIn, IOUAmount> ||
std::is_same_v<TOut, XRPAmount>))
return shouldRmSmallIncreasedQOffer<XRPAmount, IOUAmount>();
}
if (!inIsXRP && outIsXRP)
{
if constexpr (!(std::is_same_v<TIn, XRPAmount> ||
std::is_same_v<TOut, IOUAmount>))
return shouldRmSmallIncreasedQOffer<IOUAmount, XRPAmount>();
}
if (!inIsXRP && !outIsXRP)
{
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)
{
// If an offer is small (defined by the number of drops available),
// and the effective quality is different from its initial quality,
// remove it. Such offers may block order books.
auto const effectiveAmounts = [&] {
if (offer_.owner() != offer_.issueOut().account &&
*ownerFunds_ < offer_.amount().out)
{
// adjust the amounts by owner funds
return offer_.quality().ceil_out(
offer_.amount(), *ownerFunds_);
}
return offer_.amount();
}();

XRPAmount const thresholdToRm{1};

XRPAmount const xrpSide = [&]() -> XRPAmount {
if constexpr (std::is_same_v<TIn, XRPAmount>)
{
assert(inIsXRP);
return effectiveAmounts.in;
}
if constexpr (std::is_same_v<TOut, XRPAmount>)
{
assert(outIsXRP);
return effectiveAmounts.out;
}
if constexpr (std::is_same_v<TIn, STAmount>)
{
if (inIsXRP)
return effectiveAmounts.in.xrp();
}
if constexpr (std::is_same_v<TOut, STAmount>)
{
if (outIsXRP)
return effectiveAmounts.out.xrp();
}
assert(0);
// Should never happen, but if it does, keep the offer (return
// an amount above the threshold)
return thresholdToRm + XRPAmount{1};
}();

if (xrpSide <= thresholdToRm)
auto const original_funds = accountFundsHelper(
cancelView_,
offer_.owner(),
amount.out,
offer_.issueOut(),
fhZERO_IF_FROZEN,
j_);

if (original_funds == *ownerFunds_)
{
Quality const effectiveQuality{effectiveAmounts};

if (effectiveQuality < offer_.quality())
{
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 "
"reduced quality "
<< entry->key();
}
offer_ = TOffer<TIn, TOut>{};
continue;
}
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 "
"reduced quality "
<< entry->key();
}
offer_ = TOffer<TIn, TOut>{};
// See comment at top of loop for why the offer is removed
continue;
}

break;
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();
};

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

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
Loading

0 comments on commit 441776a

Please sign in to comment.