Merge master (1.1.2) into develop (1.2.0-b8)

This commit is contained in:
Nik Bougalis
2018-12-11 12:48:32 -08:00
6 changed files with 237 additions and 170 deletions

View File

@@ -14,6 +14,41 @@ If you are using Red Hat Enterprise Linux 7 or CentOS 7, you can [update using `
# Releases
## Version 1.1.2
The `rippled` 1.1.2 release introduces a fix for an issue that could have
prevented cluster peers from successfully bypassing connection limits when
connecting to other servers on the same cluster. Additionally, it improves
logic used to determine what the preferred ledger is during suboptimal
network conditions.
**New and Updated Features**
This release has no new features.
**Bug Fixes**
- Properly bypass connection limits for cluster peers (#2795, #2796)
- Improve preferred ledger calculation (#2784)
## Version 1.1.1
The `rippled` 1.1.1 release adds support for redirections when retrieving
validator lists and changes the way that validators with an expired list
behave. Additionally, informational commands return more useful information
to allow server operators to determine the state of their server
**New and Updated Features**
- Enhance status reporting when using the `server_info` and `validators` commands (#2734)
- Accept redirects from validator list sites: (#2715)
**Bug Fixes**
- Properly handle expired validator lists when validating (#2734)
## Version 1.1.0
The `rippled` 1.1.0 release release includes the `DepositPreAuth` amendment, which combined with the previously released `DepositAuth` amendment, allows users to pre-authorize incoming transactions to accounts, by whitelisting sender addresses. The 1.1.0 release also includes incremental improvements to several previously released features (`fix1515` amendment), deprecates support for the `sign` and `sign_for` commands from the rippled API and improves invariant checking for enhanced security.

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

@@ -392,6 +392,11 @@ public:
if (keys_.find (key) != keys_.end())
return Result::duplicate;
// If the peer belongs to a cluster, update the slot to reflect that.
counts_.remove (*slot);
slot->cluster (cluster);
counts_.add (*slot);
// See if we have an open space for this slot
if (! counts_.can_activate (*slot))
{
@@ -401,18 +406,15 @@ public:
return Result::full;
}
// Set key and cluster right before adding to the map
// otherwise we could assert later when erasing the key.
counts_.remove (*slot);
// Set the key right before adding to the map, otherwise we might
// assert later when erasing the key.
slot->public_key (key);
slot->cluster (cluster);
counts_.add (*slot);
// Add the public key to the active set
auto const result = keys_.insert (key);
// Public key must not already exist
assert (result.second);
(void) result.second;
{
auto const result = keys_.insert(key);
// Public key must not already exist
assert (result.second);
(void)result.second;
}
// Change state and update counts
counts_.remove (*slot);

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 {
@@ -138,20 +138,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);
}
}
@@ -188,7 +187,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"]));
@@ -206,7 +205,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
{
@@ -285,17 +283,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);
@@ -336,7 +352,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
@@ -345,18 +360,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
{
@@ -364,17 +389,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
{
@@ -383,40 +408,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
@@ -425,17 +450,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
@@ -443,11 +468,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
@@ -455,25 +480,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
@@ -494,15 +519,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
@@ -518,11 +543,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
@@ -537,11 +562,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
@@ -556,15 +581,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
@@ -610,22 +632,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;
@@ -633,11 +655,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;
}
}
@@ -647,6 +669,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);
}