mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
fixReducedOffersV2: prevent offers from blocking order books: (#5032)
Fixes issue #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 #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.
This commit is contained in:
@@ -506,7 +506,6 @@ struct Flow_test : public beast::unit_test::suite
|
||||
// Without limits, the 0.4 USD would produce 1000 EUR in the forward
|
||||
// pass. This test checks that the payment produces 1 EUR, as
|
||||
// expected.
|
||||
|
||||
Env env(*this, features);
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
@@ -515,17 +514,52 @@ struct Flow_test : public beast::unit_test::suite
|
||||
env(pay(gw, alice, USD(1000)));
|
||||
env(pay(gw, bob, EUR(1000)));
|
||||
|
||||
Keylet const bobUsdOffer = keylet::offer(bob, env.seq(bob));
|
||||
env(offer(bob, USD(1), drops(2)), txflags(tfPassive));
|
||||
env(offer(bob, drops(1), EUR(1000)), txflags(tfPassive));
|
||||
|
||||
bool const reducedOffersV2 = features[fixReducedOffersV2];
|
||||
|
||||
// With reducedOffersV2, it is not allowed to accept less than
|
||||
// USD(0.5) of bob's USD offer. If we provide 1 drop for less
|
||||
// than USD(0.5), then the remaining fractional offer would
|
||||
// block the order book.
|
||||
TER const expectedTER =
|
||||
reducedOffersV2 ? TER(tecPATH_DRY) : TER(tesSUCCESS);
|
||||
env(pay(alice, carol, EUR(1)),
|
||||
path(~XRP, ~EUR),
|
||||
sendmax(USD(0.4)),
|
||||
txflags(tfNoRippleDirect | tfPartialPayment));
|
||||
txflags(tfNoRippleDirect | tfPartialPayment),
|
||||
ter(expectedTER));
|
||||
|
||||
env.require(balance(carol, EUR(1)));
|
||||
env.require(balance(bob, USD(0.4)));
|
||||
env.require(balance(bob, EUR(999)));
|
||||
if (!reducedOffersV2)
|
||||
{
|
||||
env.require(balance(carol, EUR(1)));
|
||||
env.require(balance(bob, USD(0.4)));
|
||||
env.require(balance(bob, EUR(999)));
|
||||
|
||||
// Show that bob's USD offer is now a blocker.
|
||||
std::shared_ptr<SLE const> const usdOffer = env.le(bobUsdOffer);
|
||||
if (BEAST_EXPECT(usdOffer))
|
||||
{
|
||||
std::uint64_t const bookRate = [&usdOffer]() {
|
||||
// Extract the least significant 64 bits from the
|
||||
// book page. That's where the quality is stored.
|
||||
std::string bookDirStr =
|
||||
to_string(usdOffer->at(sfBookDirectory));
|
||||
bookDirStr.erase(0, 48);
|
||||
return std::stoull(bookDirStr, nullptr, 16);
|
||||
}();
|
||||
std::uint64_t const actualRate = getRate(
|
||||
usdOffer->at(sfTakerGets), usdOffer->at(sfTakerPays));
|
||||
|
||||
// We expect the actual rate of the offer to be worse
|
||||
// (larger) than the rate of the book page holding the
|
||||
// offer. This is a defect which is corrected by
|
||||
// fixReducedOffersV2.
|
||||
BEAST_EXPECT(actualRate > bookRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1375,9 +1409,11 @@ struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
using namespace jtx;
|
||||
FeatureBitset const ownerPaysFee{featureOwnerPaysFee};
|
||||
FeatureBitset const reducedOffersV2(fixReducedOffersV2);
|
||||
|
||||
testLineQuality(features);
|
||||
testFalseDry(features);
|
||||
testBookStep(features - reducedOffersV2);
|
||||
testDirectStep(features);
|
||||
testBookStep(features);
|
||||
testDirectStep(features | ownerPaysFee);
|
||||
|
||||
Reference in New Issue
Block a user