diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 1ffcf23e8a..74d6e772a1 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2299,16 +2299,16 @@ public: BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); - // // transfer sponsor - // env(sponsor::transfer(alice, keylet::signers(alice).key), - // sponsor::as(sponsor2, tfSponsorReserve), - // sponsor::sig(sponsor2)); - // env.close(); + // transfer sponsor + env(sponsor::transfer(alice, keylet::signers(alice).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); - // BEAST_EXPECT(ownerCount(env, alice) == 1); - // BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); - // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); - // BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + BEAST_EXPECT(ownerCount(env, alice) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); // Delete env(signers(alice, none)); @@ -2329,8 +2329,9 @@ public: Account const alice("alice"); Account const bob("bob"); Account const sponsor("sponsor"); + Account const sponsor2("sponsor2"); - env.fund(XRP(1000000), alice, bob, sponsor); + env.fund(XRP(1000000), alice, bob, sponsor, sponsor2); env.close(); auto const& highAcc = alice > bob ? alice : bob; @@ -2352,11 +2353,33 @@ public: BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 1); + auto const line = env.le(keylet::line(user, issuer, USD.currency)); BEAST_EXPECT( - env.le(keylet::line(user, issuer, USD.currency)) - ->getAccountID( - isIssuerHigh ? sfLowSponsorAccount - : sfHighSponsorAccount) == sponsor.id()); + line->getAccountID( + isIssuerHigh ? sfLowSponsorAccount + : sfHighSponsorAccount) == sponsor.id()); + BEAST_EXPECT(!line->isFieldPresent( + isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); + + // transfer sponsor + env(sponsor::transfer( + user, keylet::line(user, issuer, USD.currency).key), + sponsor::as(sponsor2, tfSponsorReserve), + sponsor::sig(sponsor2)); + env.close(); + + BEAST_EXPECT(ownerCount(env, user) == 1); + BEAST_EXPECT(sponsoredOwnerCount(env, user) == 1); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0); + BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 1); + + auto const line2 = env.le(keylet::line(user, issuer, USD.currency)); + BEAST_EXPECT( + line2->getAccountID( + isIssuerHigh ? sfLowSponsorAccount + : sfHighSponsorAccount) == sponsor2.id()); + BEAST_EXPECT(!line2->isFieldPresent( + isIssuerHigh ? sfHighSponsorAccount : sfLowSponsorAccount)); // delete TrustLine env(trust(user, USD(0))); @@ -2368,7 +2391,6 @@ public: BEAST_EXPECT(!env.le(keylet::line(user, issuer, USD.currency))); } - // TODO: transfer sponsor } void @@ -2598,6 +2620,7 @@ public: void testSponsorReserve() { + // TODO: add checks fo InsufficientReserve for Sponsoring ledger entry testRequireFlag(); testCheck(); testOffer(); diff --git a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp index c9ce928572..84442ae362 100644 --- a/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp +++ b/src/xrpld/app/tx/detail/SponsorshipTransfer.cpp @@ -95,7 +95,23 @@ getLedgerEntryOwner( uint256 const& key = sle->key(); return AccountID::fromVoid(key.data()); } - // case ltRIPPLE_STATE: + case ltRIPPLE_STATE: { + if (sle->isFlag(lsfHighReserve)) + { + auto const highAccount = + sle->getFieldAmount(sfHighLimit).getIssuer(); + if (highAccount == account) + return highAccount; + } + if (sle->isFlag(lsfLowReserve)) + { + auto const lowAccount = + sle->getFieldAmount(sfLowLimit).getIssuer(); + if (lowAccount == account) + return lowAccount; + } + return std::nullopt; + } case ltACCOUNT_ROOT: { // AccountRoot is not supported for object sponsorship return std::nullopt; @@ -126,6 +142,37 @@ getLedgerEntryOwnerCount(T const& sle) } }; +template +inline SF_ACCOUNT const& +getLedgerEntrySponsorField(T const& sle, AccountID const& owner) +{ + switch (sle->getType()) + { + case ltRIPPLE_STATE: { + if (sle->isFlag(lsfHighReserve)) + { + auto const highAccount = + sle->getFieldAmount(sfHighLimit).getIssuer(); + if (highAccount == owner) + return sfHighSponsorAccount; + } + if (sle->isFlag(lsfLowReserve)) + { + auto const lowAccount = + sle->getFieldAmount(sfLowLimit).getIssuer(); + if (lowAccount == owner) + return sfLowSponsorAccount; + } + XRPL_ASSERT( + false, + "Should not happen. Owner should be checked before calling " + "this function."); + } + default: + return sfSponsorAccount; + } +}; + TER SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { @@ -151,11 +198,14 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) if (!owner || owner != ctx.tx[sfAccount]) return tecNO_PERMISSION; + auto const& sponsorField = getLedgerEntrySponsorField(sle, *owner); + if (newSponsor) { - if (sle->isFieldPresent(sfSponsorAccount)) + if (sle->isFieldPresent(sponsorField)) { // transfer sponsor + // check if the object owner isn't the same as the new sponsor if ((*newSponsor)->getAccountID(sfAccount) == owner) return tecNO_PERMISSION; } @@ -164,7 +214,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx) { // dissolve sponsor // check object is sponsored - if (!sle->isFieldPresent(sfSponsorAccount)) + if (!sle->isFieldPresent(sponsorField)) return tecNO_PERMISSION; } @@ -242,10 +292,12 @@ SponsorshipTransfer::doApply() auto const ownerCountDelta = getLedgerEntryOwnerCount(objSle); + auto const& sponsorField = getLedgerEntrySponsorField(objSle, *owner); + if (tx.isFieldPresent(sfSponsor)) { auto const sponsorObj = tx.getFieldObject(sfSponsor); - auto const oldSponsor = objSle->getAccountID(sfSponsorAccount); + auto const oldSponsor = objSle->getAccountID(sponsorField); auto const newSponsor = sponsorObj[sfAccount]; // decrement old sponsoring count if exists if (auto const oldSponsorSle = @@ -279,7 +331,7 @@ SponsorshipTransfer::doApply() ownerCountDelta); view().update(newSponsorSle); - objSle->setAccountID(sfSponsorAccount, newSponsor); + objSle->setAccountID(sponsorField, newSponsor); view().update(objSle); } else