This commit is contained in:
tequ
2026-03-19 15:39:55 +09:00
parent d1346fa3f6
commit 6d7ffcbb91
2 changed files with 91 additions and 4 deletions

View File

@@ -497,6 +497,20 @@ AccountRootsDeletedClean::finalize(
if (enforce)
return false;
}
// An account should not be deleted with sponsorship fields
if (after->isFieldPresent(sfSponsoredOwnerCount) ||
after->isFieldPresent(sfSponsoringOwnerCount) ||
after->isFieldPresent(sfSponsoringAccountCount) || after->isFieldPresent(sfSponsor))
{
JLOG(j.fatal()) << "Invariant failed: account deletion left "
"behind a sponsorship field";
XRPL_ASSERT(
enforce,
"xrpl::AccountRootsDeletedClean::finalize : "
"deleted account has no sponsorship fields");
if (enforce)
return false;
}
// Simple types
for (auto const& [keyletfunc, _1, _2] : directAccountKeylets)
{
@@ -849,10 +863,10 @@ ValidPseudoAccounts::visitEntry(
// 1. Exactly one of the pseudo-account fields is set.
// 2. The sequence number is not changed.
// 3. The lsfDisableMaster, lsfDefaultRipple, and lsfDepositAuth
// flags are set.
// flags are set.
// 4. The RegularKey is not set.
// 5. The SponsoredOwnerCount, SponsoringOwnerCount, and
// SponsorAccount fields are not set.
// 5. The SponsoredOwnerCount, SponsoringOwnerCount, SponsoringAccountCount, Sponsor
// fields are not set.
{
std::vector<SField const*> const& fields = getPseudoAccountFields();
@@ -880,7 +894,8 @@ ValidPseudoAccounts::visitEntry(
errors_.emplace_back("pseudo-account has a regular key");
}
if (after->isFieldPresent(sfSponsoredOwnerCount) ||
after->isFieldPresent(sfSponsoringOwnerCount) || after->isFieldPresent(sfSponsor))
after->isFieldPresent(sfSponsoringOwnerCount) || after->isFieldPresent(sfSponsor) ||
after->isFieldPresent(sfSponsoringAccountCount))
{
errors_.emplace_back("pseudo-account has a sponsorship field");
}

View File

@@ -277,6 +277,74 @@ class Invariants_test : public beast::unit_test::suite
XRPAmount{},
STTx{ttACCOUNT_DELETE, [](STObject& tx) {}});
doInvariantCheck(
{{"account deletion left behind a sponsorship field"}},
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
auto const a1 = A1.id();
auto const sleA1 = ac.view().peek(keylet::account(a1));
if (!sleA1)
return false;
sleA1->at(sfBalance) = beast::zero;
sleA1->setFieldU32(sfSponsoredOwnerCount, 1);
ac.view().erase(sleA1);
return true;
},
XRPAmount{},
STTx{ttACCOUNT_DELETE, [](STObject& tx) {}});
doInvariantCheck(
{{"account deletion left behind a sponsorship field"}},
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
auto const a1 = A1.id();
auto const sleA1 = ac.view().peek(keylet::account(a1));
if (!sleA1)
return false;
sleA1->at(sfBalance) = beast::zero;
sleA1->setFieldU32(sfSponsoringOwnerCount, 1);
ac.view().erase(sleA1);
return true;
},
XRPAmount{},
STTx{ttACCOUNT_DELETE, [](STObject& tx) {}});
doInvariantCheck(
{{"account deletion left behind a sponsorship field"}},
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
auto const a1 = A1.id();
auto const sleA1 = ac.view().peek(keylet::account(a1));
if (!sleA1)
return false;
sleA1->at(sfBalance) = beast::zero;
sleA1->setFieldU32(sfSponsoringAccountCount, 1);
ac.view().erase(sleA1);
return true;
},
XRPAmount{},
STTx{ttACCOUNT_DELETE, [](STObject& tx) {}});
doInvariantCheck(
{{"account deletion left behind a sponsorship field"}},
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
auto const a1 = A1.id();
auto const sleA1 = ac.view().peek(keylet::account(a1));
if (!sleA1)
return false;
sleA1->at(sfBalance) = beast::zero;
sleA1->setAccountID(sfSponsor, A2.id());
ac.view().erase(sleA1);
return true;
},
XRPAmount{},
STTx{ttACCOUNT_DELETE, [](STObject& tx) {}});
for (auto const& keyletInfo : directAccountKeylets)
{
// TODO: Use structured binding once LLVM 16 is the minimum
@@ -1650,6 +1718,10 @@ class Invariants_test : public beast::unit_test::suite
"pseudo-account has a sponsorship field",
[](SLE::pointer& sle) { sle->at(sfSponsoringOwnerCount) = 1; },
},
{
"pseudo-account has a sponsorship field",
[](SLE::pointer& sle) { sle->at(sfSponsoringAccountCount) = 1; },
},
{
"pseudo-account has a sponsorship field",
[](SLE::pointer& sle) { sle->at(sfSponsor) = Account("sponsor").id(); },