mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 09:16:47 +00:00
add InvariantCheck for sponsor count
This commit is contained in:
@@ -112,6 +112,11 @@ class Invariants_test : public beast::unit_test::suite
|
||||
{
|
||||
terActual = ac.checkInvariants(terActual, fee);
|
||||
BEAST_EXPECT(terExpect == terActual);
|
||||
if (terExpect != terActual)
|
||||
{
|
||||
printf("terActual: %s\n", transHuman(terActual).c_str());
|
||||
printf("terExpect: %s\n", transHuman(terExpect).c_str());
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
sink.messages().str().starts_with("Invariant failed:") ||
|
||||
sink.messages().str().starts_with(
|
||||
@@ -1604,6 +1609,69 @@ class Invariants_test : public beast::unit_test::suite
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
}
|
||||
|
||||
void
|
||||
testSponsorship()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
using namespace std::string_literals;
|
||||
testcase << "Sponsorship";
|
||||
{
|
||||
auto const expect_message =
|
||||
"SponsoredOwnerCount does not equal "
|
||||
"SponsoringOwnerCount delta.";
|
||||
|
||||
doInvariantCheck(
|
||||
{{expect_message}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
sle->setFieldU32(sfSponsoredOwnerCount, 1);
|
||||
ac.view().update(sle);
|
||||
return true;
|
||||
});
|
||||
|
||||
doInvariantCheck(
|
||||
{{expect_message}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
sle->setFieldU32(sfSponsoringOwnerCount, 1);
|
||||
ac.view().update(sle);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
auto const expect_message =
|
||||
"Invariant failed: Net delta of SponsoringAccountCount does "
|
||||
"not match net delta of sfSponsorAccount presence.";
|
||||
|
||||
doInvariantCheck(
|
||||
{{expect_message}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
sle->setFieldU32(sfSponsoringAccountCount, 1);
|
||||
ac.view().update(sle);
|
||||
return true;
|
||||
});
|
||||
|
||||
doInvariantCheck(
|
||||
{{expect_message}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
sle->setAccountID(sfSponsorAccount, A2.id());
|
||||
ac.view().update(sle);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -1623,6 +1691,7 @@ public:
|
||||
testNFTokenPageInvariants();
|
||||
testPermissionedDomainInvariants();
|
||||
testPermissionedDEX();
|
||||
testSponsorship();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -453,6 +453,8 @@ DeleteAccount::doApply()
|
||||
sponsorSle->setFieldU32(
|
||||
sfSponsoringAccountCount, sponsoringAccountCount - 1);
|
||||
view().update(sponsorSle);
|
||||
|
||||
(*src).makeFieldAbsent(sfSponsorAccount);
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
|
||||
@@ -2037,4 +2037,98 @@ ValidAMM::finalize(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add new sponsorship-related invariants implementations
|
||||
|
||||
void
|
||||
SponsorshipOwnerCountsMatch::visitEntry(
|
||||
bool,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after)
|
||||
{
|
||||
auto getSponsored =
|
||||
[](std::shared_ptr<SLE const> const& sle) -> std::uint32_t {
|
||||
if (sle && sle->getType() == ltACCOUNT_ROOT)
|
||||
return sle->getFieldU32(sfSponsoredOwnerCount);
|
||||
return 0;
|
||||
};
|
||||
auto getSponsoring =
|
||||
[](std::shared_ptr<SLE const> const& sle) -> std::uint32_t {
|
||||
if (sle && sle->getType() == ltACCOUNT_ROOT)
|
||||
return sle->getFieldU32(sfSponsoringOwnerCount);
|
||||
return 0;
|
||||
};
|
||||
|
||||
std::int64_t const beforeSponsored = getSponsored(before);
|
||||
std::int64_t const afterSponsored = getSponsored(after);
|
||||
std::int64_t const beforeSponsoring = getSponsoring(before);
|
||||
std::int64_t const afterSponsoring = getSponsoring(after);
|
||||
|
||||
deltaSponsoredOwnerCount_ += (afterSponsored - beforeSponsored);
|
||||
deltaSponsoringOwnerCount_ += (afterSponsoring - beforeSponsoring);
|
||||
}
|
||||
|
||||
bool
|
||||
SponsorshipOwnerCountsMatch::finalize(
|
||||
STTx const&,
|
||||
TER const,
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
if (deltaSponsoredOwnerCount_ != deltaSponsoringOwnerCount_)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: SponsoredOwnerCount does not "
|
||||
"equal SponsoringOwnerCount delta.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SponsorshipAccountCountMatchesField::visitEntry(
|
||||
bool,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after)
|
||||
{
|
||||
auto getSponsoringAccountCount =
|
||||
[](std::shared_ptr<SLE const> const& sle) -> std::uint32_t {
|
||||
if (sle && sle->getType() == ltACCOUNT_ROOT)
|
||||
return sle->getFieldU32(sfSponsoringAccountCount);
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto hasSponsorField = [](std::shared_ptr<SLE const> const& sle) -> bool {
|
||||
return sle && sle->getType() == ltACCOUNT_ROOT &&
|
||||
sle->isFieldPresent(sfSponsorAccount);
|
||||
};
|
||||
|
||||
std::int64_t const beforeCount = getSponsoringAccountCount(before);
|
||||
std::int64_t const afterCount = getSponsoringAccountCount(after);
|
||||
deltaSponsoringAccountCount_ += (afterCount - beforeCount);
|
||||
|
||||
int const beforePresent = hasSponsorField(before) ? 1 : 0;
|
||||
int const afterPresent = hasSponsorField(after) ? 1 : 0;
|
||||
deltaSponsorFieldPresence_ += (afterPresent - beforePresent);
|
||||
}
|
||||
|
||||
bool
|
||||
SponsorshipAccountCountMatchesField::finalize(
|
||||
STTx const&,
|
||||
TER const,
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
if (deltaSponsoringAccountCount_ != deltaSponsorFieldPresence_)
|
||||
{
|
||||
JLOG(j.fatal())
|
||||
<< "Invariant failed: Net delta of SponsoringAccountCount does not "
|
||||
"match net delta of sfSponsorAccount presence.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -531,6 +531,63 @@ public:
|
||||
beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Sponsored owner counts are balanced.
|
||||
*
|
||||
* The following check is made for every transaction:
|
||||
* - The sum of all per-account deltas of `sfSponsoredOwnerCount` equals
|
||||
* the sum of all per-account deltas of `sfSponsoringOwnerCount`.
|
||||
*/
|
||||
class SponsorshipOwnerCountsMatch
|
||||
{
|
||||
std::int64_t deltaSponsoredOwnerCount_ = 0;
|
||||
std::int64_t deltaSponsoringOwnerCount_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(
|
||||
bool,
|
||||
std::shared_ptr<SLE const> const&,
|
||||
std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(
|
||||
STTx const&,
|
||||
TER const,
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Sponsoring account relationships tracked consistently.
|
||||
*
|
||||
* The following check is made for every transaction:
|
||||
* - The net delta of `sfSponsoringAccountCount` across all accounts equals
|
||||
* the net delta of the count of ltACCOUNT_ROOT entries having
|
||||
* `sfSponsorAccount` present (presence transitions only: add/remove).
|
||||
*/
|
||||
class SponsorshipAccountCountMatchesField
|
||||
{
|
||||
std::int64_t deltaSponsoringAccountCount_ = 0;
|
||||
std::int64_t deltaSponsorFieldPresence_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(
|
||||
bool,
|
||||
std::shared_ptr<SLE const> const&,
|
||||
std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(
|
||||
STTx const&,
|
||||
TER const,
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Token holder's trustline balance cannot be negative after
|
||||
* Clawback.
|
||||
@@ -716,6 +773,8 @@ using InvariantChecks = std::tuple<
|
||||
NoXRPTrustLines,
|
||||
NoDeepFreezeTrustLinesWithoutFreeze,
|
||||
TransfersNotFrozen,
|
||||
SponsorshipOwnerCountsMatch,
|
||||
SponsorshipAccountCountMatchesField,
|
||||
NoBadOffers,
|
||||
NoZeroEscrow,
|
||||
ValidNewAccountRoot,
|
||||
|
||||
Reference in New Issue
Block a user