mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 14:20:56 +00:00
fix: Enable reserve checking on ending sponsorship (#8044)
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
@@ -412,9 +413,24 @@ SponsorshipTransfer::doApply()
|
||||
if (!oldSponsorSle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// The owner reclaims the reserve burden when the object is no longer sponsored.
|
||||
// We do not check the sponsee's reserve here (via `checkReserve`) so that a sponsor can
|
||||
// always end a sponsorship, even if the sponsee lacks sufficient reserve.
|
||||
// The owner reclaims the reserve burden when the object is no longer
|
||||
// sponsored, so it must be able to hold that reserve on its own once the
|
||||
// sponsorship is removed. This mirrors the account-level End check below,
|
||||
// keeping the behavior consistent across accounts and objects: a
|
||||
// sponsorship can only be ended if the sponsee self-funds, another sponsor
|
||||
// steps in (Reassign), or the object/account is deleted.
|
||||
if (view().rules().enabled(fixCleanup3_4_0))
|
||||
{
|
||||
if (auto const ter = checkReserve(
|
||||
ctx_.getApplyViewContext(),
|
||||
sponseeSle,
|
||||
balanceBeforeFee(sponseeSle),
|
||||
SLE::pointer(),
|
||||
{.ownerCountDelta = ownerCountDelta},
|
||||
ctx_.journal);
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
}
|
||||
|
||||
// Decrement sponsored count
|
||||
if (auto const ter = decrementSponsorCount(
|
||||
|
||||
@@ -1073,14 +1073,17 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testTransferSponsor()
|
||||
testTransferSponsor(FeatureBitset features)
|
||||
{
|
||||
testcase("Transfer Sponsor");
|
||||
testcase(
|
||||
std::string("Transfer Sponsor ") +
|
||||
(features[fixCleanup3_4_0] ? "(fixCleanup3_4_0 enabled)"
|
||||
: "(fixCleanup3_4_0 disabled)"));
|
||||
using namespace test::jtx;
|
||||
|
||||
// Verify preflight checks
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor("sponsor");
|
||||
@@ -1164,7 +1167,7 @@ public:
|
||||
|
||||
{
|
||||
// Invalid SponsorshipEnd permission (sponsor object/sponsor account)
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const charlie("charlie");
|
||||
@@ -1209,7 +1212,7 @@ public:
|
||||
|
||||
{
|
||||
// sponsor account
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor1("sponsor1");
|
||||
@@ -1340,7 +1343,7 @@ public:
|
||||
}
|
||||
{
|
||||
// dissolve account sponsorship from sponsor
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor("sponsor");
|
||||
@@ -1364,7 +1367,7 @@ public:
|
||||
|
||||
{
|
||||
// sponsor object (co-signing)
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor1("sponsor1");
|
||||
@@ -1473,10 +1476,20 @@ public:
|
||||
BEAST_EXPECT(sle2->isFieldPresent(sfSponsor));
|
||||
BEAST_EXPECT(sle2->getAccountID(sfSponsor) == sponsor2.id());
|
||||
|
||||
// dissolve sponsor: ending an object sponsorship succeeds even
|
||||
// when the sponsee lacks sufficient reserve to reclaim the object.
|
||||
// dissolve sponsor: ending an object sponsorship now (fixCleanup3_4_0) requires the
|
||||
// sponsee to be able to self-fund the object's reserve.
|
||||
adjustAccountXRPBalance(env, alice, reserve(env, 1) - drops(1));
|
||||
|
||||
if (features[fixCleanup3_4_0])
|
||||
{
|
||||
// Under-funded: End is rejected until alice can self-fund.
|
||||
env(sponsor::transfer(alice, tfSponsorshipEnd, checkId),
|
||||
Ter(tecINSUFFICIENT_RESERVE));
|
||||
env.close();
|
||||
|
||||
adjustAccountXRPBalance(env, alice, reserve(env, 1));
|
||||
}
|
||||
|
||||
env(sponsor::transfer(alice, tfSponsorshipEnd, checkId));
|
||||
env.close();
|
||||
|
||||
@@ -1509,7 +1522,7 @@ public:
|
||||
}
|
||||
{
|
||||
// sponsor object (pre-funded + no ltSponsorship entry)
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor1("sponsor1");
|
||||
@@ -1543,7 +1556,7 @@ public:
|
||||
}
|
||||
{
|
||||
// sponsor object (pre-funded)
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor1("sponsor1");
|
||||
@@ -1646,7 +1659,7 @@ public:
|
||||
|
||||
{
|
||||
// Dissolve object sponsorship from sponsor(no-ltSponsorship)
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor("sponsor");
|
||||
@@ -1686,7 +1699,7 @@ public:
|
||||
|
||||
{
|
||||
// Dissolve object sponsorship from sponsor (with ltSponsorship)
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor("sponsor");
|
||||
@@ -1744,7 +1757,7 @@ public:
|
||||
|
||||
for (bool const isIssuerHigh : {false, true})
|
||||
{
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob, sponsor);
|
||||
env.close();
|
||||
|
||||
@@ -1788,7 +1801,7 @@ public:
|
||||
|
||||
{
|
||||
// invalid transfer
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const sponsor("sponsor");
|
||||
@@ -1825,7 +1838,7 @@ public:
|
||||
{
|
||||
// existing owner objects that are outside the v1 SponsorshipTransfer
|
||||
// object allow-list
|
||||
Env env{*this, testableAmendments()};
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const sponsor("sponsor");
|
||||
env.fund(XRP(10000), alice, sponsor);
|
||||
@@ -5671,7 +5684,8 @@ protected:
|
||||
testPreFundAndCosign();
|
||||
testSponsoredFreeTierReserve();
|
||||
|
||||
testTransferSponsor();
|
||||
testTransferSponsor(jtx::testableAmendments());
|
||||
testTransferSponsor(jtx::testableAmendments() - fixCleanup3_4_0);
|
||||
testLegacySignerListReserve();
|
||||
testSponsorFee();
|
||||
testSponsorAccount();
|
||||
|
||||
Reference in New Issue
Block a user