Improve directory insertion & deletion (RIPD-1353, RIPD-1488):

This commit introduces the "SortedDirectories" amendment, which
addresses two distinct issues:

First, it corrects a technical flaw that could, in some edge cases,
prevent an empty intermediate page from being deleted.

Second, it sorts directory entries within a page (other than order
book page entries, which remain strictly FIFO). This makes insert
operations deterministic, instead of pseudo-random and reliant on
temporal ordering.

Lastly, it removes the ability to perform a "soft delete" where
the page number of the item to delete need not be known if the
item is in the first 20 pages, and enforces a maximum limit to
the number of pages that a directory can span.
This commit is contained in:
Nik Bougalis
2017-06-13 19:06:55 -07:00
committed by seelabs
parent 3666948610
commit 463b154e3d
24 changed files with 1186 additions and 529 deletions

View File

@@ -835,107 +835,8 @@ class GetAmendments_test
}
};
class DirIsEmpty_test
: public beast::unit_test::suite
{
void
testDirIsEmpty()
{
using namespace jtx;
auto const alice = Account("alice");
auto const bogie = Account("bogie");
Env env(*this, with_features(featureMultiSign));
env.fund(XRP(10000), alice);
env.close();
// alice should have an empty directory.
BEAST_EXPECT(dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
// Give alice a signer list, then there will be stuff in the directory.
env(signers(alice, 1, { { bogie, 1} }));
env.close();
BEAST_EXPECT(! dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
env(signers(alice, jtx::none));
env.close();
BEAST_EXPECT(dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
// The next test is a bit awkward. It tests the case where alice
// uses 3 directory pages and then deletes all entries from the
// first 2 pages. dirIsEmpty() should still return false in this
// circumstance.
//
// Fill alice's directory with implicit trust lines (produced by
// taking offers) and then remove all but the last one.
auto const becky = Account ("becky");
auto const gw = Account ("gw");
env.fund(XRP(10000), becky, gw);
env.close();
static_assert (64 >= (2 * dirNodeMaxEntries), "");
// Generate 64 currencies named AAA -> AAP and ADA -> ADP.
std::vector<IOU> currencies;
currencies.reserve(64);
for (char b = 'A'; b <= 'D'; ++b)
{
for (char c = 'A'; c <= 'P'; ++c)
{
currencies.push_back(gw[std::string("A") + b + c]);
IOU const& currency = currencies.back();
// Establish trust lines.
env(trust(becky, currency(50)));
env.close();
env(pay(gw, becky, currency(50)));
env.close();
env(offer(alice, currency(50), XRP(10)));
env(offer(becky, XRP(10), currency(50)));
env.close();
}
}
// Set up one more currency that alice will hold onto. We expect
// this one to go in the third directory page.
IOU const lastCurrency = gw["ZZZ"];
env(trust(becky, lastCurrency(50)));
env.close();
env(pay(gw, becky, lastCurrency(50)));
env.close();
env(offer(alice, lastCurrency(50), XRP(10)));
env(offer(becky, XRP(10), lastCurrency(50)));
env.close();
BEAST_EXPECT(! dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
// Now alice gives all the currencies except the last one back to becky.
for (auto currency : currencies)
{
env(pay(alice, becky, currency(50)));
env.close();
}
// This is the crux of the test.
BEAST_EXPECT(! dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
// Give the last currency to becky. Now alice's directory is empty.
env(pay(alice, becky, lastCurrency(50)));
env.close();
BEAST_EXPECT(dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
}
void run() override
{
testDirIsEmpty();
}
};
BEAST_DEFINE_TESTSUITE(View,ledger,ripple);
BEAST_DEFINE_TESTSUITE(GetAmendments,ledger,ripple);
BEAST_DEFINE_TESTSUITE(DirIsEmpty, ledger,ripple);
} // test
} // ripple