Add sfSponsee to ttSponsorshipTransfer

This commit is contained in:
tequ
2026-02-22 17:21:04 +09:00
parent f86b255e73
commit 4a91351bcd
3 changed files with 120 additions and 22 deletions

View File

@@ -1068,6 +1068,7 @@ TRANSACTION(ttSPONSORSHIP_TRANSFER, 85, SponsorshipTransfer,
noPriv,
({
{sfObjectID, soeOPTIONAL},
{sfSponsee, soeOPTIONAL},
}))
/** This transaction create sponsorship object */

View File

@@ -36,6 +36,11 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx)
JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should not be set when creating sponsorship";
return temINVALID_FLAG;
}
if (ctx.tx.isFieldPresent(sfSponsee))
{
JLOG(ctx.j.debug()) << "preflight: sfSponsee should be available only when ending sponsorship";
return temMALFORMED;
}
}
if (flags & tfSponsorshipReassign)
{
@@ -44,6 +49,11 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx)
JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should be set when reassigning sponsorship";
return temINVALID_FLAG;
}
if (ctx.tx.isFieldPresent(sfSponsee))
{
JLOG(ctx.j.debug()) << "preflight: sfSponsee should not be set when reassigning sponsorship";
return temMALFORMED;
}
}
if (flags & tfSponsorshipEnd)
{
@@ -52,6 +62,15 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx)
JLOG(ctx.j.debug()) << "preflight: tfSponsorReserve should not be set when ending sponsorship";
return temINVALID_FLAG;
}
if (ctx.tx.isFieldPresent(sfSponsee))
{
if (ctx.tx.getAccountID(sfSponsee) == ctx.tx.getAccountID(sfAccount))
{
JLOG(ctx.j.debug()) << "preflight: sfSponsee should not be the same as the account";
return temMALFORMED;
}
}
}
// When an account sponsoring, sfSponsorSignature must be provided
@@ -202,8 +221,9 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
auto const account = ctx.tx[sfAccount];
auto const accSle = ctx.view.read(keylet::account(account));
if (!accSle)
auto const sponsee = ctx.tx[~sfSponsee].value_or(account);
auto const sponseeSle = ctx.view.read(keylet::account(sponsee));
if (!sponseeSle)
return tecINTERNAL; // LCOV_EXCL_LINE
if (isObjectSponsor)
@@ -214,8 +234,8 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
auto const ownerCountDelta = getLedgerEntryOwnerCount(sle);
auto const owner = getLedgerEntryOwner(ctx.view, sle, account);
if (!owner || owner != account)
auto const owner = getLedgerEntryOwner(ctx.view, sle, sponsee);
if (!owner || owner != sponsee)
return tecNO_PERMISSION;
auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner);
@@ -250,7 +270,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
// check new sponsor have sufficient balance
if (auto const ter = checkInsufficientReserve(
ctx.view, ctx.tx, accSle, accSle->getFieldAmount(sfBalance), newSponsor, ownerCountDelta);
ctx.view, ctx.tx, sponseeSle, sponseeSle->getFieldAmount(sfBalance), newSponsor, ownerCountDelta);
!isTesSuccess(ter))
return ter;
}
@@ -262,7 +282,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
return tecNO_PERMISSION;
// check account is not sponsored yet
if (accSle->isFieldPresent(sfSponsor))
if (sponseeSle->isFieldPresent(sfSponsor))
return tecNO_PERMISSION;
}
else if (flags & tfSponsorshipReassign)
@@ -271,7 +291,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
return tecNO_PERMISSION;
// check account is already sponsored
if (!accSle->isFieldPresent(sfSponsor))
if (!sponseeSle->isFieldPresent(sfSponsor))
return tecNO_PERMISSION;
}
else if (flags & tfSponsorshipEnd)
@@ -280,15 +300,15 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
return tecNO_PERMISSION;
// check account is sponsored
if (!accSle->isFieldPresent(sfSponsor))
if (!sponseeSle->isFieldPresent(sfSponsor))
return tecNO_PERMISSION;
}
// check account have sufficient balance
// In the case of removing an account sponsor, accSle should have no sfSponsor set (AccountReserve = 0).
// However, by setting accountCountDelta = 1 here, we are able to calculate the actual required Account Reserve.
if (auto const ter =
checkInsufficientReserve(ctx.view, ctx.tx, accSle, accSle->getFieldAmount(sfBalance), newSponsor, 0, 1);
if (auto const ter = checkInsufficientReserve(
ctx.view, ctx.tx, sponseeSle, sponseeSle->getFieldAmount(sfBalance), newSponsor, 0, 1);
!isTesSuccess(ter))
return ter;
}
@@ -330,8 +350,9 @@ SponsorshipTransfer::doApply()
auto const flags = tx.getFlags();
bool const isObjectSponsor = index != std::nullopt;
auto const accSle = view().peek(keylet::account(account_));
if (!accSle)
auto const sponsee = tx[~sfSponsee].value_or(account_);
auto const sponseeSle = view().peek(keylet::account(sponsee));
if (!sponseeSle)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const setSponsorFieldU32 = [](auto const& sle, auto const& field, auto const& delta) {
@@ -441,8 +462,8 @@ SponsorshipTransfer::doApply()
return tefINTERNAL; // LCOV_EXCL_LINE
// decrement sponsored count
setSponsorFieldU32(accSle, sfSponsoredOwnerCount, -ownerCountDelta);
view().update(accSle);
setSponsorFieldU32(sponseeSle, sfSponsoredOwnerCount, -ownerCountDelta);
view().update(sponseeSle);
// decrement old sponsoring count
setSponsorFieldU32(oldSponsorSle, sfSponsoringOwnerCount, -ownerCountDelta);
@@ -473,8 +494,8 @@ SponsorshipTransfer::doApply()
view().update(newSponsorSle);
// set new sponsor to account
accSle->setAccountID(sfSponsor, newSponsor);
view().update(accSle);
sponseeSle->setAccountID(sfSponsor, newSponsor);
view().update(sponseeSle);
}
else if (flags & tfSponsorshipReassign)
{
@@ -486,7 +507,7 @@ SponsorshipTransfer::doApply()
view().update(newSponsorSle);
// decrement old sponsoring count
auto const oldSponsor = accSle->getAccountID(sfSponsor);
auto const oldSponsor = sponseeSle->getAccountID(sfSponsor);
auto const oldSponsorSle = view().peek(keylet::account(oldSponsor));
if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -494,14 +515,15 @@ SponsorshipTransfer::doApply()
view().update(oldSponsorSle);
// set new sponsor to account
accSle->setAccountID(sfSponsor, newSponsor);
view().update(accSle);
sponseeSle->setAccountID(sfSponsor, newSponsor);
view().update(sponseeSle);
}
else if (flags & tfSponsorshipEnd)
{
// dissolve account sponsor
auto const oldSponsor = accSle->getAccountID(sfSponsor);
accSle->makeFieldAbsent(sfSponsor);
auto const oldSponsor = sponseeSle->getAccountID(sfSponsor);
sponseeSle->makeFieldAbsent(sfSponsor);
view().update(sponseeSle);
// decrement account sponsoring count
auto const oldSponsorSle = view().peek(keylet::account(oldSponsor));

View File

@@ -549,7 +549,7 @@ public:
using namespace test::jtx;
{
// invalid flags
// invalid fields
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const bob("bob");
@@ -573,16 +573,28 @@ public:
// invalid tfSponsorshipCreate
// no sponsor field present
env(sponsor::transfer(alice, tfSponsorshipCreate), ter(temINVALID_FLAG));
// sponsee field present
env(sponsor::transfer(alice, tfSponsorshipCreate),
sponsor::sponseeAcc(bob),
sponsor::as(sponsor1, tfSponsorReserve),
ter(temMALFORMED));
// invalid tfSponsorshipReassign
// no sponsor field present
env(sponsor::transfer(alice, tfSponsorshipReassign), ter(temINVALID_FLAG));
// sponsee field present
env(sponsor::transfer(alice, tfSponsorshipReassign),
sponsor::sponseeAcc(bob),
sponsor::as(sponsor1, tfSponsorReserve),
ter(temMALFORMED));
// invalid tfSponsorshipEnd
// sponsor field present
env(sponsor::transfer(alice, tfSponsorshipEnd),
sponsor::as(sponsor1, tfSponsorReserve),
ter(temINVALID_FLAG));
// account = sponsee
env(sponsor::transfer(alice, tfSponsorshipEnd), sponsor::sponseeAcc(alice), ter(temMALFORMED));
}
{
@@ -705,6 +717,30 @@ public:
env(sponsor::transfer(bob, tfSponsorshipEnd), ter(tecNO_PERMISSION));
env.close();
}
{
// dissolve account sponsorship from sponsor
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const bob("bob");
Account const sponsor("sponsor");
env.fund(XRP(10000), alice, bob, sponsor);
env.close();
env(sponsor::transfer(alice, tfSponsorshipCreate),
sponsor::as(sponsor, tfSponsorReserve),
sig(sfSponsorSignature, sponsor));
env.close();
BEAST_EXPECT(env.le(alice)->getAccountID(sfSponsor) == sponsor.id());
BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 1);
env(sponsor::transfer(sponsor, tfSponsorshipEnd), sponsor::sponseeAcc(alice));
env.close();
BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfSponsor));
BEAST_EXPECT(sponsoringAccountCount(env, sponsor) == 0);
}
{
// sponsor object (co-signing)
Env env{*this, testable_amendments()};
@@ -951,6 +987,45 @@ public:
BEAST_EXPECT(sponsor2Sle->getFieldU32(sfReserveCount) == 100); // paybacked
}
{
// Dissolve object sponsorship from sponsor
Env env{*this, testable_amendments()};
Account const alice("alice");
Account const bob("bob");
Account const sponsor("sponsor");
env.fund(XRP(10000), alice, bob, sponsor);
env.close();
auto const seq = env.seq(alice);
env(check::create(alice, bob, XRP(1)));
env.close();
auto const checkId = keylet::check(alice, seq).key;
BEAST_EXPECT(env.le(keylet::unchecked(checkId)) != nullptr);
env(sponsor::transfer(alice, tfSponsorshipCreate, checkId),
sponsor::as(sponsor, tfSponsorReserve),
sig(sfSponsorSignature, sponsor));
env.close();
BEAST_EXPECT(env.le(keylet::unchecked(checkId))->getAccountID(sfSponsor) == sponsor.id());
BEAST_EXPECT(ownerCount(env, alice) == 1);
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1);
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1);
// not the owner of the object
env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), ter(tecNO_PERMISSION));
env.close();
env(sponsor::transfer(sponsor, tfSponsorshipEnd, checkId), sponsor::sponseeAcc(alice));
env.close();
BEAST_EXPECT(!env.le(keylet::unchecked(checkId))->isFieldPresent(sfSponsor));
BEAST_EXPECT(ownerCount(env, alice) == 1);
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
}
{
// sponsor trustline
Account const alice("alice");