mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-25 08:00:22 +00:00
Merge branch 'xrplf/sponsor' into mvadari/sponsor/reserve-context
This commit is contained in:
@@ -197,6 +197,15 @@ getLedgerEntryOwnerCount(T const& sle)
|
||||
// Vaults require 2 owner counts (the vault and a pseudo-account)
|
||||
case ltVAULT:
|
||||
return 2;
|
||||
case ltSIGNER_LIST: {
|
||||
// Mirror SignerListSet's owner-count accounting so that create and
|
||||
// delete agree. Modern lists (post-MultiSignReserve) carry the
|
||||
// lsfOneOwnerCount flag and cost a single owner count. Legacy
|
||||
// pre-MultiSignReserve lists cost 2 + signer_count owner counts
|
||||
if (sle->isFlag(lsfOneOwnerCount))
|
||||
return 1;
|
||||
return 2 + static_cast<std::uint32_t>(sle->getFieldArray(sfSignerEntries).size());
|
||||
}
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,15 @@ SponsorshipOwnerCountsMatch::visitEntry(
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
case ltSIGNER_LIST: {
|
||||
if (!sle->isFieldPresent(sfSponsor))
|
||||
return 0;
|
||||
// Modern lists carry lsfOneOwnerCount and cost 1.
|
||||
// Legacy (pre-MultiSignReserve) lists cost 2 + signer_count.
|
||||
if (sle->isFlag(lsfOneOwnerCount))
|
||||
return 1;
|
||||
return 2 + static_cast<std::uint32_t>(sle->getFieldArray(sfSignerEntries).size());
|
||||
}
|
||||
default: {
|
||||
if (sle->isFieldPresent(sfSponsor))
|
||||
return 1;
|
||||
|
||||
@@ -330,7 +330,7 @@ SponsorshipTransfer::doApply()
|
||||
if (!ownerSle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
std::int64_t const ownerCountDelta = 1;
|
||||
auto const ownerCountDelta = static_cast<std::int32_t>(getLedgerEntryOwnerCount(objSle));
|
||||
|
||||
auto const& sponsorField = getLedgerEntrySponsorField(objSle, *ownerID);
|
||||
|
||||
|
||||
@@ -3438,6 +3438,77 @@ public:
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor2) == 0);
|
||||
}
|
||||
|
||||
// Legacy (pre-MultiSignReserve) SignerLists lack lsfOneOwnerCount and cost
|
||||
// 2 + signer_count owner units, whereas modern lists cost 1.
|
||||
void
|
||||
testLegacySignerListReserve()
|
||||
{
|
||||
testcase("Legacy SignerList sponsorship reserve");
|
||||
using namespace test::jtx;
|
||||
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const carol("carol");
|
||||
Account const dave("dave");
|
||||
Account const sponsor("sponsor");
|
||||
|
||||
Env env{*this, testableAmendments()};
|
||||
env.fund(XRP(1000000), alice, bob, carol, dave, sponsor);
|
||||
env.close();
|
||||
|
||||
// Modern 3-signer list: weight 1, lsfOneOwnerCount set.
|
||||
env(signers(alice, 1, {{bob, 1}, {carol, 1}, {dave, 1}}));
|
||||
env.close();
|
||||
|
||||
auto const signerListKeylet = keylet::signerList(alice.id());
|
||||
auto const sponsorKeylet = keylet::sponsorship(sponsor.id(), alice.id());
|
||||
std::uint32_t const legacyWeight = 5; // 2 + 3 signers
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
|
||||
// Pre-fund exactly the legacy weight
|
||||
env(sponsor::set_reserve(sponsor, 0, legacyWeight), sponsor::SponseeAcc(alice));
|
||||
env.close();
|
||||
if (auto const sle = env.le(sponsorKeylet); BEAST_EXPECT(sle))
|
||||
BEAST_EXPECT(sle->getFieldU32(sfRemainingOwnerCount) == legacyWeight);
|
||||
|
||||
// Synthesize a pre-MultiSignReserve list: clear lsfOneOwnerCount and
|
||||
// restore the owner's OwnerCount to the legacy weight.
|
||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal) -> bool {
|
||||
auto signerList = std::make_shared<SLE>(*view.read(signerListKeylet));
|
||||
auto account = std::make_shared<SLE>(*view.read(keylet::account(alice.id())));
|
||||
signerList->clearFlag(lsfOneOwnerCount);
|
||||
account->setFieldU32(sfOwnerCount, legacyWeight);
|
||||
view.rawReplace(signerList);
|
||||
view.rawReplace(account);
|
||||
return true;
|
||||
});
|
||||
if (auto const sle = env.le(signerListKeylet); BEAST_EXPECT(sle))
|
||||
BEAST_EXPECT((sle->getFlags() & lsfOneOwnerCount) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == legacyWeight);
|
||||
|
||||
// Create must charge the full legacy weight (5), not 1: the bug bumped
|
||||
// the counters by 1 and left 4 pre-funded units unspent.
|
||||
env(sponsor::transfer(alice, tfSponsorshipCreate, signerListKeylet.key),
|
||||
sponsor::As(sponsor, spfSponsorReserve));
|
||||
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == legacyWeight);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == legacyWeight);
|
||||
// All pre-funded units consumed (drained to absent).
|
||||
if (auto const sle = env.le(sponsorKeylet); BEAST_EXPECT(sle))
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount));
|
||||
|
||||
// Delete unwinds the legacy weight; create bumped by the same amount, so
|
||||
// the counters return to 0 (the bug bumped by 1 -> underflow on delete).
|
||||
env(signers(alice, NoneT()));
|
||||
|
||||
BEAST_EXPECT(!env.le(signerListKeylet));
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoredOwnerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(sponsoringOwnerCount(env, sponsor) == 0);
|
||||
if (auto const sle = env.le(sponsorKeylet); BEAST_EXPECT(sle))
|
||||
BEAST_EXPECT(!sle->isFieldPresent(sfRemainingOwnerCount));
|
||||
}
|
||||
|
||||
void
|
||||
testSponsoredTrustLineNoFreeReserve()
|
||||
{
|
||||
@@ -4346,6 +4417,7 @@ protected:
|
||||
testSponsoredFreeTierReserve();
|
||||
|
||||
testTransferSponsor();
|
||||
testLegacySignerListReserve();
|
||||
testSponsorFee();
|
||||
testSponsorAccount();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user