mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-03 00:36:48 +00:00
Per XLS-0095, we are taking steps to rename ripple(d) to xrpl(d). This change specifically removes all copyright notices referencing Ripple, XRPLF, and certain affiliated contributors upon mutual agreement, so the notice in the LICENSE.md file applies throughout. Copyright notices referencing external contributions remain as-is. Duplicate verbiage is also removed.
665 lines
23 KiB
C++
665 lines
23 KiB
C++
#include <test/csf/ledgers.h>
|
|
|
|
#include <xrpld/consensus/LedgerTrie.h>
|
|
|
|
#include <xrpl/beast/unit_test.h>
|
|
|
|
#include <random>
|
|
|
|
namespace ripple {
|
|
namespace test {
|
|
|
|
class LedgerTrie_test : public beast::unit_test::suite
|
|
{
|
|
void
|
|
testInsert()
|
|
{
|
|
using namespace csf;
|
|
// Single entry by itself
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 1);
|
|
|
|
t.insert(h["abc"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 2);
|
|
}
|
|
// Suffix of existing (extending tree)
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
// extend with no siblings
|
|
t.insert(h["abcd"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 1);
|
|
|
|
// extend with existing sibling
|
|
t.insert(h["abce"]);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 3);
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abce"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abce"]) == 1);
|
|
}
|
|
// uncommitted of existing node
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abcd"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
// uncommitted with no siblings
|
|
t.insert(h["abcdf"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h["abcdf"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcdf"]) == 1);
|
|
|
|
// uncommitted with existing child
|
|
t.insert(h["abc"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 3);
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h["abcdf"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcdf"]) == 1);
|
|
}
|
|
// Suffix + uncommitted of existing node
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abcd"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
t.insert(h["abce"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abce"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abce"]) == 1);
|
|
}
|
|
// Suffix + uncommitted with existing child
|
|
{
|
|
// abcd : abcde, abcf
|
|
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abcd"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
t.insert(h["abcde"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
t.insert(h["abcf"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 3);
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h["abcf"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcf"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abcde"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcde"]) == 1);
|
|
}
|
|
|
|
// Multiple counts
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["ab"], 4);
|
|
BEAST_EXPECT(t.tipSupport(h["ab"]) == 4);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 4);
|
|
BEAST_EXPECT(t.tipSupport(h["a"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["a"]) == 4);
|
|
|
|
t.insert(h["abc"], 2);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h["ab"]) == 4);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 6);
|
|
BEAST_EXPECT(t.tipSupport(h["a"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["a"]) == 6);
|
|
}
|
|
}
|
|
|
|
void
|
|
testRemove()
|
|
{
|
|
using namespace csf;
|
|
// Not in trie
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
|
|
BEAST_EXPECT(!t.remove(h["ab"]));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(!t.remove(h["a"]));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
}
|
|
// In trie but with 0 tip support
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abcd"]);
|
|
t.insert(h["abce"]);
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(!t.remove(h["abc"]));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 2);
|
|
}
|
|
// In trie with > 1 tip support
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"], 2);
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(t.remove(h["abc"]));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
|
|
t.insert(h["abc"], 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(t.remove(h["abc"], 2));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
|
|
t.insert(h["abc"], 3);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 3);
|
|
BEAST_EXPECT(t.remove(h["abc"], 300));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
}
|
|
// In trie with = 1 tip support, no children
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["ab"]);
|
|
t.insert(h["abc"]);
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["ab"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 1);
|
|
|
|
BEAST_EXPECT(t.remove(h["abc"]));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["ab"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 0);
|
|
}
|
|
// In trie with = 1 tip support, 1 child
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["ab"]);
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcd"]);
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 1);
|
|
|
|
BEAST_EXPECT(t.remove(h["abc"]));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 1);
|
|
}
|
|
// In trie with = 1 tip support, > 1 children
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["ab"]);
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcd"]);
|
|
t.insert(h["abce"]);
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 3);
|
|
|
|
BEAST_EXPECT(t.remove(h["abc"]));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 2);
|
|
}
|
|
|
|
// In trie with = 1 tip support, parent compaction
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["ab"]);
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abd"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
t.remove(h["ab"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abd"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["ab"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 2);
|
|
|
|
t.remove(h["abd"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
testEmpty()
|
|
{
|
|
using namespace csf;
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
BEAST_EXPECT(t.empty());
|
|
|
|
Ledger genesis = h[""];
|
|
t.insert(genesis);
|
|
BEAST_EXPECT(!t.empty());
|
|
t.remove(genesis);
|
|
BEAST_EXPECT(t.empty());
|
|
|
|
t.insert(h["abc"]);
|
|
BEAST_EXPECT(!t.empty());
|
|
t.remove(h["abc"]);
|
|
BEAST_EXPECT(t.empty());
|
|
}
|
|
|
|
void
|
|
testSupport()
|
|
{
|
|
using namespace csf;
|
|
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
BEAST_EXPECT(t.tipSupport(h["a"]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h["axy"]) == 0);
|
|
|
|
BEAST_EXPECT(t.branchSupport(h["a"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["axy"]) == 0);
|
|
|
|
t.insert(h["abc"]);
|
|
BEAST_EXPECT(t.tipSupport(h["a"]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h["ab"]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abcd"]) == 0);
|
|
|
|
BEAST_EXPECT(t.branchSupport(h["a"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abcd"]) == 0);
|
|
|
|
t.insert(h["abe"]);
|
|
BEAST_EXPECT(t.tipSupport(h["a"]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h["ab"]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h["abe"]) == 1);
|
|
|
|
BEAST_EXPECT(t.branchSupport(h["a"]) == 2);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 2);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abe"]) == 1);
|
|
|
|
t.remove(h["abc"]);
|
|
BEAST_EXPECT(t.tipSupport(h["a"]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h["ab"]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h["abe"]) == 1);
|
|
|
|
BEAST_EXPECT(t.branchSupport(h["a"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["ab"]) == 1);
|
|
BEAST_EXPECT(t.branchSupport(h["abc"]) == 0);
|
|
BEAST_EXPECT(t.branchSupport(h["abe"]) == 1);
|
|
}
|
|
|
|
void
|
|
testGetPreferred()
|
|
{
|
|
using namespace csf;
|
|
using Seq = Ledger::Seq;
|
|
// Empty
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
BEAST_EXPECT(t.getPreferred(Seq{0}) == std::nullopt);
|
|
BEAST_EXPECT(t.getPreferred(Seq{2}) == std::nullopt);
|
|
}
|
|
// Genesis support is NOT empty
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
Ledger genesis = h[""];
|
|
t.insert(genesis);
|
|
BEAST_EXPECT(t.getPreferred(Seq{0})->id == genesis.id());
|
|
BEAST_EXPECT(t.remove(genesis));
|
|
BEAST_EXPECT(t.getPreferred(Seq{0}) == std::nullopt);
|
|
BEAST_EXPECT(!t.remove(genesis));
|
|
}
|
|
// Single node no children
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id());
|
|
}
|
|
// Single node smaller child support
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcd"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id());
|
|
}
|
|
// Single node larger child
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcd"], 2);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcd"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id());
|
|
}
|
|
// Single node smaller children support
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcd"]);
|
|
t.insert(h["abce"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id());
|
|
|
|
t.insert(h["abc"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id());
|
|
}
|
|
// Single node larger children
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcd"], 2);
|
|
t.insert(h["abce"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id());
|
|
|
|
t.insert(h["abcd"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcd"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id());
|
|
}
|
|
// Tie-breaker by id
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abcd"], 2);
|
|
t.insert(h["abce"], 2);
|
|
|
|
BEAST_EXPECT(h["abce"].id() > h["abcd"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abce"].id());
|
|
|
|
t.insert(h["abcd"]);
|
|
BEAST_EXPECT(h["abce"].id() > h["abcd"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id());
|
|
}
|
|
|
|
// Tie-breaker not needed
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcd"]);
|
|
t.insert(h["abce"], 2);
|
|
// abce only has a margin of 1, but it owns the tie-breaker
|
|
BEAST_EXPECT(h["abce"].id() > h["abcd"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abce"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abce"].id());
|
|
|
|
// Switch support from abce to abcd, tie-breaker now needed
|
|
t.remove(h["abce"]);
|
|
t.insert(h["abcd"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id());
|
|
}
|
|
|
|
// Single node larger grand child
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcd"], 2);
|
|
t.insert(h["abcde"], 4);
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcde"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcde"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abcde"].id());
|
|
}
|
|
|
|
// Too much uncommitted support from competing branches
|
|
{
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["abc"]);
|
|
t.insert(h["abcde"], 2);
|
|
t.insert(h["abcfg"], 2);
|
|
// 'de' and 'fg' are tied without 'abc' vote
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abc"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abc"].id());
|
|
|
|
t.remove(h["abc"]);
|
|
t.insert(h["abcd"]);
|
|
|
|
// 'de' branch has 3 votes to 2, so earlier sequences see it as
|
|
// preferred
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abcde"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcde"].id());
|
|
|
|
// However, if you validated a ledger with Seq 5, potentially on
|
|
// a different branch, you do not yet know if they chose abcd
|
|
// or abcf because of you, so abc remains preferred
|
|
BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abc"].id());
|
|
}
|
|
|
|
// Changing largestSeq perspective changes preferred branch
|
|
{
|
|
/** Build the tree below with initial tip support annotated
|
|
A
|
|
/ \
|
|
B(1) C(1)
|
|
/ | |
|
|
H D F(1)
|
|
|
|
|
E(2)
|
|
|
|
|
G
|
|
*/
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
t.insert(h["ab"]);
|
|
t.insert(h["ac"]);
|
|
t.insert(h["acf"]);
|
|
t.insert(h["abde"], 2);
|
|
|
|
// B has more branch support
|
|
BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id());
|
|
// But if you last validated D,F or E, you do not yet know
|
|
// if someone used that validation to commit to B or C
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["a"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id());
|
|
|
|
/** One of E advancing to G doesn't change anything
|
|
A
|
|
/ \
|
|
B(1) C(1)
|
|
/ | |
|
|
H D F(1)
|
|
|
|
|
E(1)
|
|
|
|
|
G(1)
|
|
*/
|
|
t.remove(h["abde"]);
|
|
t.insert(h["abdeg"]);
|
|
|
|
BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["a"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["a"].id());
|
|
|
|
/** C advancing to H does advance the seq 3 preferred ledger
|
|
A
|
|
/ \
|
|
B(1) C
|
|
/ | |
|
|
H(1)D F(1)
|
|
|
|
|
E(1)
|
|
|
|
|
G(1)
|
|
*/
|
|
t.remove(h["ac"]);
|
|
t.insert(h["abh"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["ab"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["ab"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["ab"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["a"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["a"].id());
|
|
|
|
/** F advancing to E also moves the preferred ledger forward
|
|
A
|
|
/ \
|
|
B(1) C
|
|
/ | |
|
|
H(1)D F
|
|
|
|
|
E(2)
|
|
|
|
|
G(1)
|
|
*/
|
|
t.remove(h["acf"]);
|
|
t.insert(h["abde"]);
|
|
BEAST_EXPECT(t.getPreferred(Seq{1})->id == h["abde"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{2})->id == h["abde"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abde"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["ab"].id());
|
|
BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["ab"].id());
|
|
}
|
|
}
|
|
|
|
void
|
|
testRootRelated()
|
|
{
|
|
using namespace csf;
|
|
// Since the root is a special node that breaks the no-single child
|
|
// invariant, do some tests that exercise it.
|
|
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
BEAST_EXPECT(!t.remove(h[""]));
|
|
BEAST_EXPECT(t.branchSupport(h[""]) == 0);
|
|
BEAST_EXPECT(t.tipSupport(h[""]) == 0);
|
|
|
|
t.insert(h["a"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.branchSupport(h[""]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h[""]) == 0);
|
|
|
|
t.insert(h["e"]);
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.branchSupport(h[""]) == 2);
|
|
BEAST_EXPECT(t.tipSupport(h[""]) == 0);
|
|
|
|
BEAST_EXPECT(t.remove(h["e"]));
|
|
BEAST_EXPECT(t.checkInvariants());
|
|
BEAST_EXPECT(t.branchSupport(h[""]) == 1);
|
|
BEAST_EXPECT(t.tipSupport(h[""]) == 0);
|
|
}
|
|
|
|
void
|
|
testStress()
|
|
{
|
|
using namespace csf;
|
|
LedgerTrie<Ledger> t;
|
|
LedgerHistoryHelper h;
|
|
|
|
// Test quasi-randomly add/remove supporting for different ledgers
|
|
// from a branching history.
|
|
|
|
// Ledgers have sequence 1,2,3,4
|
|
std::uint32_t const depthConst = 4;
|
|
// Each ledger has 4 possible children
|
|
std::uint32_t const width = 4;
|
|
|
|
std::uint32_t const iterations = 10000;
|
|
|
|
// Use explicit seed to have same results for CI
|
|
std::mt19937 gen{42};
|
|
std::uniform_int_distribution<> depthDist(0, depthConst - 1);
|
|
std::uniform_int_distribution<> widthDist(0, width - 1);
|
|
std::uniform_int_distribution<> flip(0, 1);
|
|
for (std::uint32_t i = 0; i < iterations; ++i)
|
|
{
|
|
// pick a random ledger history
|
|
std::string curr = "";
|
|
char depth = depthDist(gen);
|
|
char offset = 0;
|
|
for (char d = 0; d < depth; ++d)
|
|
{
|
|
char a = offset + widthDist(gen);
|
|
curr += a;
|
|
offset = (a + 1) * width;
|
|
}
|
|
|
|
// 50-50 to add remove
|
|
if (flip(gen) == 0)
|
|
t.insert(h[curr]);
|
|
else
|
|
t.remove(h[curr]);
|
|
if (!BEAST_EXPECT(t.checkInvariants()))
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
testInsert();
|
|
testRemove();
|
|
testEmpty();
|
|
testSupport();
|
|
testGetPreferred();
|
|
testRootRelated();
|
|
testStress();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(LedgerTrie, consensus, ripple);
|
|
} // namespace test
|
|
} // namespace ripple
|