Compare commits

..

15 Commits

Author SHA1 Message Date
Ed Hennis
8f267c3bc9 Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-12-05 21:13:25 -05:00
Ed Hennis
bface8d5d6 Merge remote-tracking branch 'XRPLF/develop' into ximinez/emptydirectoryinvariant
* XRPLF/develop:
  Implement Lending Protocol (unsupported) (5270)
2025-12-02 19:04:37 -05:00
Ed Hennis
24174f6ea7 Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-12-01 14:41:02 -05:00
Ed Hennis
cf3ad16bdf Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-28 15:52:45 -05:00
Ed Hennis
a4046aa135 Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-26 00:25:34 -05:00
Ed Hennis
46f6332e60 Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-25 14:55:24 -05:00
Ed Hennis
ff3c2bf2f9 Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-24 21:49:26 -05:00
Ed Hennis
379e1ed555 Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-24 21:30:38 -05:00
Ed Hennis
e9fb99056b Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-21 14:34:49 -05:00
Ed Hennis
e9fa9d7aa6 Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-18 22:51:22 -05:00
Ed Hennis
2c3f169dec Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-15 03:08:56 -05:00
Ed Hennis
23565405ee Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-13 12:20:06 -05:00
Ed Hennis
a5d08b0cd5 Merge branch 'develop' into ximinez/emptydirectoryinvariant 2025-11-12 14:17:02 -05:00
Ed Hennis
7bf3f543b3 Experiment: Always delete the root 2025-11-10 19:53:38 -05:00
Ed Hennis
c773288df5 Experiment: Add invariant to enforce directory node population 2025-11-10 19:53:38 -05:00
4 changed files with 73 additions and 51 deletions

View File

@@ -16,7 +16,6 @@
// 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(LendingProtocol, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (DirectoryLimit, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -10,14 +10,6 @@ namespace ripple {
namespace directory {
struct Gap
{
uint64_t const page;
SLE::pointer node;
uint64_t const nextPage;
SLE::pointer next;
};
std::uint64_t
createRoot(
ApplyView& view,
@@ -120,9 +112,7 @@ insertPage(
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:
@@ -144,8 +134,15 @@ insertPage(
// it's the default.
if (page != 1)
node->setFieldU64(sfIndexPrevious, page - 1);
XRPL_ASSERT_PARTS(
!nextPage,
"ripple::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);
@@ -161,7 +158,7 @@ ApplyView::dirAdd(
uint256 const& key,
std::function<void(std::shared_ptr<SLE> const&)> const& describe)
{
auto const root = peek(directory);
auto root = peek(directory);
if (!root)
{
@@ -172,44 +169,6 @@ 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)
{
@@ -297,6 +256,7 @@ ApplyView::dirRemove(
uint256 const& key,
bool keepRoot)
{
keepRoot = false;
auto node = peek(keylet::page(directory, page));
if (!node)

View File

@@ -3553,4 +3553,42 @@ ValidVault::finalize(
return true;
}
//------------------------------------------------------------------------------
void
NoEmptyDirectory::visitEntry(
bool isDelete,
std::shared_ptr<SLE const> const& before,
std::shared_ptr<SLE const> const& after)
{
if (isDelete)
return;
if (before && before->getType() != ltDIR_NODE)
return;
if (after && after->getType() != ltDIR_NODE)
return;
if (!after->isFieldPresent(sfOwner))
// Not an account dir
return;
bad_ = after->at(sfIndexes).empty();
}
bool
NoEmptyDirectory::finalize(
STTx const& tx,
TER const result,
XRPAmount const,
ReadView const& view,
beast::Journal const& j)
{
if (bad_)
{
JLOG(j.fatal()) << "Invariant failed: empty owner directory.";
return false;
}
return true;
}
} // namespace ripple

View File

@@ -901,6 +901,30 @@ public:
beast::Journal const&);
};
/**
* @brief Invariants: An account's directory should never be empty
*
*/
class NoEmptyDirectory
{
bool bad_ = false;
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&);
};
// additional invariant checks can be declared above and then added to this
// tuple
using InvariantChecks = std::tuple<
@@ -927,7 +951,8 @@ using InvariantChecks = std::tuple<
ValidPseudoAccounts,
ValidLoanBroker,
ValidLoan,
ValidVault>;
ValidVault,
NoEmptyDirectory>;
/**
* @brief get a tuple of all invariant checks