mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-18 18:15:50 +00:00
test: Unit test for AMM offer overflow (#4986)
This commit is contained in:
@@ -4889,6 +4889,280 @@ private:
|
||||
false);
|
||||
}
|
||||
|
||||
void
|
||||
testFixOverflowOffer()
|
||||
{
|
||||
using namespace jtx;
|
||||
using namespace std::chrono;
|
||||
FeatureBitset const all{supported_amendments()};
|
||||
|
||||
Account const gatehub{"gatehub"};
|
||||
Account const bitstamp{"bitstamp"};
|
||||
Account const trader{"trader"};
|
||||
auto const usdGH = gatehub["USD"];
|
||||
auto const btcGH = gatehub["BTC"];
|
||||
auto const usdBIT = bitstamp["USD"];
|
||||
|
||||
struct InputSet
|
||||
{
|
||||
char const* testCase;
|
||||
double const poolUsdBIT;
|
||||
double const poolUsdGH;
|
||||
sendmax const sendMaxUsdBIT;
|
||||
STAmount const sendUsdGH;
|
||||
STAmount const failUsdGH;
|
||||
STAmount const failUsdBIT;
|
||||
STAmount const goodUsdGH;
|
||||
STAmount const goodUsdBIT;
|
||||
IOUAmount const lpTokenBalance;
|
||||
double const offer1BtcGH = 0.1;
|
||||
double const offer2BtcGH = 0.1;
|
||||
double const offer2UsdGH = 1;
|
||||
double const rateBIT = 0.0;
|
||||
double const rateGH = 0.0;
|
||||
};
|
||||
|
||||
using uint64_t = std::uint64_t;
|
||||
|
||||
for (auto const& input : {
|
||||
InputSet{
|
||||
.testCase = "Test Fix Overflow Offer", //
|
||||
.poolUsdBIT = 3, //
|
||||
.poolUsdGH = 273, //
|
||||
.sendMaxUsdBIT{usdBIT(50)}, //
|
||||
.sendUsdGH{usdGH, uint64_t(272'455089820359), -12}, //
|
||||
.failUsdGH = STAmount{0}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(46'47826086956522), -14}, //
|
||||
.goodUsdGH{usdGH, uint64_t(96'7543114220382), -13}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(8'464739069120721), -15}, //
|
||||
.lpTokenBalance = {28'61817604250837, -14}, //
|
||||
.offer1BtcGH = 0.1, //
|
||||
.offer2BtcGH = 0.1, //
|
||||
.offer2UsdGH = 1, //
|
||||
.rateBIT = 1.15, //
|
||||
.rateGH = 1.2, //
|
||||
},
|
||||
InputSet{
|
||||
.testCase = "Overflow test {1, 100, 0.111}", //
|
||||
.poolUsdBIT = 1, //
|
||||
.poolUsdGH = 100, //
|
||||
.sendMaxUsdBIT{usdBIT(0.111)}, //
|
||||
.sendUsdGH{usdGH, 100}, //
|
||||
.failUsdGH = STAmount{0}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(1'111), -3}, //
|
||||
.goodUsdGH{usdGH, uint64_t(90'04347888284115), -14}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(1'111), -3}, //
|
||||
.lpTokenBalance{10, 0}, //
|
||||
.offer1BtcGH = 1e-5, //
|
||||
.offer2BtcGH = 1, //
|
||||
.offer2UsdGH = 1e-5, //
|
||||
.rateBIT = 0, //
|
||||
.rateGH = 0, //
|
||||
},
|
||||
InputSet{
|
||||
.testCase = "Overflow test {1, 100, 1.00}", //
|
||||
.poolUsdBIT = 1, //
|
||||
.poolUsdGH = 100, //
|
||||
.sendMaxUsdBIT{usdBIT(1.00)}, //
|
||||
.sendUsdGH{usdGH, 100}, //
|
||||
.failUsdGH = STAmount{0}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(2), 0}, //
|
||||
.goodUsdGH{usdGH, uint64_t(52'94379354424079), -14}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(2), 0}, //
|
||||
.lpTokenBalance{10, 0}, //
|
||||
.offer1BtcGH = 1e-5, //
|
||||
.offer2BtcGH = 1, //
|
||||
.offer2UsdGH = 1e-5, //
|
||||
.rateBIT = 0, //
|
||||
.rateGH = 0, //
|
||||
},
|
||||
InputSet{
|
||||
.testCase = "Overflow test {1, 100, 4.6432}", //
|
||||
.poolUsdBIT = 1, //
|
||||
.poolUsdGH = 100, //
|
||||
.sendMaxUsdBIT{usdBIT(4.6432)}, //
|
||||
.sendUsdGH{usdGH, 100}, //
|
||||
.failUsdGH = STAmount{0}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(5'6432), -4}, //
|
||||
.goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15}, //
|
||||
.lpTokenBalance{10, 0}, //
|
||||
.offer1BtcGH = 1e-5, //
|
||||
.offer2BtcGH = 1, //
|
||||
.offer2UsdGH = 1e-5, //
|
||||
.rateBIT = 0, //
|
||||
.rateGH = 0, //
|
||||
},
|
||||
InputSet{
|
||||
.testCase = "Overflow test {1, 100, 10}", //
|
||||
.poolUsdBIT = 1, //
|
||||
.poolUsdGH = 100, //
|
||||
.sendMaxUsdBIT{usdBIT(10)}, //
|
||||
.sendUsdGH{usdGH, 100}, //
|
||||
.failUsdGH = STAmount{0}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(11), 0}, //
|
||||
.goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15}, //
|
||||
.lpTokenBalance{10, 0}, //
|
||||
.offer1BtcGH = 1e-5, //
|
||||
.offer2BtcGH = 1, //
|
||||
.offer2UsdGH = 1e-5, //
|
||||
.rateBIT = 0, //
|
||||
.rateGH = 0, //
|
||||
},
|
||||
InputSet{
|
||||
.testCase = "Overflow test {50, 100, 5.55}", //
|
||||
.poolUsdBIT = 50, //
|
||||
.poolUsdGH = 100, //
|
||||
.sendMaxUsdBIT{usdBIT(5.55)}, //
|
||||
.sendUsdGH{usdGH, 100}, //
|
||||
.failUsdGH = STAmount{0}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(55'55), -2}, //
|
||||
.goodUsdGH{usdGH, uint64_t(90'04347888284113), -14}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(55'55), -2}, //
|
||||
.lpTokenBalance{uint64_t(70'71067811865475), -14}, //
|
||||
.offer1BtcGH = 1e-5, //
|
||||
.offer2BtcGH = 1, //
|
||||
.offer2UsdGH = 1e-5, //
|
||||
.rateBIT = 0, //
|
||||
.rateGH = 0, //
|
||||
},
|
||||
InputSet{
|
||||
.testCase = "Overflow test {50, 100, 50.00}", //
|
||||
.poolUsdBIT = 50, //
|
||||
.poolUsdGH = 100, //
|
||||
.sendMaxUsdBIT{usdBIT(50.00)}, //
|
||||
.sendUsdGH{usdGH, 100}, //
|
||||
.failUsdGH{usdGH, uint64_t(52'94379354424081), -14}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(100), 0}, //
|
||||
.goodUsdGH{usdGH, uint64_t(52'94379354424081), -14}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(100), 0}, //
|
||||
.lpTokenBalance{uint64_t(70'71067811865475), -14}, //
|
||||
.offer1BtcGH = 1e-5, //
|
||||
.offer2BtcGH = 1, //
|
||||
.offer2UsdGH = 1e-5, //
|
||||
.rateBIT = 0, //
|
||||
.rateGH = 0, //
|
||||
},
|
||||
InputSet{
|
||||
.testCase = "Overflow test {50, 100, 232.16}", //
|
||||
.poolUsdBIT = 50, //
|
||||
.poolUsdGH = 100, //
|
||||
.sendMaxUsdBIT{usdBIT(232.16)}, //
|
||||
.sendUsdGH{usdGH, 100}, //
|
||||
.failUsdGH = STAmount{0}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(282'16), -2}, //
|
||||
.goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(141'0789844851958), -13}, //
|
||||
.lpTokenBalance{70'71067811865475, -14}, //
|
||||
.offer1BtcGH = 1e-5, //
|
||||
.offer2BtcGH = 1, //
|
||||
.offer2UsdGH = 1e-5, //
|
||||
.rateBIT = 0, //
|
||||
.rateGH = 0, //
|
||||
},
|
||||
InputSet{
|
||||
.testCase = "Overflow test {50, 100, 500}", //
|
||||
.poolUsdBIT = 50, //
|
||||
.poolUsdGH = 100, //
|
||||
.sendMaxUsdBIT{usdBIT(500)}, //
|
||||
.sendUsdGH{usdGH, 100}, //
|
||||
.failUsdGH = STAmount{0}, //
|
||||
.failUsdBIT{usdBIT, uint64_t(550), 0}, //
|
||||
.goodUsdGH{usdGH, uint64_t(35'44113971506987), -14}, //
|
||||
.goodUsdBIT{usdBIT, uint64_t(141'0789844851958), -13}, //
|
||||
.lpTokenBalance{70'71067811865475, -14}, //
|
||||
.offer1BtcGH = 1e-5, //
|
||||
.offer2BtcGH = 1, //
|
||||
.offer2UsdGH = 1e-5, //
|
||||
.rateBIT = 0, //
|
||||
.rateGH = 0, //
|
||||
},
|
||||
})
|
||||
{
|
||||
testcase(input.testCase);
|
||||
for (auto const& features :
|
||||
{all - fixAMMOverflowOffer, all | fixAMMOverflowOffer})
|
||||
{
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(5'000), gatehub, bitstamp, trader);
|
||||
env.close();
|
||||
|
||||
if (input.rateGH != 0.0)
|
||||
env(rate(gatehub, input.rateGH));
|
||||
if (input.rateBIT != 0.0)
|
||||
env(rate(bitstamp, input.rateBIT));
|
||||
|
||||
env(trust(trader, usdGH(10'000'000)));
|
||||
env(trust(trader, usdBIT(10'000'000)));
|
||||
env(trust(trader, btcGH(10'000'000)));
|
||||
env.close();
|
||||
|
||||
env(pay(gatehub, trader, usdGH(100'000)));
|
||||
env(pay(gatehub, trader, btcGH(100'000)));
|
||||
env(pay(bitstamp, trader, usdBIT(100'000)));
|
||||
env.close();
|
||||
|
||||
AMM amm{
|
||||
env,
|
||||
trader,
|
||||
usdGH(input.poolUsdGH),
|
||||
usdBIT(input.poolUsdBIT)};
|
||||
env.close();
|
||||
|
||||
IOUAmount const preSwapLPTokenBalance =
|
||||
amm.getLPTokensBalance();
|
||||
|
||||
env(offer(trader, usdBIT(1), btcGH(input.offer1BtcGH)));
|
||||
env(offer(
|
||||
trader,
|
||||
btcGH(input.offer2BtcGH),
|
||||
usdGH(input.offer2UsdGH)));
|
||||
env.close();
|
||||
|
||||
env(pay(trader, trader, input.sendUsdGH),
|
||||
path(~usdGH),
|
||||
path(~btcGH, ~usdGH),
|
||||
sendmax(input.sendMaxUsdBIT),
|
||||
txflags(tfPartialPayment));
|
||||
env.close();
|
||||
if (!features[fixAMMOverflowOffer])
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
input.failUsdGH,
|
||||
input.failUsdBIT,
|
||||
input.lpTokenBalance));
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
input.goodUsdGH,
|
||||
input.goodUsdBIT,
|
||||
input.lpTokenBalance));
|
||||
|
||||
// Invariant: LPToken balance must not change in a payment
|
||||
// or a swap transaction
|
||||
BEAST_EXPECT(
|
||||
amm.getLPTokensBalance() == preSwapLPTokenBalance);
|
||||
|
||||
// Invariant: The square root of (product of the pool
|
||||
// balances) must be at least the LPTokenBalance
|
||||
Number const sqrtPoolProduct =
|
||||
root2(input.goodUsdGH * input.goodUsdBIT);
|
||||
|
||||
// Include a tiny tolerance for the test cases using
|
||||
// .goodUsdGH{usdGH, uint64_t(35'44113971506987), -14},
|
||||
// .goodUsdBIT{usdBIT, uint64_t(2'821579689703915), -15},
|
||||
// These two values multiply
|
||||
// to 99.99999999999994227040383754105 which gets internally
|
||||
// rounded to 100, due to representation error.
|
||||
BEAST_EXPECT(
|
||||
(sqrtPoolProduct + Number{1, -14} >=
|
||||
input.lpTokenBalance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCore()
|
||||
{
|
||||
@@ -4916,6 +5190,7 @@ private:
|
||||
testAMMID();
|
||||
testSelection();
|
||||
testFixDefaultInnerObj();
|
||||
testFixOverflowOffer();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user