From 65768237cddf8f3aa6365a0d75767e0e4996773a Mon Sep 17 00:00:00 2001 From: tequ Date: Fri, 30 Jan 2026 11:36:56 +0900 Subject: [PATCH] Payback ReserveCount --- src/libxrpl/ledger/View.cpp | 7 ++- src/test/app/Sponsor_test.cpp | 43 ++++++++++++------- .../app/tx/detail/SponsorshipTransfer.cpp | 26 ++++++++--- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index e95b793d22..2e175d5252 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -1167,16 +1167,19 @@ adjustOwnerCount( auto sponsorObjSle = view.peek(keylet::sponsor(sponsorAcc, account)); - if (sponsorObjSle && amount > 0) + if (sponsorObjSle) { // pre funded // update the pre-funded ReserveCount on Sponsorship ledger object XRPL_ASSERT(sponsorObjSle, "xrpl::adjustOwnerCount : co-signing sponsor exists"); auto const currentReserveCount = sponsorObjSle->getFieldU32(sfReserveCount); - XRPL_ASSERT(currentReserveCount >= amount, "xrpl::adjustOwnerCount : enough reserve count"); + if (amount > 0) + XRPL_ASSERT(currentReserveCount >= amount, "xrpl::adjustOwnerCount : enough reserve count"); if (currentReserveCount - amount > 0) + // if amount > 0, reduce the reserve count + // if amount < 0, payback the reserve count sponsorObjSle->setFieldU32(sfReserveCount, currentReserveCount - amount); else sponsorObjSle->makeFieldAbsent(sfReserveCount); diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 24360b3af3..0d120d698a 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -445,17 +445,26 @@ public: env(sponsor::set(sponsor, 0, 100, XRP(100), XRP(1)), sponsor::sponseeAcc(alice), ter(tesSUCCESS)); env.close(); - env(ticket::create(alice, 1), + env(did::set(alice), + did::uri("uri"), sponsor::as(sponsor, tfSponsorReserve | tfSponsorFee), sig(sfSponsorSignature, sponsor), fee(XRP(1)), ter(tesSUCCESS)); env.close(); - auto const sle = env.le(keylet::sponsor(sponsor, alice)); + auto sle = env.le(keylet::sponsor(sponsor, alice)); BEAST_EXPECT(sle); BEAST_EXPECT(sle->at(sfReserveCount) == 99); BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); + + env(did::del(alice), ter(tesSUCCESS)); + env.close(); + + sle = env.le(keylet::sponsor(sponsor, alice)); + BEAST_EXPECT(sle); + BEAST_EXPECT(sle->at(sfReserveCount) == 100); // paybacked + BEAST_EXPECT(sle->at(sfFeeAmount) == XRP(99)); } { @@ -794,11 +803,11 @@ public: BEAST_EXPECT(sponsoringOwnerCount(env, sponsor1) == 1); BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); - auto const sle1 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(sle1->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(sle1->getAccountID(sfSponsorAccount) == sponsor1.id()); - auto const sponsorSle = env.le(keylet::sponsor(sponsor1, alice)); - BEAST_EXPECT(sponsorSle->getFieldU32(sfReserveCount) == 99); + auto checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(checkSle->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(checkSle->getAccountID(sfSponsorAccount) == sponsor1.id()); + auto sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice)); + BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 99); // transfer sponsor env(sponsor::set_reserve(sponsor2, 0, 100), sponsor::sponseeAcc(alice)); @@ -816,11 +825,13 @@ public: BEAST_EXPECT(sponsoringAccountCount(env, alice) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); - auto const sle2 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(sle2->isFieldPresent(sfSponsorAccount)); - BEAST_EXPECT(sle2->getAccountID(sfSponsorAccount) == sponsor2.id()); - auto const sponsorSle2 = env.le(keylet::sponsor(sponsor2, alice)); - BEAST_EXPECT(sponsorSle2->getFieldU32(sfReserveCount) == 99); + checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(checkSle->isFieldPresent(sfSponsorAccount)); + BEAST_EXPECT(checkSle->getAccountID(sfSponsorAccount) == sponsor2.id()); + sponsor1Sle = env.le(keylet::sponsor(sponsor1, alice)); + BEAST_EXPECT(sponsor1Sle->getFieldU32(sfReserveCount) == 100); // paybacked + auto sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); + BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 99); // dissolve sponsor adjustAccountXRPBalance(env, alice, reserve(env, 1)); @@ -837,10 +848,10 @@ public: BEAST_EXPECT(sponsoringAccountCount(env, sponsor1) == 0); BEAST_EXPECT(sponsoringAccountCount(env, sponsor2) == 0); BEAST_EXPECT(!env.le(keylet::account(sponsor2))->isFieldPresent(sfSponsoringOwnerCount)); - auto const sle3 = env.le(keylet::unchecked(checkId)); - BEAST_EXPECT(!sle3->isFieldPresent(sfSponsorAccount)); - auto const sponsorSle3 = env.le(keylet::sponsor(sponsor2, alice)); - BEAST_EXPECT(sponsorSle3->getFieldU32(sfReserveCount) == 99); // no change + checkSle = env.le(keylet::unchecked(checkId)); + BEAST_EXPECT(!checkSle->isFieldPresent(sfSponsorAccount)); + sponsor2Sle = env.le(keylet::sponsor(sponsor2, alice)); + BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 100); // paybacked } { diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index 681dddb8ec..35d4eb71d7 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -241,8 +241,6 @@ adjustReserveCount(ApplyView& view, AccountID const& account, AccountID const& s { if (delta == 0) return tesSUCCESS; - if (delta > 0) - return tefINTERNAL; // LCOV_EXCL_LINE auto const sponsorKeylet = keylet::sponsor(sponsor, account); auto const sponsorSle = view.peek(sponsorKeylet); if (!sponsorSle) @@ -337,21 +335,35 @@ SponsorshipTransfer::doApply() !isTesSuccess(ter)) return ter; } + + // payback the reserve count if ltSponsorship exists + if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); sponsorSle) + if (auto const ter = adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); + !isTesSuccess(ter)) + return ter; } else { // dissolve object sponsor auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); + auto const oldSponsorSle = view().peek(keylet::account(oldSponsor)); + if (!oldSponsorSle) + return tefINTERNAL; // LCOV_EXCL_LINE + // decrement sponsored count setSponsorFieldU32(accSle, sfSponsoredOwnerCount, -ownerCountDelta); view().update(accSle); + // decrement old sponsoring count - if (auto const oldSponsorSle = view().peek(keylet::account(oldSponsor))) - { - setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); - view().update(oldSponsorSle); - } + setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta); + view().update(oldSponsorSle); + + // payback the reserve count if ltSponsorship exists + if (auto const sponsorSle = view().exists(keylet::sponsor(oldSponsor, account_)); sponsorSle) + if (auto const ter = adjustReserveCount(view(), account_, oldSponsor, ownerCountDelta); + !isTesSuccess(ter)) + return ter; // remove sponsor from object objSle->makeFieldAbsent(sfSponsorAccount);