From ab4ac64c9299f9293897fde6bed85905eebc5784 Mon Sep 17 00:00:00 2001 From: tequ Date: Wed, 24 Sep 2025 15:05:31 +0900 Subject: [PATCH] test Sponsor Reserve checks for Checks --- src/test/app/Sponsor_test.cpp | 141 +++++++++++++++++++------- src/xrpld/app/tx/detail/CashCheck.cpp | 12 ++- 2 files changed, 109 insertions(+), 44 deletions(-) diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 74d6e772a1..fc797b4637 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -25,6 +25,34 @@ namespace ripple { namespace test { +static STAmount +reserve(jtx::Env& env, std::uint32_t count) +{ + return env.current()->fees().accountReserve(count); +} + +static void +adjustAccountXRPBalance( + jtx::Env& env, + jtx::Account const& account, + STAmount const& balanceTo) +{ + using namespace test::jtx; + XRPL_ASSERT( + isXRP(balanceTo), "adjustAccountXRPBalance: balanceTo must be XRP"); + auto const currentBalance = env.balance(account); + if (currentBalance == balanceTo) + return; + + if (currentBalance > balanceTo) + env(pay(account, env.master, currentBalance - balanceTo + XRP(1)), + fee(XRP(1))); + else + env(pay(env.master, account, balanceTo - currentBalance), fee(XRP(1))); + + env.close(); +} + class Sponsor_test : public beast::unit_test::suite { public: @@ -874,7 +902,6 @@ public: { testcase("Check"); using namespace test::jtx; - Env env{*this, testable_amendments()}; Account const alice("alice"); Account const bob("bob"); Account const gw("gw"); @@ -883,38 +910,10 @@ public: auto const USD = gw["USD"]; - auto const reserve = env.current()->fees().reserve; - auto const increment = env.current()->fees().increment; - - env.fund(XRP(10000), alice, bob, gw, sponsor2); - env.fund(drops(reserve) + drops(increment) - drops(1), sponsor); - env.close(); - - env.trust(USD(100), alice); - env.close(); - env(pay(gw, alice, USD(100))); - env.close(); - { - BEAST_EXPECT( - env.balance(sponsor) < drops(reserve) + drops(increment)); - - // check sponsor balance - env(check::create(alice, bob, XRP(1)), - sponsor::as(sponsor, tfSponsorReserve), - sponsor::sig(sponsor), - ter(tecINSUFFICIENT_RESERVE)); + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, sponsor, sponsor2); env.close(); - } - - env(pay(env.master, sponsor, drops(1))); - env.close(); - - { - BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState - BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); - BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); // CheckCreate -> CheckCancel auto const seq = env.seq(alice); @@ -923,7 +922,7 @@ public: sponsor::sig(sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); @@ -938,7 +937,7 @@ public: sponsor::sig(sponsor2)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); @@ -952,13 +951,17 @@ public: env(check::cancel(alice, keylet.key)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0); } { + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + // CheckCreate -> CheckCash auto const seq2 = env.seq(alice); env(check::create(alice, bob, XRP(1)), @@ -966,7 +969,7 @@ public: sponsor::sig(sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 2); // RippleState + Check + BEAST_EXPECT(ownerCount(env, alice) == 1); // Check BEAST_EXPECT(ownerCount(env, bob) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); @@ -979,18 +982,24 @@ public: sponsor::sig(sponsor)); env.close(); - BEAST_EXPECT(ownerCount(env, alice) == 1); // RippleState + BEAST_EXPECT(ownerCount(env, alice) == 0); BEAST_EXPECT(ownerCount(env, bob) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0); BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 0); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); } - env(pay(env.master, sponsor, drops(env.current()->fees().increment))); - env.close(); - // RippleState sponsor (CheckCashMakesTrustLine) { + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor, sponsor2); + env.close(); + + env.trust(USD(100), alice); + env.close(); + env(pay(gw, alice, USD(100))); + env.close(); + // CheckCreate -> CheckCash auto const seq2 = env.seq(alice); env(check::create(alice, bob, USD(1)), @@ -1020,6 +1029,60 @@ public: BEAST_EXPECT(sponsoredOwnerCount(env, bob) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); } + + { + // check INSUFFICIENT_RESERVE + + { + // CheckCreate + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, sponsor); + env.close(); + + adjustAccountXRPBalance( + env, sponsor, reserve(env, 1) - drops(1)); + + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecINSUFFICIENT_RESERVE)); + env.close(); + + adjustAccountXRPBalance(env, sponsor, reserve(env, 1)); + + env(check::create(alice, bob, XRP(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tesSUCCESS)); + env.close(); + } + + { + // CheckCash (CheckCashMakesTrustLine) + Env env{*this, testable_amendments()}; + env.fund(XRP(10000), alice, bob, gw, sponsor); + env.close(); + + env.trust(USD(100), alice); + env.close(); + env(pay(gw, alice, USD(100))); + env.close(); + + auto const seq = env.seq(alice); + env(check::create(alice, bob, USD(1))); + env.close(); + + adjustAccountXRPBalance( + env, sponsor, reserve(env, 1) - drops(1)); + + auto const keylet = keylet::check(alice, seq); + env(check::cash(bob, keylet.key, USD(1)), + sponsor::as(sponsor, tfSponsorReserve), + sponsor::sig(sponsor), + ter(tecNO_LINE_INSUF_RESERVE)); + env.close(); + } + } } void diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 573d8b97fa..8278b16649 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -272,11 +272,6 @@ CashCheck::doApply() return tecFAILED_PROCESSING; } - auto const sponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); - std::optional> sponsorSle = std::nullopt; - if (sponsorAcc) - sponsorSle = psb.peek(keylet::account(*sponsorAcc)); - // Preclaim already checked that source has at least the requested // funds. // @@ -372,6 +367,11 @@ CashCheck::doApply() auto const sleDst = psb.peek(keylet::account(account_)); + auto const sponsorAcc = getTxReserveSponsorAccountID(ctx_.tx); + std::optional> sponsorSle = std::nullopt; + if (sponsorAcc) + sponsorSle = psb.peek(keylet::account(*sponsorAcc)); + // Can the account cover the trust line's reserve? if (auto const ret = checkInsufficientReserve( psb, sleDst, mPriorBalance, sponsorSle, 1); @@ -515,6 +515,8 @@ CashCheck::doApply() } // If we succeeded, update the check owner's reserve. + + auto const sponsorSle = getLedgerEntryReserveSponsor(psb, sleCheck); adjustOwnerCount( psb, psb.peek(keylet::account(srcId)), sponsorSle, -1, viewJ);