mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
[wip] added test for edge case balance
This commit is contained in:
@@ -2069,8 +2069,18 @@ rippleCreditIOU(
|
||||
&& !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)))
|
||||
// Receiver reserve is not set.
|
||||
{
|
||||
adjustOwnerCount(
|
||||
view, view.peek(keylet::account(uReceiverID)), 1, j);
|
||||
auto const sleReceiver = view.peek(keylet::account(uReceiverID));
|
||||
auto const ownerCount = sleReceiver->getFieldU32(sfOwnerCount);
|
||||
|
||||
// Trust lines use "owner count < 2" reserve exception
|
||||
XRPAmount const reserve = (ownerCount < 2)
|
||||
? XRPAmount(beast::zero)
|
||||
: view.fees().accountReserve(ownerCount + 1);
|
||||
|
||||
if (sleReceiver->getFieldAmount(sfBalance) < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
adjustOwnerCount(view, sleReceiver, 1, j);
|
||||
|
||||
sleRippleState->setFieldU32(
|
||||
sfFlags,
|
||||
|
||||
@@ -453,7 +453,6 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
// Helper lambda to check alice's owner count and reserve flag
|
||||
auto checkAlice = [&](std::uint32_t expectedOwnerCount,
|
||||
bool expectedReserveSet) {
|
||||
BEAST_EXPECT(
|
||||
@@ -468,24 +467,20 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
env.fund(XRP(10000), gw, alice, market);
|
||||
env.close();
|
||||
|
||||
// create trust lines
|
||||
env(trust(alice, USD(1000)));
|
||||
env(trust(market, USD(1000)));
|
||||
env.close();
|
||||
checkAlice(1, true);
|
||||
|
||||
// gw issues USD to alice and market
|
||||
env(pay(gw, alice, USD(100)));
|
||||
env(pay(gw, market, USD(1000)));
|
||||
env.close();
|
||||
checkAlice(1, true);
|
||||
|
||||
// gw clears asfDefaultRipple
|
||||
env(fclear(gw, asfDefaultRipple));
|
||||
env.close();
|
||||
|
||||
// alice clears trustline limit. This should trigger the check for
|
||||
// default ripple state and charge gw for non-default state
|
||||
// alice clears trustline limit, triggering default ripple state check
|
||||
env(trust(alice, USD(0)));
|
||||
env.close();
|
||||
checkAlice(1, true);
|
||||
@@ -495,13 +490,11 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
checkAlice(0, false);
|
||||
|
||||
// market offers USD for XRP
|
||||
env(offer(market, XRP(100), USD(100)));
|
||||
env.close();
|
||||
// Now alice acquires balance again by placing an offer (direct payment
|
||||
// would fail because alice set limit to 0). This offer will cross
|
||||
// market's. Balance goes from 0 to positive - this triggers the new
|
||||
// increment logic
|
||||
|
||||
// alice acquires balance via offer crossing (direct payment would fail
|
||||
// because limit is 0). Balance 0 -> positive triggers increment logic
|
||||
env(offer(alice, USD(50), XRP(50)));
|
||||
env.close();
|
||||
|
||||
@@ -512,31 +505,128 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
checkAlice(0, false);
|
||||
}
|
||||
|
||||
void
|
||||
testInsufficientReserveOnOwnerCountChange(FeatureBitset features)
|
||||
{
|
||||
testcase("Insufficient Reserve on Owner Count Change");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this, features};
|
||||
Account gw{"gateway"};
|
||||
Account alice{"alice"};
|
||||
Account market{"market"};
|
||||
bool const aliceHigh = alice.id() > gw.id();
|
||||
|
||||
auto const USD = gw["USD"];
|
||||
auto const fee = env.current()->fees().base;
|
||||
auto const acctReserve = env.current()->fees().accountReserve(0);
|
||||
auto const incReserve = env.current()->fees().increment;
|
||||
|
||||
auto checkAliceUSD = [&](std::uint32_t expectedOwnerCount,
|
||||
bool expectedReserveSet) {
|
||||
BEAST_EXPECT(
|
||||
env.le(alice)->getFieldU32(sfOwnerCount) == expectedOwnerCount);
|
||||
auto const line =
|
||||
env.le(keylet::line(alice, gw, to_currency("USD")));
|
||||
BEAST_EXPECT(
|
||||
line->isFlag(aliceHigh ? lsfHighReserve : lsfLowReserve) ==
|
||||
expectedReserveSet);
|
||||
};
|
||||
|
||||
env.fund(XRP(10000), gw, market);
|
||||
// Fund alice with enough for reserve + increment + 50 XRP + fees
|
||||
// alice performs 6 transactions: 2x trust, 2x pay, 1x pay excess, 1x
|
||||
// offer
|
||||
env.fund(acctReserve + incReserve + XRP(50) + fee * 6, alice);
|
||||
env.close();
|
||||
|
||||
// Create trust lines - alice needs owner count >= 2 for reserve check
|
||||
env(trust(alice, USD(1000)));
|
||||
env(trust(market, USD(1000)));
|
||||
env.close();
|
||||
checkAliceUSD(1, true);
|
||||
|
||||
env(pay(gw, alice, USD(100)));
|
||||
env(pay(gw, market, USD(1000)));
|
||||
env.close();
|
||||
|
||||
env(fclear(gw, asfDefaultRipple));
|
||||
env.close();
|
||||
|
||||
env(trust(alice, USD(0)));
|
||||
env.close();
|
||||
checkAliceUSD(1, true);
|
||||
|
||||
// alice clears the USD balance causing decrement in owners count
|
||||
env(pay(alice, gw, USD(100)));
|
||||
env.close();
|
||||
checkAliceUSD(0, false);
|
||||
|
||||
// Reduce alice's balance to 50 XRP + fee
|
||||
// Owner count is 0
|
||||
auto aliceBalance = env.balance(alice);
|
||||
auto targetBalance = acctReserve + XRP(50) + fee;
|
||||
if (aliceBalance > targetBalance)
|
||||
{
|
||||
auto excess = aliceBalance - targetBalance - fee;
|
||||
if (excess > drops(0))
|
||||
env(pay(alice, gw, excess));
|
||||
env.close();
|
||||
}
|
||||
|
||||
aliceBalance = env.balance(alice);
|
||||
BEAST_EXPECT(aliceBalance == acctReserve + XRP(50) + fee);
|
||||
|
||||
env(offer(market, XRP(100), USD(100)));
|
||||
env.close();
|
||||
|
||||
// alice's offer would cross, causing USD balance 0 ->
|
||||
// positive. With fix: owner count increment requires reserve, so fails.
|
||||
// Without fix: no owner count change, so succeeds.
|
||||
if (features[fixTrustLineOwnerCount])
|
||||
{
|
||||
env(offer(alice, USD(50), XRP(50)));
|
||||
env.close();
|
||||
|
||||
env.require(balance(alice, USD(0)), offers(alice, 1));
|
||||
checkAliceUSD(1, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
env(offer(alice, USD(50), XRP(50)));
|
||||
env.close();
|
||||
|
||||
env.require(balance(alice, USD(50)), offers(alice, 0));
|
||||
checkAliceUSD(0, false);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testTrustNonexistent();
|
||||
testCreditLimit();
|
||||
// testTrustNonexistent();
|
||||
// testCreditLimit();
|
||||
|
||||
auto testWithFeatures = [this](FeatureBitset features) {
|
||||
testPayNonexistent(features);
|
||||
testDirectRipple(features);
|
||||
testWithTransferFee(false, false, features);
|
||||
testWithTransferFee(false, true, features);
|
||||
testWithTransferFee(true, false, features);
|
||||
testWithTransferFee(true, true, features);
|
||||
testWithPath(features);
|
||||
testIndirect(features);
|
||||
testIndirectMultiPath(true, features);
|
||||
testIndirectMultiPath(false, features);
|
||||
testInvoiceID(features);
|
||||
testOwnerCountOnBalanceChange(features);
|
||||
// testPayNonexistent(features);
|
||||
// testDirectRipple(features);
|
||||
// testWithTransferFee(false, false, features);
|
||||
// testWithTransferFee(false, true, features);
|
||||
// testWithTransferFee(true, false, features);
|
||||
// testWithTransferFee(true, true, features);
|
||||
// testWithPath(features);
|
||||
// testIndirect(features);
|
||||
// testIndirectMultiPath(true, features);
|
||||
// testIndirectMultiPath(false, features);
|
||||
// testInvoiceID(features);
|
||||
// testOwnerCountOnBalanceChange(features);
|
||||
testInsufficientReserveOnOwnerCountChange(features);
|
||||
};
|
||||
|
||||
using namespace test::jtx;
|
||||
auto const sa = testable_amendments();
|
||||
testWithFeatures(sa - featurePermissionedDEX);
|
||||
// testWithFeatures(sa - featurePermissionedDEX);
|
||||
testWithFeatures(sa - fixTrustLineOwnerCount);
|
||||
testWithFeatures(sa);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user