rabbit hole: refactor dirAdd to find gaps in "full" directories.

- This would potentially be very expensive to implement, so don't.
- However, it might be a good start for a ledger fix option.
This commit is contained in:
Ed Hennis
2025-03-21 19:15:57 -04:00
parent 61fbde3a71
commit 53e6d7580a
4 changed files with 80 additions and 5 deletions

View File

@@ -20,6 +20,10 @@ removeTokenOffersWithLimit(
Keylet const& directory,
std::size_t maxDeletableOffers);
/** Returns tesSUCCESS if NFToken has few enough offers that it can be burned */
TER
notTooManyOffers(ReadView const& view, uint256 const& nftokenID);
/** Finds the specified token in the owner's token directory. */
std::optional<STObject>
findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID);

View File

@@ -15,6 +15,7 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
XRPL_FEATURE(DefragDirectories, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(MPTokensV2, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (Security3_1_3, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -10,6 +10,14 @@ namespace xrpl {
namespace directory {
struct Gap
{
uint64_t const page;
SLE::pointer node;
uint64_t const nextPage;
SLE::pointer next;
};
std::uint64_t
createRoot(
ApplyView& view,
@@ -111,7 +119,9 @@ insertPage(
if (page == 0)
return std::nullopt;
if (!view.rules().enabled(fixDirectoryLimit) && page >= dirNodeMaxPages) // Old pages limit
{
return std::nullopt;
}
// We are about to create a new node; we'll link it to
// the chain first:
@@ -133,12 +143,8 @@ insertPage(
// it's the default.
if (page != 1)
node->setFieldU64(sfIndexPrevious, page - 1);
XRPL_ASSERT_PARTS(!nextPage, "xrpl::directory::insertPage", "nextPage has default value");
/* Reserved for future use when directory pages may be inserted in
* between two other pages instead of only at the end of the chain.
if (nextPage)
node->setFieldU64(sfIndexNext, nextPage);
*/
describe(node);
view.insert(node);
@@ -154,7 +160,7 @@ ApplyView::dirAdd(
uint256 const& key,
std::function<void(std::shared_ptr<SLE> const&)> const& describe)
{
auto root = peek(directory);
auto const root = peek(directory);
if (!root)
{
@@ -164,6 +170,43 @@ ApplyView::dirAdd(
auto [page, node, indexes] = directory::findPreviousPage(*this, directory, root);
if (rules().enabled(featureDefragDirectories))
{
// If there are more nodes than just the root, and there's no space in
// the last one, walk backwards to find one with space, or to find one
// missing.
std::optional<directory::Gap> gapPages;
while (page && indexes.size() >= dirNodeMaxEntries)
{
// Find a page with space, or a gap in pages.
auto [prevPage, prevNode, prevIndexes] =
directory::findPreviousPage(*this, directory, node);
if (!gapPages && prevPage != page - 1)
gapPages.emplace(prevPage, prevNode, page, node);
page = prevPage;
node = prevNode;
indexes = prevIndexes;
}
// We looped through all the pages back to the root.
if (!page)
{
// If we found a gap, use it.
if (gapPages)
{
return directory::insertPage(
*this,
gapPages->page,
gapPages->node,
gapPages->nextPage,
gapPages->next,
key,
directory,
describe);
}
std::tie(page, node, indexes) = directory::findPreviousPage(*this, directory, root);
}
}
// If there's space, we use it:
if (indexes.size() < dirNodeMaxEntries)
{

View File

@@ -607,6 +607,33 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
return deletedOffersCount;
}
TER
notTooManyOffers(ReadView const& view, uint256 const& nftokenID)
{
std::size_t totalOffers = 0;
{
Dir const buys(view, keylet::nft_buys(nftokenID));
for (auto iter = buys.begin(); iter != buys.end(); iter.next_page())
{
totalOffers += iter.page_size();
if (totalOffers > maxDeletableTokenOfferEntries)
return tefTOO_BIG;
}
}
{
Dir const sells(view, keylet::nft_sells(nftokenID));
for (auto iter = sells.begin(); iter != sells.end(); iter.next_page())
{
totalOffers += iter.page_size();
if (totalOffers > maxDeletableTokenOfferEntries)
return tefTOO_BIG;
}
}
return tesSUCCESS;
}
bool
deleteTokenOffer(ApplyView& view, std::shared_ptr<SLE> const& offer)
{