fix: Clarify the cleanup bound and narrow the invariant relaxation

The bound passed to the pseudo-account credential cleanup limits how many
owner-directory entries the walk visits, not how many credentials it deletes:
entries the walk leaves alone are charged against the same budget. Both comments
claimed the latter, so say what the code actually does.

Narrow the tecINCOMPLETE relaxation in the MPT issuance invariant to VaultDelete.
That is the only transaction carrying the DestroyMptIssuance privilege that can
return tecINCOMPLETE, and the comment above the branch already said as much, so
the code now matches it and any future transaction with the same privilege stays
under the strict rule.

Rename removeExpiredCredentials to removeDeletedCredentials. It now also deletes
credentials removed by a bounded cleanup rather than by expiry, which is the same
reason removeDeletedTrustLines carries that name.
This commit is contained in:
Timur Ialymov
2026-08-18 18:51:03 +01:00
parent ddfbaf6f71
commit 0107671846
4 changed files with 17 additions and 9 deletions

View File

@@ -39,9 +39,12 @@ deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j);
*
* Cleans up credentials that were linked to a pseudo-account (Vault, LoanBroker,
* AMM), which such an account can neither accept nor delete. Only credentials
* are removed, at most @p maxNodesToDelete of them; on reaching that bound the
* result is `tecINCOMPLETE` and the caller must propagate it so a later
* transaction resumes.
* are removed; every other object is left in place. The walk visits at most
* @p maxNodesToDelete directory entries and charges the ones it leaves alone
* against that budget too, so a directory holding other objects yields fewer
* than @p maxNodesToDelete deletions. On reaching the bound the result is
* `tecINCOMPLETE` and the caller must propagate it so a later transaction
* resumes.
*
* @param view Mutable ledger view.
* @param pseudoAcct The pseudo-account whose directory is cleaned.

View File

@@ -397,8 +397,12 @@ using TxID = uint256;
constexpr std::uint16_t kMaxDeletableAmmTrustLines = 512;
/**
* The maximum number of credentials to delete from a pseudo-account's owner
* directory in a single transaction.
* The maximum number of owner-directory entries to walk when clearing
* credentials pinned to a pseudo-account, in a single transaction.
*
* The walk stops after this many entries whether or not each one turns out to
* be a credential, so a directory that also holds other objects yields fewer
* deletions per transaction.
*/
constexpr std::uint16_t kMaxDeletablePseudoAccountCredentials = 512;

View File

@@ -1246,7 +1246,7 @@ removeExpiredNFTokenOffers(
}
static void
removeExpiredCredentials(ApplyView& view, std::vector<uint256> const& creds, beast::Journal viewJ)
removeDeletedCredentials(ApplyView& view, std::vector<uint256> const& creds, beast::Journal viewJ)
{
for (auto const& index : creds)
{
@@ -1255,7 +1255,7 @@ removeExpiredCredentials(ApplyView& view, std::vector<uint256> const& creds, bea
if (auto const ter = credentials::deleteSLE(view, sle, viewJ); !isTesSuccess(ter))
{
JLOG(viewJ.error())
<< "removeExpiredCredentials: failed to delete expired credential. Err: "
<< "removeDeletedCredentials: failed to delete credential. Err: "
<< transToken(ter);
}
}
@@ -1529,7 +1529,7 @@ Transactor::processPersistentChanges(TER result, XRPAmount fee)
removeDeletedTrustLines(view(), ids, viewJ);
break;
case ltCREDENTIAL:
removeExpiredCredentials(view(), ids, viewJ);
removeDeletedCredentials(view(), ids, viewJ);
break;
// LCOV_EXCL_START
default:

View File

@@ -235,7 +235,8 @@ ValidMPTIssuance::finalize(
// pseudo-account returns tecINCOMPLETE and has not yet reached the
// share issuance. Don't require the issuance to be removed until
// the deletion completes (a later transaction).
if (rules.enabled(fixCleanup3_4_0) && result == tecINCOMPLETE)
if (rules.enabled(fixCleanup3_4_0) && txnType == ttVAULT_DELETE &&
result == tecINCOMPLETE)
return mptIssuancesDeleted_ == 0 && mptIssuancesCreated_ == 0;
if (mptIssuancesDeleted_ == 0)