fix: add missing tests to cover all scenario for sponsor reserve (#7574)

This commit is contained in:
Kassaking7
2026-06-30 17:24:33 -04:00
committed by GitHub
parent 140d5ec297
commit 4e6f8f334f

View File

@@ -30,6 +30,7 @@
#include <test/jtx/vault.h>
#include <xrpl/basics/Number.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/strHex.h>
@@ -40,6 +41,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OpenView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -58,7 +60,10 @@
#include <functional>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
namespace xrpl::test {
@@ -2231,10 +2236,11 @@ public:
std::optional<std::function<void()>> expected = std::nullopt)
{
using namespace test::jtx;
// auto const sponsorOwnerCountBefore = ownerCount(env, sponsor);
auto const sponseeOwnerCountBefore = ownerCount(env, sponsee);
auto const sponseeSponsoredOwnerCountBefore = sponsoredOwnerCount(env, sponsee);
auto const sponseeSponsoringOwnerCountBefore = sponsoringOwnerCount(env, sponsee);
auto const sponsorOwnerCountBefore = ownerCount(env, sponsor);
auto const sponsorSponsoredOwnerCountBefore = sponsoredOwnerCount(env, sponsor);
auto const sponsorSponsoringOwnerCountBefore = sponsoringOwnerCount(env, sponsor);
std::optional<Sig> sponsorSig =
@@ -2274,7 +2280,7 @@ public:
env.close();
}
if (sponsorReserveCount - 1 > 0)
if (sponsorReserveCount > 1)
{
env(sponsor::set(sponsor, 0, sponsorReserveCount - 1, XRP(1)),
sponsor::SponseeAcc(sponsee));
@@ -2287,8 +2293,53 @@ public:
}
env.close();
}
// A failed sponsored create must not consume prefunded reserve or mutate owner counts.
auto const sponseeOwnerCountBeforeAttempt = ownerCount(env, sponsee);
auto const sponseeSponsoredOwnerCountBeforeAttempt = sponsoredOwnerCount(env, sponsee);
auto const sponseeSponsoringOwnerCountBeforeAttempt =
sponsoringOwnerCount(env, sponsee);
auto const sponsorOwnerCountBeforeAttempt = ownerCount(env, sponsor);
auto const sponsorSponsoredOwnerCountBeforeAttempt = sponsoredOwnerCount(env, sponsor);
auto const sponsorSponsoringOwnerCountBeforeAttempt =
sponsoringOwnerCount(env, sponsor);
auto const sponsorshipSleBeforeAttempt = env.le(keylet::sponsorship(sponsor, sponsee));
bool const reserveCountPresentBeforeAttempt = sponsorshipSleBeforeAttempt &&
sponsorshipSleBeforeAttempt->isFieldPresent(sfRemainingOwnerCount);
std::uint32_t const reserveCountBeforeAttempt = reserveCountPresentBeforeAttempt
? sponsorshipSleBeforeAttempt->getFieldU32(sfRemainingOwnerCount)
: 0;
callback(env, submit(insufficientReserveResult));
env.close();
BEAST_EXPECT(ownerCount(env, sponsee) == sponseeOwnerCountBeforeAttempt);
BEAST_EXPECT(
sponsoredOwnerCount(env, sponsee) == sponseeSponsoredOwnerCountBeforeAttempt);
BEAST_EXPECT(
sponsoringOwnerCount(env, sponsee) == sponseeSponsoringOwnerCountBeforeAttempt);
BEAST_EXPECT(ownerCount(env, sponsor) == sponsorOwnerCountBeforeAttempt);
BEAST_EXPECT(
sponsoredOwnerCount(env, sponsor) == sponsorSponsoredOwnerCountBeforeAttempt);
BEAST_EXPECT(
sponsoringOwnerCount(env, sponsor) == sponsorSponsoringOwnerCountBeforeAttempt);
auto const sponsorshipSleAfterAttempt = env.le(keylet::sponsorship(sponsor, sponsee));
BEAST_EXPECT(
static_cast<bool>(sponsorshipSleAfterAttempt) ==
static_cast<bool>(sponsorshipSleBeforeAttempt));
if (sponsorshipSleAfterAttempt)
{
BEAST_EXPECT(
sponsorshipSleAfterAttempt->isFieldPresent(sfRemainingOwnerCount) ==
reserveCountPresentBeforeAttempt);
if (reserveCountPresentBeforeAttempt)
{
BEAST_EXPECT(
sponsorshipSleAfterAttempt->getFieldU32(sfRemainingOwnerCount) ==
reserveCountBeforeAttempt);
}
}
}
// Success
@@ -2311,6 +2362,13 @@ public:
if (!cosigning)
{
// Prefunded success consumes the reserved owner slot before cleanup.
auto const sponsorshipSle = env.le(keylet::sponsorship(sponsor, sponsee));
BEAST_EXPECT(sponsorshipSle);
BEAST_EXPECT(
!sponsorshipSle->isFieldPresent(sfRemainingOwnerCount) ||
sponsorshipSle->getFieldU32(sfRemainingOwnerCount) == 0);
// cleanup sponsorship
env(sponsor::del(sponsor), sponsor::SponseeAcc(sponsee));
env.close();
@@ -2329,6 +2387,8 @@ public:
sponsorReserveCount);
BEAST_EXPECT(
sponsoringOwnerCount(env, sponsee) - sponseeSponsoringOwnerCountBefore == 0);
BEAST_EXPECT(ownerCount(env, sponsor) == sponsorOwnerCountBefore);
BEAST_EXPECT(sponsoredOwnerCount(env, sponsor) == sponsorSponsoredOwnerCountBefore);
BEAST_EXPECT(
sponsoringOwnerCount(env, sponsor) - sponsorSponsoringOwnerCountBefore ==
sponsorReserveCount);
@@ -2571,6 +2631,7 @@ public:
Account const alice("alice");
Account const sponsor("sponsor");
Account const sponsor2("sponsor2");
auto const credType = std::string("credType");
{
Env env{*this, testableAmendments()};
@@ -2598,6 +2659,114 @@ public:
env.close();
}
else
{
env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice));
env.close();
// No sponsor signature here: this exercises the prefunded reassign path.
env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key),
sponsor::As(sponsor2, spfSponsorReserve));
env.close();
auto const sponsor2Sle = env.le(keylet::sponsorship(sponsor2, alice));
BEAST_EXPECT(sponsor2Sle);
if (sponsor2Sle)
{
BEAST_EXPECT(
!sponsor2Sle->isFieldPresent(sfRemainingOwnerCount) ||
sponsor2Sle->getFieldU32(sfRemainingOwnerCount) == 0);
}
}
BEAST_EXPECT(ownerCount(env, alice) == 1);
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1);
// DepositPreauthDelete
env(deposit::unauth(alice, sponsor));
env.close();
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, testableAmendments()};
env.fund(XRP(1000000), alice, sponsor);
env.close();
auto const authCreds = std::vector<deposit::AuthorizeCredentials>{
{.issuer = sponsor, .credType = credType}};
auto const preauthKeylet = keylet::depositPreauth(
alice.id(),
std::set<std::pair<AccountID, Slice>>{
{sponsor.id(), Slice(credType.data(), credType.size())}});
// Cover DepositPreauth's sfAuthorizeCredentials sponsor-reserve branch.
testEachSponsorship(
env,
cosigning,
sponsor,
alice,
1,
1,
tecINSUFFICIENT_RESERVE,
[&](Env&, auto const& submit) {
submit(deposit::authCredentials(alice, authCreds));
});
// Cover sfUnauthorizeCredentials cleanup for a sponsored preauth object.
BEAST_EXPECT(env.le(preauthKeylet));
BEAST_EXPECT(ownerCount(env, alice) == 1);
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
env(deposit::unauthCredentials(alice, authCreds));
env.close();
BEAST_EXPECT(!env.le(preauthKeylet));
BEAST_EXPECT(ownerCount(env, alice) == 0);
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
}
}
void
testDID(bool cosigning)
{
testcase("DID");
using namespace test::jtx;
Account const alice("alice");
Account const sponsor("sponsor");
Account const sponsor2("sponsor2");
{
Env env{*this, testableAmendments()};
env.fund(XRP(1000000), alice, sponsor, sponsor2);
env.close();
// DIDSet
testEachSponsorship(
env,
cosigning,
sponsor,
alice,
1,
1,
tecINSUFFICIENT_RESERVE,
[&](Env& env, auto const& submit) { submit(did::set(alice), did::Uri("uri")); });
// transfer sponsor
auto const keylet = keylet::did(alice);
if (cosigning)
{
env(sponsor::transfer(alice, tfSponsorshipReassign, keylet.key),
sponsor::As(sponsor2, spfSponsorReserve),
Sig(sfSponsorSignature, sponsor2));
env.close();
}
else
{
env(sponsor::set_reserve(sponsor2, 0, 1), sponsor::SponseeAcc(alice));
env.close();
@@ -2615,11 +2784,15 @@ public:
auto const sponsorshipSle = env.le(keylet::sponsorship(sponsor2, alice));
BEAST_EXPECT(sponsorshipSle);
if (sponsorshipSle)
BEAST_EXPECT(sponsorshipSle->getFieldU32(sfRemainingOwnerCount) == 0);
{
BEAST_EXPECT(
!sponsorshipSle->isFieldPresent(sfRemainingOwnerCount) ||
sponsorshipSle->getFieldU32(sfRemainingOwnerCount) == 0);
}
}
// DepositPreauthDelete
env(deposit::unauth(alice, sponsor));
// DIDDelete
env(did::del(alice));
env.close();
BEAST_EXPECT(ownerCount(env, alice) == 0);
@@ -3490,6 +3663,12 @@ public:
auto const requiredFee = drops(env.current()->fees().increment);
env(acctdelete(alice, bob), Fee(requiredFee), Ter(tecNO_SPONSOR_PERMISSION));
// The failed delete must leave the account sponsored by the original sponsor.
auto const aliceSle = env.le(keylet::account(alice));
BEAST_EXPECT(aliceSle);
if (aliceSle)
BEAST_EXPECT(aliceSle->getAccountID(sfSponsor) == sponsor.id());
auto const sponsorSle = env.le(keylet::account(sponsor));
BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringAccountCount) == 1);
@@ -3533,13 +3712,19 @@ public:
// Verify sfSponsoringOwnerCount is set on sponsor
auto const sponsorSle = env.le(keylet::account(sponsor));
BEAST_EXPECT(sponsorSle->isFieldPresent(sfSponsoringOwnerCount));
BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringOwnerCount) >= 1);
auto const sponsoringOwnerCount = sponsorSle->getFieldU32(sfSponsoringOwnerCount);
BEAST_EXPECT(sponsoringOwnerCount >= 1);
incLgrSeqForAccDel(env, sponsor);
// AccountDelete should fail
auto const requiredFee = drops(env.current()->fees().increment);
env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS));
// The failed delete must not decrement the outstanding sponsored-object count.
auto const sponsorSleAfter = env.le(keylet::account(sponsor));
BEAST_EXPECT(sponsorSleAfter->isFieldPresent(sfSponsoringOwnerCount));
BEAST_EXPECT(
sponsorSleAfter->getFieldU32(sfSponsoringOwnerCount) == sponsoringOwnerCount);
}
{
@@ -3556,13 +3741,19 @@ public:
// Verify sfSponsoringAccountCount is set on sponsor
auto const sponsorSle = env.le(keylet::account(sponsor));
BEAST_EXPECT(sponsorSle->isFieldPresent(sfSponsoringAccountCount));
BEAST_EXPECT(sponsorSle->getFieldU32(sfSponsoringAccountCount) == 1);
auto const sponsoringAccountCount = sponsorSle->getFieldU32(sfSponsoringAccountCount);
BEAST_EXPECT(sponsoringAccountCount == 1);
incLgrSeqForAccDel(env, sponsor);
// AccountDelete should fail
auto const requiredFee = drops(env.current()->fees().increment);
env(acctdelete(sponsor, bob), Fee(requiredFee), Ter(tecHAS_OBLIGATIONS));
// The failed delete must not decrement the outstanding sponsored-account count.
auto const sponsorSleAfter = env.le(keylet::account(sponsor));
BEAST_EXPECT(sponsorSleAfter->isFieldPresent(sfSponsoringAccountCount));
BEAST_EXPECT(
sponsorSleAfter->getFieldU32(sfSponsoringAccountCount) == sponsoringAccountCount);
}
}