Improve preferred ledger calculation:

This changeset ensures the preferred ledger calculation
properly distinguishes the absence of trusted validations
from a preferred ledger which is the genesis ledger.
This commit is contained in:
Brad Chase
2018-11-07 11:54:33 -05:00
committed by Nik Bougalis
parent 72e6005f56
commit bd2a38f584
4 changed files with 189 additions and 160 deletions

View File

@@ -29,7 +29,7 @@
namespace ripple {
/** The tip of a span of ledger ancestry
*/
*/
template <class Ledger>
class SpanTip
{
@@ -461,7 +461,7 @@ public:
newNode->branchSupport = loc->branchSupport;
newNode->children = std::move(loc->children);
assert(loc->children.empty());
for(std::unique_ptr<Node> & child : newNode->children)
for (std::unique_ptr<Node>& child : newNode->children)
child->parent = newNode.get();
// Loc truncates to prefix and newNode is its child
@@ -512,8 +512,7 @@ public:
Seq diffSeq;
std::tie(loc, diffSeq) = find(ledger);
// Cannot erase root
if (loc && loc != root.get())
if (loc)
{
// Must be exact match with tip support
if (diffSeq == loc->span.end() && diffSeq > ledger.seq() &&
@@ -525,7 +524,7 @@ public:
auto const it = seqSupport.find(ledger.seq());
assert(it != seqSupport.end() && it->second >= count);
it->second -= count;
if(it->second == 0)
if (it->second == 0)
seqSupport.erase(it->first);
Node* decNode = loc;
@@ -584,7 +583,8 @@ public:
/** Return the count of branch support for the specific ledger
@param ledger The ledger to lookup
@return The number of entries in the trie for this ledger or a descendant
@return The number of entries in the trie for this ledger or a
descendant
*/
std::uint32_t
branchSupport(Ledger const& ledger) const
@@ -595,8 +595,7 @@ public:
// Check that ledger is is an exact match or proper
// prefix of loc
if (loc && diffSeq > ledger.seq() &&
ledger.seq() < loc->span.end())
if (loc && diffSeq > ledger.seq() && ledger.seq() < loc->span.end())
{
return loc->branchSupport;
}
@@ -659,11 +658,15 @@ public:
@param largestIssued The sequence number of the largest validation
issued by this node.
@return Pair with the sequence number and ID of the preferred ledger
@return Pair with the sequence number and ID of the preferred ledger or
boost::none if no preferred ledger exists
*/
SpanTip<Ledger>
boost::optional<SpanTip<Ledger>>
getPreferred(Seq const largestIssued) const
{
if (empty())
return boost::none;
Node* curr = root.get();
bool done = false;
@@ -699,7 +702,7 @@ public:
uncommitted += uncommittedIt->second;
uncommittedIt++;
}
else // otherwise we jump to the end of the span
else // otherwise we jump to the end of the span
nextSeq = curr->span.end();
}
// We did not consume the entire span, so we have found the
@@ -727,8 +730,10 @@ public:
curr->children.end(),
[](std::unique_ptr<Node> const& a,
std::unique_ptr<Node> const& b) {
return std::make_tuple(a->branchSupport, a->span.startID()) >
std::make_tuple(b->branchSupport, b->span.startID());
return std::make_tuple(
a->branchSupport, a->span.startID()) >
std::make_tuple(
b->branchSupport, b->span.startID());
});
best = curr->children[0].get();
@@ -752,6 +757,14 @@ public:
return curr->span.tip();
}
/** Return whether the trie is tracking any ledgers
*/
bool
empty() const
{
return !root || root->branchSupport == 0;
}
/** Dump an ascii representation of the trie to the stream
*/
void
@@ -761,7 +774,7 @@ public:
}
/** Dump JSON representation of trie state
*/
*/
Json::Value
getJson() const
{
@@ -798,7 +811,7 @@ public:
for (auto const& child : curr->children)
{
if(child->parent != curr)
if (child->parent != curr)
return false;
support += child->branchSupport;

View File

@@ -87,6 +87,7 @@ class SeqEnforcer
using time_point = std::chrono::steady_clock::time_point;
Seq seq_{0};
time_point when_;
public:
/** Try advancing the largest observed validation ledger sequence
@@ -101,11 +102,11 @@ public:
@return Whether the validation satisfies the invariant
*/
bool
operator()(time_point now, Seq s, ValidationParms const & p)
operator()(time_point now, Seq s, ValidationParms const& p)
{
if(now > (when_ + p.validationSET_EXPIRES))
if (now > (when_ + p.validationSET_EXPIRES))
seq_ = Seq{0};
if(s <= seq_)
if (s <= seq_)
return false;
seq_ = s;
when_ = now;
@@ -147,7 +148,6 @@ isCurrent(
(seenTime < (now + p.validationCURRENT_LOCAL)));
}
/** Status of newly received validation
*/
enum class ValStatus {
@@ -233,8 +233,8 @@ to_string(ValStatus m)
// Whether this is a full or partial validation
bool full() const;
// Identifier for this node that remains fixed even when rotating signing
// keys
// Identifier for this node that remains fixed even when rotating
// signing keys
NodeID nodeID() const;
implementation_specific_t
@@ -312,7 +312,7 @@ class Validations
hash_map<NodeID, Ledger> lastLedger_;
// Set of ledgers being acquired from the network
hash_map<std::pair<Seq,ID>, hash_set<NodeID>> acquiring_;
hash_map<std::pair<Seq, ID>, hash_set<NodeID>> acquiring_;
// Parameters to determine validation staleness
ValidationParms const parms_;
@@ -327,7 +327,8 @@ private:
removeTrie(ScopedLock const&, NodeID const& nodeID, Validation const& val)
{
{
auto it = acquiring_.find(std::make_pair(val.seq(), val.ledgerID()));
auto it =
acquiring_.find(std::make_pair(val.seq(), val.ledgerID()));
if (it != acquiring_.end())
{
it->second.erase(nodeID);
@@ -387,7 +388,8 @@ private:
@param lock Existing lock of mutex_
@param nodeID The node identifier of the validating node
@param val The trusted validation issued by the node
@param prior If not none, the last current validated ledger Seq,ID of key
@param prior If not none, the last current validated ledger Seq,ID of
key
*/
void
updateTrie(
@@ -445,7 +447,7 @@ private:
withTrie(ScopedLock const& lock, F&& f)
{
// Call current to flush any stale validations
current(lock, [](auto){}, [](auto, auto){});
current(lock, [](auto) {}, [](auto, auto) {});
checkAcquired(lock);
return f(trie_);
}
@@ -537,8 +539,8 @@ public:
}
/** Return the adaptor instance
*/
Adaptor const &
*/
Adaptor const&
adaptor() const
{
return adaptor_;
@@ -552,8 +554,8 @@ public:
return parms_;
}
/** Return whether the local node can issue a validation for the given sequence
number
/** Return whether the local node can issue a validation for the given
sequence number
@param s The sequence number of the ledger the node wants to validate
@return Whether the validation satisfies the invariant, updating the
@@ -602,7 +604,7 @@ public:
Validation& oldVal = ins.first->second;
if (val.signTime() > oldVal.signTime())
{
std::pair<Seq,ID> old(oldVal.seq(),oldVal.ledgerID());
std::pair<Seq, ID> old(oldVal.seq(), oldVal.ledgerID());
adaptor_.onStale(std::move(oldVal));
ins.first->second = val;
if (val.trusted())
@@ -691,20 +693,19 @@ public:
@param curr The local node's current working ledger
@return The sequence and id of the preferred working ledger,
or Seq{0},ID{0} if no trusted validations are available to
or boost::none if no trusted validations are available to
determine the preferred ledger.
*/
std::pair<Seq, ID>
boost::optional<std::pair<Seq, ID>>
getPreferred(Ledger const& curr)
{
ScopedLock lock{mutex_};
SpanTip<Ledger> preferred =
boost::optional<SpanTip<Ledger>> preferred =
withTrie(lock, [this](LedgerTrie<Ledger>& trie) {
return trie.getPreferred(localSeqEnforcer_.largest());
});
// No trusted validations to determine branch
if (preferred.seq == Seq{0})
if (!preferred)
{
// fall back to majority over acquiring ledgers
auto it = std::max_element(
@@ -722,26 +723,26 @@ public:
return std::tie(aSize, aKey.second) <
std::tie(bSize, bKey.second);
});
if(it != acquiring_.end())
if (it != acquiring_.end())
return it->first;
return std::make_pair(preferred.seq, preferred.id);
return boost::none;
}
// If we are the parent of the preferred ledger, stick with our
// current ledger since we might be about to generate it
if (preferred.seq == curr.seq() + Seq{1} &&
preferred.ancestor(curr.seq()) == curr.id())
if (preferred->seq == curr.seq() + Seq{1} &&
preferred->ancestor(curr.seq()) == curr.id())
return std::make_pair(curr.seq(), curr.id());
// A ledger ahead of us is preferred regardless of whether it is
// a descendant of our working ledger or it is on a different chain
if (preferred.seq > curr.seq())
return std::make_pair(preferred.seq, preferred.id);
if (preferred->seq > curr.seq())
return std::make_pair(preferred->seq, preferred->id);
// Only switch to earlier or same sequence number
// if it is a different chain.
if (curr[preferred.seq] != preferred.id)
return std::make_pair(preferred.seq, preferred.id);
if (curr[preferred->seq] != preferred->id)
return std::make_pair(preferred->seq, preferred->id);
// Stick with current ledger
return std::make_pair(curr.seq(), curr.id());
@@ -759,22 +760,21 @@ public:
ID
getPreferred(Ledger const& curr, Seq minValidSeq)
{
std::pair<Seq, ID> preferred = getPreferred(curr);
if(preferred.first >= minValidSeq && preferred.second != ID{0})
return preferred.second;
boost::optional<std::pair<Seq, ID>> preferred = getPreferred(curr);
if (preferred && preferred->first >= minValidSeq)
return preferred->second;
return curr.id();
}
/** Determine the preferred last closed ledger for the next consensus round.
Called before starting the next round of ledger consensus to determine the
preferred working ledger. Uses the dominant peerCount ledger if no
Called before starting the next round of ledger consensus to determine
the preferred working ledger. Uses the dominant peerCount ledger if no
trusted validations are available.
@param lcl Last closed ledger by this node
@param minSeq Minimum allowed sequence number of the trusted preferred ledger
@param minSeq Minimum allowed sequence number of the trusted preferred
ledger
@param peerCounts Map from ledger ids to count of peers with that as the
last closed ledger
@return The preferred last closed ledger ID
@@ -784,15 +784,16 @@ public:
*/
ID
getPreferredLCL(
Ledger const & lcl,
Ledger const& lcl,
Seq minSeq,
hash_map<ID, std::uint32_t> const& peerCounts)
{
std::pair<Seq, ID> preferred = getPreferred(lcl);
boost::optional<std::pair<Seq, ID>> preferred = getPreferred(lcl);
// Trusted validations exist
if (preferred.second != ID{0} && preferred.first > Seq{0})
return (preferred.first >= minSeq) ? preferred.second : lcl.id();
// Trusted validations exist, but stick with local preferred ledger if
// preferred is in the past
if (preferred)
return (preferred->first >= minSeq) ? preferred->second : lcl.id();
// Otherwise, rely on peer ledgers
auto it = std::max_element(

View File

@@ -18,9 +18,9 @@
//==============================================================================
#include <ripple/beast/unit_test.h>
#include <ripple/consensus/LedgerTrie.h>
#include <random>
#include <test/csf/ledgers.h>
#include <unordered_map>
#include <random>
namespace ripple {
namespace test {
@@ -29,7 +29,6 @@ class LedgerTrie_test : public beast::unit_test::suite
{
beast::Journal j;
void
testInsert()
{
@@ -141,20 +140,19 @@ class LedgerTrie_test : public beast::unit_test::suite
{
LedgerTrie<Ledger> t;
LedgerHistoryHelper h;
t.insert(h["ab"],4);
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);
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);
}
}
@@ -191,7 +189,7 @@ class LedgerTrie_test : public beast::unit_test::suite
{
LedgerTrie<Ledger> t;
LedgerHistoryHelper h;
t.insert(h["abc"],2);
t.insert(h["abc"], 2);
BEAST_EXPECT(t.tipSupport(h["abc"]) == 2);
BEAST_EXPECT(t.remove(h["abc"]));
@@ -209,7 +207,6 @@ class LedgerTrie_test : public beast::unit_test::suite
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
{
@@ -288,17 +285,35 @@ class LedgerTrie_test : public beast::unit_test::suite
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;
using Seq = Ledger::Seq;
LedgerTrie<Ledger> t;
LedgerHistoryHelper h;
BEAST_EXPECT(t.tipSupport(h["a"]) == 0);
@@ -339,7 +354,6 @@ class LedgerTrie_test : public beast::unit_test::suite
BEAST_EXPECT(t.branchSupport(h["ab"]) == 1);
BEAST_EXPECT(t.branchSupport(h["abc"]) == 0);
BEAST_EXPECT(t.branchSupport(h["abe"]) == 1);
}
void
@@ -348,18 +362,28 @@ class LedgerTrie_test : public beast::unit_test::suite
using namespace csf;
using Seq = Ledger::Seq;
// Empty
{
LedgerTrie<Ledger> t;
BEAST_EXPECT(t.getPreferred(Seq{0}) == boost::none);
BEAST_EXPECT(t.getPreferred(Seq{2}) == boost::none);
}
// Genesis support is NOT empty
{
LedgerTrie<Ledger> t;
LedgerHistoryHelper h;
BEAST_EXPECT(t.getPreferred(Seq{0}).id == h[""].id());
BEAST_EXPECT(t.getPreferred(Seq{2}).id == h[""].id());
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}) == boost::none);
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());
BEAST_EXPECT(t.getPreferred(Seq{3})->id == h["abc"].id());
}
// Single node smaller child support
{
@@ -367,17 +391,17 @@ class LedgerTrie_test : public beast::unit_test::suite
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());
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());
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
{
@@ -386,40 +410,40 @@ class LedgerTrie_test : public beast::unit_test::suite
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());
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());
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["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());
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());
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);
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());
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());
BEAST_EXPECT(t.getPreferred(Seq{4})->id == h["abcd"].id());
}
// Tie-breaker not needed
@@ -428,17 +452,17 @@ class LedgerTrie_test : public beast::unit_test::suite
LedgerHistoryHelper h;
t.insert(h["abc"]);
t.insert(h["abcd"]);
t.insert(h["abce"],2);
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());
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());
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
@@ -446,11 +470,11 @@ class LedgerTrie_test : public beast::unit_test::suite
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());
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
@@ -458,25 +482,25 @@ class LedgerTrie_test : public beast::unit_test::suite
LedgerTrie<Ledger> t;
LedgerHistoryHelper h;
t.insert(h["abc"]);
t.insert(h["abcde"],2);
t.insert(h["abcfg"],2);
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());
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());
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());
BEAST_EXPECT(t.getPreferred(Seq{5})->id == h["abc"].id());
}
// Changing largestSeq perspective changes preferred branch
@@ -497,15 +521,15 @@ class LedgerTrie_test : public beast::unit_test::suite
t.insert(h["ab"]);
t.insert(h["ac"]);
t.insert(h["acf"]);
t.insert(h["abde"],2);
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());
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());
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
@@ -521,11 +545,11 @@ class LedgerTrie_test : public beast::unit_test::suite
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());
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
@@ -540,11 +564,11 @@ class LedgerTrie_test : public beast::unit_test::suite
*/
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());
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
@@ -559,15 +583,12 @@ class LedgerTrie_test : public beast::unit_test::suite
*/
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());
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
@@ -613,22 +634,22 @@ class LedgerTrie_test : public beast::unit_test::suite
// Ledgers have sequence 1,2,3,4
std::uint32_t const depth = 4;
// Each ledger has 4 possible children
std::uint32_t const width = 4;
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, depth-1);
std::uniform_int_distribution<> widthDist(0, width-1);
std::mt19937 gen{42};
std::uniform_int_distribution<> depthDist(0, depth - 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)
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)
for (char d = 0; d < depth; ++d)
{
char a = offset + widthDist(gen);
curr += a;
@@ -636,11 +657,11 @@ class LedgerTrie_test : public beast::unit_test::suite
}
// 50-50 to add remove
if(flip(gen) == 0)
if (flip(gen) == 0)
t.insert(h[curr]);
else
t.remove(h[curr]);
if(!BEAST_EXPECT(t.checkInvariants()))
if (!BEAST_EXPECT(t.checkInvariants()))
return;
}
}
@@ -650,6 +671,7 @@ class LedgerTrie_test : public beast::unit_test::suite
{
testInsert();
testRemove();
testEmpty();
testSupport();
testGetPreferred();
testRootRelated();

View File

@@ -58,9 +58,7 @@ class Validations_test : public beast::unit_test::suite
boost::optional<std::uint32_t> loadFee_;
public:
Node(PeerID nodeID, clock_type const& c)
: c_(c)
, nodeID_(nodeID)
Node(PeerID nodeID, clock_type const& c) : c_(c), nodeID_(nodeID)
{
}
@@ -292,7 +290,7 @@ class Validations_test : public beast::unit_test::suite
}
};
Ledger const genesisLedger{Ledger::MakeGenesis{}};
Ledger const genesisLedger{Ledger::MakeGenesis{}};
void
testAddValidation()
@@ -349,8 +347,7 @@ class Validations_test : public beast::unit_test::suite
BEAST_EXPECT(
ValStatus::badSeq == harness.add(n.validate(ledgerAB)));
// Cannot send the same partial validation sequence
BEAST_EXPECT(
ValStatus::badSeq == harness.add(n.partial(ledgerAB)));
BEAST_EXPECT(ValStatus::badSeq == harness.add(n.partial(ledgerAB)));
// Now trusts the newest ledger too
harness.clock().advance(1s);
@@ -425,9 +422,8 @@ class Validations_test : public beast::unit_test::suite
TestHarness harness(h.oracle);
Node n = harness.makeNode();
auto process = [&](Ledger & lgr)
{
if(doFull)
auto process = [&](Ledger& lgr) {
if (doFull)
return harness.add(n.validate(lgr));
return harness.add(n.partial(lgr));
};
@@ -458,7 +454,6 @@ class Validations_test : public beast::unit_test::suite
Ledger ledgerA = h["a"];
Ledger ledgerAB = h["ab"];
using Trigger = std::function<void(TestValidations&)>;
std::vector<Trigger> triggers = {
@@ -492,8 +487,7 @@ class Validations_test : public beast::unit_test::suite
BEAST_EXPECT(
harness.vals().getNodesAfter(ledgerA, ledgerA.id()) == 0);
BEAST_EXPECT(
harness.vals().getPreferred(genesisLedger) ==
std::make_pair(Ledger::Seq{0}, Ledger::ID{0}));
harness.vals().getPreferred(genesisLedger) == boost::none);
}
}
@@ -611,8 +605,7 @@ class Validations_test : public beast::unit_test::suite
ValStatus::current == harness.add(node.validate(ledgerA)));
{
hash_set<PeerID> const expectedKeys = {a.nodeID(),
b.nodeID()};
hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
}
@@ -627,8 +620,7 @@ class Validations_test : public beast::unit_test::suite
ValStatus::current == harness.add(node.partial(ledgerAC)));
{
hash_set<PeerID> const expectedKeys = {a.nodeID(),
b.nodeID()};
hash_set<PeerID> const expectedKeys = {a.nodeID(), b.nodeID()};
BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
}
@@ -695,7 +687,6 @@ class Validations_test : public beast::unit_test::suite
BEAST_EXPECT(
sorted(harness.vals().fees(id, baseFee)) ==
sorted(expectedFees));
}
};
@@ -836,8 +827,7 @@ class Validations_test : public beast::unit_test::suite
};
// Empty (no ledgers)
BEAST_EXPECT(
harness.vals().getPreferred(ledgerA) == pref(genesisLedger));
BEAST_EXPECT(harness.vals().getPreferred(ledgerA) == boost::none);
// Single ledger
BEAST_EXPECT(ValStatus::current == harness.add(a.validate(ledgerB)));
@@ -1069,8 +1059,12 @@ class Validations_test : public beast::unit_test::suite
BEAST_EXPECT(
vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) ==
trustedVals.size());
BEAST_EXPECT(
vals.getPreferred(this->genesisLedger).second == testID);
if (trustedVals.empty())
BEAST_EXPECT(
vals.getPreferred(this->genesisLedger) == boost::none);
else
BEAST_EXPECT(
vals.getPreferred(this->genesisLedger)->second == testID);
BEAST_EXPECT(vals.getTrustedForLedger(testID) == trustedVals);
BEAST_EXPECT(
vals.numTrustedForLedger(testID) == trustedVals.size());
@@ -1127,7 +1121,7 @@ class Validations_test : public beast::unit_test::suite
auto& vals = harness.vals();
BEAST_EXPECT(vals.currentTrusted() == trustedVals);
BEAST_EXPECT(
vals.getPreferred(genesisLedger).second == v.ledgerID());
vals.getPreferred(genesisLedger)->second == v.ledgerID());
BEAST_EXPECT(
vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
@@ -1136,8 +1130,7 @@ class Validations_test : public beast::unit_test::suite
// make acquiring ledger available
h["ab"];
BEAST_EXPECT(vals.currentTrusted() == trustedVals);
BEAST_EXPECT(
vals.getPreferred(genesisLedger).second == genesisLedger.id());
BEAST_EXPECT(vals.getPreferred(genesisLedger) == boost::none);
BEAST_EXPECT(
vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
}