From ceeb11aee04d5126d5bf9effcfa749f7301de2fa Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 30 Apr 2025 12:46:03 -0400 Subject: [PATCH] Ensure that an account is not deleted with a non-zero owner count - Updates the AccountRootsDeletedClean invariant --- src/test/ledger/Invariants_test.cpp | 27 ++++++++++++++++++++-- src/xrpld/app/tx/detail/InvariantCheck.cpp | 14 ++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/test/ledger/Invariants_test.cpp b/src/test/ledger/Invariants_test.cpp index da918eed13..3763256cad 100644 --- a/src/test/ledger/Invariants_test.cpp +++ b/src/test/ledger/Invariants_test.cpp @@ -209,12 +209,32 @@ class Invariants_test : public beast::unit_test::suite doInvariantCheck( {{"account deletion left behind a non-zero balance"}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - // Add an object to the ledger for account A1, then delete - // A1 + // A1 has a balance. Delete A1 auto const a1 = A1.id(); auto const sleA1 = ac.view().peek(keylet::account(a1)); if (!sleA1) return false; + if (!BEAST_EXPECT(*sleA1->at(sfBalance) != beast::zero)) + return false; + + ac.view().erase(sleA1); + + return true; + }, + XRPAmount{}, + STTx{ttACCOUNT_DELETE, [](STObject& tx) {}}); + + doInvariantCheck( + {{"account deletion left behind a non-zero owner count"}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + // Increment A1's owner count, then delete A1 + auto const a1 = A1.id(); + auto const sleA1 = ac.view().peek(keylet::account(a1)); + if (!sleA1) + return false; + sleA1->at(sfBalance) = beast::zero; + BEAST_EXPECT(sleA1->at(sfOwnerCount) == 0); + adjustOwnerCount(ac.view(), sleA1, 1, ac.journal); ac.view().erase(sleA1); @@ -268,6 +288,7 @@ class Invariants_test : public beast::unit_test::suite if (!sle) return false; sle->at(sfBalance) = beast::zero; + sle->at(sfOwnerCount) = 0; ac.view().erase(sle); return true; }, @@ -299,6 +320,7 @@ class Invariants_test : public beast::unit_test::suite BEAST_EXPECT(sle->at(~sfAMMID) == ammKey); sle->at(sfBalance) = beast::zero; + sle->at(sfOwnerCount) = 0; ac.view().erase(sle); return true; @@ -362,6 +384,7 @@ class Invariants_test : public beast::unit_test::suite ac.view().emptyDirDelete(ownerDirKeylet)); sle->at(sfBalance) = beast::zero; + sle->at(sfOwnerCount) = 0; ac.view().erase(sle); return true; diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index 3ae7d80a9e..29d35e890f 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -473,7 +473,19 @@ AccountRootsDeletedClean::finalize( XRPL_ASSERT( enforce, "ripple::AccountRootsDeletedClean::finalize : " - "account deletion left behind a non-zero balance"); + "deleted account has zero balance"); + if (enforce) + return false; + } + // An account should not be deleted with a non-zero owner count + if (after->at(sfOwnerCount) != 0) + { + JLOG(j.fatal()) << "Invariant failed: account deletion left " + "behind a non-zero owner count"; + XRPL_ASSERT( + enforce, + "ripple::AccountRootsDeletedClean::finalize : " + "deleted account has zero owner count"); if (enforce) return false; }