@@ -76,7 +76,11 @@ namespace test {
of the step, step_one, step_for, and step_until functions
to iterate the network,
- Peer Concept:
+ Peer Requirements:
+
+ Peer should be a lightweight type, cheap to copy
+ and/or move. A good candidate is a simple pointer to
+ the underlying user defined type in the simulation.
Expression Type Requirements
---------- ---- ------------
@@ -87,6 +91,7 @@ namespace test {
u == v bool EqualityComparable
u < v bool LessThanComparable
std::hash class std::hash is defined for P
+ ! u bool true if u is not-a-peer
*/
template
class BasicNetwork
@@ -96,7 +101,7 @@ public:
using clock_type =
beast::manual_clock<
- std::chrono::system_clock>;
+ std::chrono::steady_clock>;
using duration =
typename clock_type::duration;
@@ -129,8 +134,8 @@ private:
struct msg
: by_to_hook, by_from_hook, by_when_hook
{
- Peer* to;
- Peer* from;
+ Peer to;
+ Peer from;
time_point when;
msg (msg const&) = delete;
@@ -138,7 +143,8 @@ private:
virtual ~msg() = default;
virtual void operator()() const = 0;
- msg (Peer* from_, Peer* to_, time_point when_)
+ msg (Peer const& from_, Peer const& to_,
+ time_point when_)
: to(to_), from(from_), when(when_)
{
}
@@ -160,14 +166,14 @@ private:
msg_impl (msg_impl const&) = delete;
msg_impl& operator= (msg_impl const&) = delete;
- msg_impl (Peer* from_, Peer* to_,
+ msg_impl (Peer const& from_, Peer const& to_,
time_point when_, Handler&& h)
: msg (from_, to_, when_)
, h_ (std::move(h))
{
}
- msg_impl (Peer* from_, Peer* to_,
+ msg_impl (Peer const& from_, Peer const& to_,
time_point when_, Handler const& h)
: msg (from_, to_, when_)
, h_ (h)
@@ -197,10 +203,10 @@ private:
boost::intrusive::make_multiset>::type;
- std::unordered_map by_to_;
- std::unordered_map by_from_;
- by_when_set by_when_;
qalloc alloc_;
+ by_when_set by_when_;
+ std::unordered_map by_to_;
+ std::unordered_map by_from_;
public:
using iterator =
@@ -225,14 +231,14 @@ private:
template
typename by_when_set::iterator
- emplace (Peer* from, Peer* to,
+ emplace (Peer const& from, Peer const& to,
time_point when, Handler&& h);
void
erase (iterator iter);
void
- remove (Peer* from, Peer* to);
+ remove (Peer const& from, Peer const& to);
};
struct link_type
@@ -240,8 +246,7 @@ private:
bool inbound;
duration delay;
- link_type (bool inbound_,
- duration delay_)
+ link_type (bool inbound_, duration delay_)
: inbound (inbound_)
, delay (delay_)
{
@@ -249,15 +254,17 @@ private:
};
using links_type =
- boost::container::flat_map;
+ boost::container::flat_map;
class link_transform;
qalloc alloc_;
queue_type queue_;
- clock_type clock_;
+ // VFALCO This is an ugly wart, aged containers
+ // want a non-const reference to a clock.
+ clock_type mutable clock_;
std::mt19937_64 rng_;
- std::unordered_map links_;
+ std::unordered_map links_;
public:
BasicNetwork (BasicNetwork const&) = delete;
@@ -273,6 +280,10 @@ public:
qalloc const&
alloc() const;
+ /** Return the clock. */
+ clock_type&
+ clock() const;
+
/** Return the current network time.
@note The epoch is unspecified
@@ -311,7 +322,7 @@ public:
@return `true` if a new connection was established
*/
bool
- connect (Peer& from, Peer& to,
+ connect (Peer const& from, Peer const& to,
duration const& delay = std::chrono::seconds{0});
/** Break a link.
@@ -327,7 +338,7 @@ public:
@return `true` if a connection was broken.
*/
bool
- disconnect (Peer& peer1, Peer& peer2);
+ disconnect (Peer const& peer1, Peer const& peer2);
/** Return the range of active links.
@@ -335,7 +346,7 @@ public:
*/
boost::transformed_range<
link_transform, links_type>
- links (Peer& from);
+ links (Peer const& from);
/** Send a message to a peer.
@@ -357,7 +368,8 @@ public:
*/
template
void
- send (Peer& from, Peer& to, Function&& f);
+ send (Peer const& from, Peer const& to,
+ Function&& f);
// Used to cancel timers
struct cancel_token;
@@ -409,7 +421,7 @@ public:
*/
template
void
- bfs (Peer& start, Function&& f);
+ bfs (Peer const& start, Function&& f);
/** Run the network for up to one message.
@@ -514,7 +526,7 @@ template
template
auto
BasicNetwork::queue_type::emplace(
- Peer* from, Peer* to, time_point when,
+ Peer const& from, Peer const& to, time_point when,
Handler&& h) ->
typename by_when_set::iterator
{
@@ -554,42 +566,26 @@ BasicNetwork::queue_type::erase(
template
void
BasicNetwork::queue_type::remove(
- Peer* from, Peer* to)
+ Peer const& from, Peer const& to)
{
{
auto& list = by_to_[to];
for(auto iter = list.begin();
iter != list.end();)
{
- if (iter->from == from)
- {
- auto& m = *iter;
- iter = list.erase(iter);
- m.~msg();
- alloc_.dealloc(&m, 1);
- }
- else
- {
- ++iter;
- }
+ auto& m = *iter++;
+ if (m.from == from)
+ erase(by_when_.iterator_to(m));
}
}
{
- auto& list = by_from_[from];
+ auto& list = by_to_[from];
for(auto iter = list.begin();
iter != list.end();)
{
- if (iter->to == to)
- {
- auto& m = *iter;
- iter = list.erase(iter);
- m.~msg();
- alloc_.dealloc(&m, 1);
- }
- else
- {
- ++iter;
- }
+ auto& m = *iter++;
+ if (m.from == to)
+ erase(by_when_.iterator_to(m));
}
}
}
@@ -601,7 +597,7 @@ class BasicNetwork::link_transform
{
private:
BasicNetwork& net_;
- Peer& from_;
+ Peer from_;
public:
using argument_type =
@@ -610,13 +606,14 @@ public:
class result_type
{
public:
- Peer& to;
+ Peer to;
bool inbound;
result_type (result_type const&) = default;
- result_type (BasicNetwork& net, Peer& from,
- Peer& to_, bool inbound_)
+ result_type (BasicNetwork& net,
+ Peer const& from, Peer const& to_,
+ bool inbound_)
: to(to_)
, inbound(inbound_)
, net_(net)
@@ -630,18 +627,19 @@ public:
The connection is removed at both ends.
*/
- void
+ bool
disconnect() const
{
- net_.disconnect(from_, to);
+ return net_.disconnect(from_, to);
}
private:
BasicNetwork& net_;
- Peer& from_;
+ Peer from_;
};
- link_transform (BasicNetwork& net, Peer& from)
+ link_transform (BasicNetwork& net,
+ Peer const& from)
: net_(net)
, from_(from)
{
@@ -651,7 +649,7 @@ public:
operator()(argument_type const& v) const
{
return result_type(net_, from_,
- *v.first, v.second.inbound);
+ v.first, v.second.inbound);
}
};
@@ -702,6 +700,16 @@ BasicNetwork::alloc() const
}
template
+inline
+auto
+BasicNetwork::clock() const ->
+ clock_type&
+{
+ return clock_;
+}
+
+template
+inline
auto
BasicNetwork::now() const ->
time_point
@@ -730,16 +738,17 @@ BasicNetwork::rand(
template
bool
BasicNetwork::connect(
- Peer& from, Peer& to, duration const& delay)
+ Peer const& from, Peer const& to,
+ duration const& delay)
{
- if (&to == &from)
+ if (to == from)
return false;
using namespace std;
- if (! links_[&from].emplace(&to,
+ if (! links_[from].emplace(to,
link_type{ false, delay }).second)
return false;
- auto const result = links_[&to].emplace(
- &from, link_type{ true, delay });
+ auto const result = links_[to].emplace(
+ from, link_type{ true, delay });
(void)result;
assert(result.second);
return true;
@@ -748,27 +757,27 @@ BasicNetwork::connect(
template
bool
BasicNetwork::disconnect(
- Peer& peer1, Peer& peer2)
+ Peer const& peer1, Peer const& peer2)
{
- if (links_[&peer1].erase(&peer2) == 0)
+ if (links_[peer1].erase(peer2) == 0)
return false;
auto const n =
- links_[&peer2].erase(&peer1);
+ links_[peer2].erase(peer1);
(void)n;
assert(n);
- queue_.remove(&peer1, &peer2);
+ queue_.remove(peer1, peer2);
return true;
}
template
inline
auto
-BasicNetwork::links(Peer& from) ->
+BasicNetwork::links(Peer const& from) ->
boost::transformed_range<
link_transform, links_type>
{
return boost::adaptors::transform(
- links_[&from],
+ links_[from],
link_transform{ *this, from });
}
@@ -777,12 +786,13 @@ template
inline
void
BasicNetwork::send(
- Peer& from, Peer& to, Function&& f)
+ Peer const& from, Peer const& to,
+ Function&& f)
{
using namespace std;
auto const iter =
- links_[&from].find(&to);
- queue_.emplace(&from, &to,
+ links_[from].find(to);
+ queue_.emplace(from, to,
clock_.now() + iter->second.delay,
forward(f));
}
@@ -852,14 +862,14 @@ bool
BasicNetwork::step_until(
time_point const& until)
{
- // VFALCO This routine needs optimize
+ // VFALCO This routine needs optimizing
if(queue_.empty())
{
clock_.set(until);
return false;
}
auto iter = queue_.begin();
- if (iter->when > until)
+ if(iter->when > until)
{
clock_.set(until);
return true;
@@ -882,28 +892,27 @@ bool
BasicNetwork::step_for(
std::chrono::duration const& amount)
{
- return step_until(
- clock_.now() + amount);
+ return step_until(now() + amount);
}
template
template
void
BasicNetwork::bfs(
- Peer& start, Function&& f)
+ Peer const& start, Function&& f)
{
- std::deque> q;
- std::unordered_set seen;
- q.emplace_back(&start, 0);
- seen.insert(&start);
+ std::deque> q;
+ std::unordered_set seen;
+ q.emplace_back(start, 0);
+ seen.insert(start);
while(! q.empty())
{
auto v = q.front();
q.pop_front();
- f(v.second, *v.first);
+ f(v.second, v.first);
for(auto const& link : links_[v.first])
{
- auto w = link.first;
+ auto const& w = link.first;
if (seen.count(w) == 0)
{
q.emplace_back(w, v.second + 1);
diff --git a/src/ripple/test/impl/BasicNetwork_test.cpp b/src/ripple/test/impl/BasicNetwork_test.cpp
new file mode 100644
index 0000000000..22b3085ba3
--- /dev/null
+++ b/src/ripple/test/impl/BasicNetwork_test.cpp
@@ -0,0 +1,133 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2015 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace test {
+
+class BasicNetwork_test : public beast::unit_test::suite
+{
+public:
+ struct Peer
+ {
+ int id;
+ std::set set;
+
+ Peer (Peer const&) = default;
+ Peer (Peer&&) = default;
+
+ explicit Peer(int id_)
+ : id(id_)
+ {
+ }
+
+ template
+ void start(Net& net)
+ {
+ using namespace std::chrono_literals;
+ auto t = net.timer(1s,
+ [&]{ set.insert(0); });
+ if (id == 0)
+ {
+ for(auto const& link : net.links(this))
+ net.send(this, link.to,
+ [&, to = link.to]
+ {
+ to->receive(net, this, 1);
+ });
+ }
+ else
+ {
+ net.cancel(t);
+ }
+ }
+
+ template
+ void receive(Net& net, Peer* from, int m)
+ {
+ set.insert(m);
+ ++m;
+ if (m < 5)
+ {
+ for(auto const& link : net.links(this))
+ net.send(this, link.to,
+ [&, mm = m, to = link.to]
+ {
+ to->receive(net, this, mm);
+ });
+ }
+ }
+ };
+
+ void run() override
+ {
+ using namespace std::chrono_literals;
+ std::vector pv;
+ pv.emplace_back(0);
+ pv.emplace_back(1);
+ pv.emplace_back(2);
+ BasicNetwork net;
+ expect(net.rand(0, 1) == 0);
+ expect(! net.connect(&pv[0], &pv[0]));
+ expect(net.connect(&pv[0], &pv[1], 1s));
+ expect(net.connect(&pv[1], &pv[2], 1s));
+ expect(! net.connect(&pv[0], &pv[1]));
+ std::size_t diameter = 0;
+ net.bfs(&pv[0],
+ [&](auto d, Peer*)
+ { diameter = std::max(d, diameter); });
+ expect(diameter == 2);
+ for(auto& peer : pv)
+ peer.start(net);
+ expect(net.step_for(0s));
+ expect(net.step_for(1s));
+ expect(net.step());
+ expect(! net.step());
+ expect(! net.step_for(1s));
+ net.send(&pv[0], &pv[1], []{});
+ net.send(&pv[1], &pv[0], []{});
+ expect(net.disconnect(&pv[0], &pv[1]));
+ expect(! net.disconnect(&pv[0], &pv[1]));
+ for(;;)
+ {
+ auto const links = net.links(&pv[1]);
+ if(links.empty())
+ break;
+ expect(links[0].disconnect());
+ }
+ expect(pv[0].set ==
+ std::set({0, 2, 4}));
+ expect(pv[1].set ==
+ std::set({1, 3}));
+ expect(pv[2].set ==
+ std::set({2, 4}));
+ net.timer(0s, []{});
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(BasicNetwork, test, ripple)
+
+} // test
+} // ripple
+
diff --git a/src/ripple/unity/test.cpp b/src/ripple/unity/test.cpp
index ed85cb1d8b..788c3dd017 100644
--- a/src/ripple/unity/test.cpp
+++ b/src/ripple/unity/test.cpp
@@ -48,4 +48,5 @@
#include
#include
+#include
#include
diff --git a/src/ripple/unity/unl.cpp b/src/ripple/unity/unl.cpp
deleted file mode 100644
index db6b01ff65..0000000000
--- a/src/ripple/unity/unl.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#include
-
-#include
-#include
-#include
diff --git a/src/ripple/unl/README.md b/src/ripple/unl/README.md
deleted file mode 100644
index 22c7824565..0000000000
--- a/src/ripple/unl/README.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# Validators
-
-The Validators module has these responsibilities:
-
-- Provide an administrative interface for maintaining the list _Source_
- locations.
-- Report performance statistics on _Source_ locations
-- Report performance statistics on _validators_ provided by _Source_ locations.
-- Choose a suitable random subset of observed _Validators_ to become the
- _Chosen Validators_ set.
-- Update the _Chosen Validators_ set as needed to meet performance requirements.
-
-## Description
-
-The consensus process used by the Ripple payment protocol requires that ledger
-hashes be signed by _Validators_, producing a _Validation_. The integrity of
-the process is mathematically assured when each node chooses a random subset
-of _Validators_ to trust, where each _Validator_ is a public verifiable entity
-that is independent. Or more specifically, no entity should be in control of
-any significant number of _validators_ chosen by each node.
-
-The list of _Validators_ a node chooses to trust is called the _Chosen
-Validators_. The **Validators** module implements business logic to automate the
-selection of _Chosen Validators_ by allowing the administrator to provide one
-or more trusted _Sources_, from which _Validators_ are learned. Performance
-statistics are tracked for these _Validators_, and the module chooses a
-suitable subset from which to form the _Chosen Validators_ list.
-
-The module looks for these criteria to determine suitability:
-
-- Different validators are not controlled by the same entity.
-- Each validator participates in a majority of ledgers.
-- A validator does not sign ledgers that fail the consensus process.
-
-## Terms
-
-
-
- | Chosen Validators |
- A set of validators chosen by the Validators module. This is the new term
- for what was formerly known as the Unique Node List.
- |
-
-
- | Source |
- A trusted source of validator descriptors. Examples: the rippled
- configuration file, a local text file, or a trusted URL such
- as https://ripple.com/validators.txt.
- |
-
-
- | Validation |
- A closed ledger hash signed by a validator.
- |
-
-
- | Validator |
- A publicly verifiable entity which signs ledger hashes with its private
- key, and makes its public key available through out of band means.
- |
-
-
diff --git a/src/ripple/unl/tests/Consensus_test.cpp b/src/ripple/unl/tests/Consensus_test.cpp
deleted file mode 100644
index dcfa3d1e44..0000000000
--- a/src/ripple/unl/tests/Consensus_test.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-namespace test {
-
-class Consensus_test : public beast::unit_test::suite
-{
-public:
- void
- run()
- {
- Sim4::run(log);
- //Sim3::run(log);
- //Sim2::run(log);
- //Sim1::run(log);
- pass();
- }
-};
-
-BEAST_DEFINE_TESTSUITE_MANUAL(Consensus,sim,ripple);
-
-}
-}
diff --git a/src/ripple/unl/tests/Network_test.cpp b/src/ripple/unl/tests/Network_test.cpp
deleted file mode 100644
index d0bbf90dac..0000000000
--- a/src/ripple/unl/tests/Network_test.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-namespace test {
-
-class Net_test : public beast::unit_test::suite
-{
-public:
- struct Ping
- {
- int hops = 0;
- };
-
- struct InstantPeer
- {
- using Peer = InstantPeer;
- bool set = false;
- int hops = 0;
-
- InstantPeer() = default;
-
- template
- std::chrono::seconds
- delay(Net&) const
- {
- return {};
- }
-
- template
- void
- send (Net& net, Peer& from, Message&& m)
- {
- net.send (from, *this,
- [&, m]() { receive(net, from, m); });
- }
-
- template
- void
- receive (Net& net, Peer& from, Ping p)
- {
- if (set)
- return;
- ++p.hops;
- set = true;
- hops = p.hops;
- for(auto& link : net.links(*this))
- link.to.send(net, *this, p);
- }
- };
-
- struct LatencyPeer
- {
- using Peer = LatencyPeer;
- int hops = 0;
- bool set = false;
-
- LatencyPeer() = default;
-
- template
- std::chrono::milliseconds
- delay(Net& net) const
- {
- using namespace std::chrono;
- return milliseconds(net.rand(5, 200));
- }
-
- template
- void
- send (Net& net, Peer& from, Message&& m)
- {
- net.send (from, *this,
- [&, m]() { receive(net, from, m); });
- }
-
- template
- void
- receive (Net& net, Peer& from, Ping p)
- {
- if (set)
- return;
- ++p.hops;
- set = true;
- hops = p.hops;
- for(auto& link : net.links(*this))
- link.to.send(net, *this, p);
- }
- };
-
- template
- struct Network : BasicNetwork
- {
- static std::size_t const nPeer = 10000;
- static std::size_t const nDegree = 10;
-
- std::vector pv;
- std::mt19937_64 rng;
-
- Network()
- {
- pv.resize(nPeer);
- for (auto& peer : pv)
- for (auto i = 0; i < nDegree; ++i)
- connect_one(peer);
- }
-
- // Return int in range [0, n)
- std::size_t
- rand (std::size_t n)
- {
- return std::uniform_int_distribution<
- std::size_t>(0, n - 1)(rng);
- }
-
- // Return int in range [base, base+n)
- std::size_t
- rand (std::size_t base, std::size_t n)
- {
- return std::uniform_int_distribution<
- std::size_t>(base, base + n - 1)(rng);
- }
-
- // Add one random connection
- void
- connect_one (Peer& peer)
- {
- using namespace std::chrono;
- for(;;)
- if (this->connect(peer, pv[rand(pv.size())],
- peer.delay(*this)))
- break;
- }
- };
-
- template
- void
- testDiameter(std::string const& name)
- {
- using Net = Network;
- using namespace std::chrono;
- log << name << ":";
- Net net;
- net.pv[0].set = true;
- net.pv[0].hops = 0;
- for(auto& link : net.links(net.pv[0]))
- link.to.send(net, net.pv[0], Ping{});
- net.step();
- std::size_t reach = 0;
- std::vector dist;
- std::vector hops;
- std::vector degree;
- for(auto& peer : net.pv)
- {
- hops.resize(std::max(
- peer.hops + 1, hops.size()));
- ++hops[peer.hops];
- }
- net.bfs(net.pv[0],
- [&](std::size_t d, Peer& peer)
- {
- ++reach;
- dist.resize(std::max(
- d + 1, dist.size()));
- ++dist[d];
- auto const n = net.links(peer).size();
- degree.resize(std::max(
- n + 1, degree.size()));
- ++degree[n];
- });
- log << "reach: " << net.pv.size();
- log << "size: " << reach;
- log << "hops: " << seq_string(hops);
- log << "dist: " << seq_string(dist);
- log << "degree: " << seq_string(degree);
- log << "diameter: " << diameter(dist);
- log << "hop diam: " << diameter(hops);
- }
-
- void
- run()
- {
- testDiameter("InstantPeer");
- testDiameter("LatencyPeer");
- pass();
- }
-};
-
-BEAST_DEFINE_TESTSUITE_MANUAL(Net,sim,ripple);
-
-}
-}
diff --git a/src/ripple/unl/tests/Sim1.h b/src/ripple/unl/tests/Sim1.h
deleted file mode 100644
index 6e5bd95e8b..0000000000
--- a/src/ripple/unl/tests/Sim1.h
+++ /dev/null
@@ -1,376 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#ifndef RIPPLE_UNL_SIM1_H_INCLUDED
-#define RIPPLE_UNL_SIM1_H_INCLUDED
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-namespace test {
-
-struct Sim1
-{
- struct Config
- {
- };
-
- static int const nPeer = 100; // # of peers
- static int const nDegree = 10; // outdegree
- static int const nTrial = 10; // number of trials
- static int const nRound = 1; // number of rounds
- static int const nUNLMin = 20;
- static int const nUNLMax = 30;
-
- using clock_type = std::chrono::system_clock;
-
- struct Network;
-
- // A round of consensus.
- // Each round consists of a series of votes,
- // terminating when a supermajority is reached.
- class Round
- {
- public:
- static int const nPercent = 80; // % of agreement
-
- int id_;
- bool consensus_ = false;
- std::unordered_map> pos_;
- std::size_t count_ = 0;
- clock_type::time_point t0_;
-
- public:
- // Create a new round with initial position
- Round (int id, bool value,
- clock_type::time_point now)
- : id_ (id)
- , t0_ (now)
- {
- pos_.emplace(std::make_pair(id,
- std::make_pair(0, value)));
- }
-
- // Returns our value
- bool
- value() const
- {
- auto const iter = pos_.find(id_);
- return iter->second.second;
- }
-
- // Return our position
- // This increments the sequence number
- std::pair
- pos()
- {
- auto const iter = pos_.find(id_);
- return { ++iter->second.first,
- iter->second.second };
- }
-
- // Update a peer's position
- // Return `true` if we should relay
- bool
- receive (int id,
- std::size_t seq, bool value)
- {
- if (id == id_)
- return false;
- auto const result = pos_.emplace(
- std::make_pair(id,
- std::make_pair(seq, value)));
- if (! result.second && seq <=
- result.first->second.first)
- return false;
- result.first->second.first = seq;
- result.first->second.second = value;
- return true;
- }
-
- // Update our position
- // Return `true` if we changed our position
- template
- bool
- update (UNL const& unl,
- clock_type::time_point const& now)
- {
- if (consensus_)
- return false;
- ++count_;
- std::array v;
- v.fill(0);
- for(auto const& p : pos_)
- if (p.first == id_ ||
- unl.count(p.first) > 0)
- ++v[p.second.second];
- using namespace std::chrono;
- auto const iter = pos_.find(id_);
- auto const super =
- ((unl.size() * nPercent) + 50) / 100;
- if (v[0] >= super || v[1] >= super)
- consensus_ = true;
- // agree to disagree
- v[0] += duration_cast(
- now - t0_).count() / 250;
- if (v[0] >= v[1])
- {
- if (iter->second.second != false)
- {
- iter->second.second = false;
- return true;
- }
- }
- else
- {
- if (iter->second.second != true)
- {
- iter->second.second = true;
- return true;
- }
- }
- return false;
- }
- };
-
- //--------------------------------------------------------------------------
-
- class Peer
- {
- private:
- struct PosMsg
- {
- int id;
- std::size_t seq;
- bool value; // position
- };
-
- public:
- int id_;
- std::set unl_;
- Config const& config_;
- boost::optional round_;
- std::chrono::milliseconds delay_;
- Network& net_;
-
- Peer (int id, Config const& config,
- Network& net)
- : id_(id)
- , config_ (config)
- , delay_(std::chrono::milliseconds(
- net.rand(5, 50)))
- , net_(net)
- {
- auto const size = net_.rand(
- nUNLMin, nUNLMax + 1);
- while(unl_.size() < size)
- {
- unl_.insert(net_.rand(nPeer));
- unl_.erase(id_);
- }
- }
-
- // Called to begin the round
- void
- start()
- {
- round_.emplace(id_,
- !(id_%3), net_.now());
- ++round_->count_;
- PosMsg m;
- m.id = id_;
- std::tie(m.seq, m.value) =
- round_->pos();
- broadcast(m);
- using namespace std::chrono;
- net_.timer(milliseconds(
- 700 + net_.rand(700)),
- [=]() { timer(); });
- }
-
- void
- receive (Peer& from, PosMsg const& m)
- {
- if (round_->receive(m.id,
- m.seq, m.value))
- relay(from, m);
- else
- ++net_.dup;
- }
-
- void
- timer()
- {
- if (round_->update(unl_, net_.now()))
- {
- PosMsg m;
- m.id = id_;
- std::tie(m.seq, m.value) =
- round_->pos();
- broadcast(m);
- }
- if (round_->consensus_)
- return;
- using namespace std::chrono;
- net_.timer(milliseconds(700),
- [=]() { timer(); });
- }
-
- //----------------------------------------------------------------------
-
- // Send a message to this peer
- template
- void
- send (Peer& from, Message&& m)
- {
- ++net_.sent;
- using namespace std::chrono;
- net_.send (from, *this,
- [&, m]() { receive(from, m); });
- }
-
- // Broadcast a message to all links
- template
- void
- broadcast (Message const& m)
- {
- for(auto& link : net_.links(*this))
- link.to.send(*this, m);
- }
-
- // Relay a message to all links
- template
- void
- relay (Peer& from, Message const& m)
- {
- for(auto& link : net_.links(*this))
- if (&link.to != &from)
- link.to.send(*this, m);
- }
- };
-
- //--------------------------------------------------------------------------
-
- struct Network : BasicNetwork
- {
- std::size_t dup = 0; // total dup
- std::size_t sent = 0; // total sent
- std::vector pv;
-
- Network (std::size_t seed,
- Config const& config)
- {
- this->rng().seed(seed);
- using namespace std;
- using namespace std::chrono;
- pv.reserve(nPeer);
- for(std::size_t id = 0; id < nPeer; ++id)
- pv.emplace_back(id, config, *this);
- for(auto& peer : pv)
- for(int i = 0; i < nDegree; ++i)
- connect_one(peer);
- }
-
- // Add one random connection
- void
- connect_one(Peer& from)
- {
- using namespace std::chrono;
- auto const delay = from.delay_ +
- milliseconds(rand(5, 200));
- for(;;)
- if (connect(from,
- pv[rand(pv.size())], delay))
- break;
- }
-
- template
- void
- report (std::chrono::milliseconds ms, Log& log)
- {
- std::array n;
- std::vector count;
- n.fill(0);
- std::size_t consensus = 0;
- for(auto const& p : pv)
- {
- ++n[p.round_->value()];
- ++nth(count, p.round_->count_);
- if (p.round_->consensus_)
- ++consensus;
- }
- log <<
- n[1] << "/" << n[0] << ", " <<
- "consensus: " << consensus << " in " <<
- ms.count() << "ms, " <<
- "sent: " << sent << ", " <<
- "dup: " << dup << ", " <<
- "count: " << seq_string(count);
- }
-
- // Execute a round of consensus
- template
- void
- round (Log& log)
- {
- using namespace std::chrono;
- for(int i = 0; i < nPeer; ++i)
- pv[i].start();
- auto const t0 = now();
-#if 0
- do
- {
- report(duration_cast<
- milliseconds>(now() - t0), log);
- }
- while (step_for(milliseconds(50)));
-#else
- step();
-#endif
- report(duration_cast<
- milliseconds>(now() - t0), log);
- }
- };
-
- template
- static
- void
- run (Log& log)
- {
- log << "Sim1" << ":";
- Config config;
- for(auto i = 1; i <= nTrial; ++i)
- {
- Network net(i, config);
- for(auto j = 1; j <= nRound; ++j)
- net.round(log);
- }
- }
-};
-
-} // test
-} // ripple
-
-#endif
diff --git a/src/ripple/unl/tests/Sim2.h b/src/ripple/unl/tests/Sim2.h
deleted file mode 100644
index 5fb8f0ec0c..0000000000
--- a/src/ripple/unl/tests/Sim2.h
+++ /dev/null
@@ -1,434 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#ifndef RIPPLE_UNL_SIM2_H_INCLUDED
-#define RIPPLE_UNL_SIM2_H_INCLUDED
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-namespace test {
-
-template
-struct Sim2
-{
- struct Config
- {
- };
-
- static int const nPeer = 100; // # of peers
- static int const nDegree = 10; // outdegree
- static int const nTrial = 1000000; // number of trials
- static int const nRound = 1; // number of rounds
- static int const nUNLMin = 20;
- static int const nUNLMax = 30;
- static int const nPos = 10;
-
- using NodeKey = int; // identifies a consensus participant
- using ItemKey = int; // identifies a ballot item
- using ItemSet = boost::container::flat_set;
- using clock_type = std::chrono::system_clock;
-
- struct Network;
-
- struct PosMsg
- {
- NodeKey id;
- std::size_t seq;
- ItemSet items;
- bool last;
- };
-
- // A round of consensus.
- // Each round consists of a series of votes,
- // terminating when a supermajority is reached.
- class Round
- {
- private:
- int thresh_ = 50;
-
- public:
- struct Pos
- {
- ItemSet items;
- bool last = false;
- std::size_t seq = 0;
- };
-
- int id_;
- bool failed_ = false;
- bool consensus_ = false;
- std::unordered_map pos_;
- std::size_t count_ = 0;
- clock_type::time_point t0_;
- Log& log_;
-
- public:
- // Create a new round with initial position
- Round (NodeKey id, ItemSet&& pos,
- clock_type::time_point now, Log& log)
- : id_ (id)
- , t0_ (now)
- , log_ (log)
- {
- using namespace std;
- pos_[id].items = std::move(pos);
- }
-
- std::shared_ptr
- posMsg()
- {
- auto const iter = pos_.find(id_);
- auto m = std::make_shared();
- m->id = id_;
- m->seq = ++iter->second.seq;
- m->items = iter->second.items;
- m->last = consensus_;
- return m;
- }
-
- ItemSet const&
- items() const
- {
- return pos_.find(id_)->second.items;
- }
-
- // Update a peer's position
- // Return `true` if we should relay
- bool
- receive (PosMsg const& m)
- {
- if (m.id == id_)
- return false;
- using namespace std;
- auto& pos = pos_[m.id];
- if (m.seq <= pos.seq)
- return false;
- pos.seq = m.seq;
- pos.last = m.last;
- pos.items = m.items;
- return true;
- }
-
- // Update our position
- // Returns `true` if we changed our position
- template
- bool
- update (UNL const& unl,
- clock_type::time_point const& now)
- {
- if (consensus_)
- return false;
- // count votes per item from unl
- boost::container::flat_map<
- ItemKey, std::size_t> votes;
- for(auto const& pos : pos_)
- {
- if (! unl.count(pos.first))
- continue;
- for(auto const& item : pos.second.items)
- {
- auto const result =
- votes.emplace(item, 1);
- if (! result.second)
- ++result.first->second;
- }
- }
- // calculate our new position
- ItemSet items;
- {
- auto const needed =
- (thresh_ * unl.size() + 50) / 100;
- for(auto const& v : votes)
- if (v.second >= needed)
- items.insert(v.first);
- thresh_ += 5;
- }
- // see if we reached a consensus
- std::size_t most = 0;
- std::size_t agree = 0;
- for(auto const& pos : pos_)
- {
- if (! unl.count(pos.first))
- continue;
- if (pos.second.items == items)
- ++agree;
- else if (! pos.second.last)
- ++most;
- }
- //{
- auto const needed =
- (80 * unl.size() + 50) / 100;
- if (agree >= needed)
- {
- consensus_ = true;
- }
- else if (agree + most < needed)
- {
- failed_ = true;
- consensus_ = true;
- }
- //}
- if (now.time_since_epoch() >=
- std::chrono::seconds(7))
- {
- log_ <<
- "agree = " << agree <<
- ", most = " << most <<
- ", needed = " << needed <<
- ", thresh_ = " << thresh_ <<
- ", items.size() = " << items.size();
- }
- auto const iter = pos_.find(id_);
- if (! consensus_ &&
- iter->second.items == items)
- return false;
- iter->second.items = items;
- return true;
- }
- };
-
- //--------------------------------------------------------------------------
-
- class Peer
- {
- private:
- //beast::aged_unordered_map<
-
- public:
- NodeKey id_;
- std::set unl_;
- Config const& config_;
- boost::optional round_;
- std::chrono::milliseconds delay_;
- Network& net_;
-
- Peer (int id, Config const& config,
- Network& net)
- : id_ (id)
- , config_ (config)
- , delay_ (std::chrono::milliseconds(
- net.rand(5, 50)))
- , net_ (net)
- {
- auto const size = 1 + net_.rand(
- nUNLMin, nUNLMax + 1);
- unl_.insert(id_); // self
- while(unl_.size() < size)
- unl_.insert(net_.rand(nPeer));
- }
-
- // Called to begin the round
- void
- start()
- {
- {
- ItemSet pos;
- for(int i = 0; i < nPos; ++i)
- if (net_.rand(2))
- pos.insert(i);
- round_.emplace(id_, std::move(pos),
- net_.now(), net_.log);
- }
- broadcast(round_->posMsg());
- using namespace std::chrono;
- net_.timer(milliseconds(
- 700 + net_.rand(700)),
- [=]() { timer(); });
- }
-
- void
- receive (Peer& from,
- std::shared_ptr const& m)
- {
- if (round_->receive(*m))
- relay(from, m);
- else
- ++net_.dup;
- }
-
- void
- timer()
- {
- if (round_->update(unl_, net_.now()))
- broadcast(round_->posMsg());
- if (round_->consensus_)
- return;
- using namespace std::chrono;
- net_.timer(milliseconds(700),
- [=]() { timer(); });
- }
-
- //----------------------------------------------------------------------
-
- // Send a message to this peer
- template
- void
- send (Peer& from,
- std::shared_ptr const& m)
- {
- ++net_.sent;
- net_.send (from, *this,
- [&, m]() { receive(from, m); });
- }
-
- // Broadcast a message to all links
- template
- void
- broadcast (std::shared_ptr<
- Message const> const& m)
- {
- for(auto& link : net_.links(*this))
- link.to.send(*this, m);
- }
-
- // Relay a message to all links
- template
- void
- relay (Peer& from,
- std::shared_ptr const& m)
- {
- for(auto& link : net_.links(*this))
- if (&link.to != &from)
- link.to.send(*this, m);
- }
- };
-
- //--------------------------------------------------------------------------
-
- struct Network : BasicNetwork
- {
- std::size_t dup = 0; // total dup
- std::size_t sent = 0; // total sent
- std::vector pv;
- Log& log;
-
- Network (std::size_t seed,
- Config const& config, Log& log_)
- : log (log_)
- {
- this->rng.seed(seed);
- using namespace std;
- using namespace std::chrono;
- pv.reserve(nPeer);
- for(std::size_t id = 0; id < nPeer; ++id)
- pv.emplace_back(id, config, *this);
- for(auto& peer : pv)
- for(int i = 0; i < nDegree; ++i)
- connect_one(peer);
- }
-
- // Add one random connection
- void
- connect_one(Peer& from)
- {
- using namespace std::chrono;
- auto const delay = from.delay_ +
- milliseconds(this->rand(5, 200));
- for(;;)
- if (connect(from,
- pv[this->rand(pv.size())], delay))
- break;
- }
-
- void
- report (std::size_t n,
- std::chrono::milliseconds ms, Log& log)
- {
- std::size_t failed = 0;
- std::size_t consensus = 0;
- std::vector hist;
- hist.resize(nPos);
- for(auto const& p : pv)
- {
- hist_accum(hist, p.round_->items());
- if (p.round_->consensus_)
- ++consensus;
- if (p.round_->failed_)
- ++failed;
- }
- log <<
- ((n > 0) ? "#" + std::to_string(n) + " " : "") <<
- seq_string(hist, 3) << " " <<
- "consensus: " << consensus - failed << " in " <<
- ms.count() << "ms, " <<
- "sent: " << sent << ", " <<
- "dup: " << dup;
- }
-
- // Execute a round of consensus
- void
- round (std::size_t n)
- {
- using namespace std::chrono;
- for(int i = 0; i < nPeer; ++i)
- pv[i].start();
- auto const t0 = this->now();
- #if 0
- do
- {
- report(0, duration_cast<
- milliseconds>(now() - t0), log);
- }
- while (this->step_for(milliseconds(50)));
- #else
- this->step();
- #endif
- report(n, duration_cast<
- milliseconds>(this->now() - t0), log);
- }
- };
-
- static
- void
- run (Log& log)
- {
- log << "Sim2" << ":";
- Config config;
- for(auto i = 1; i <= nTrial; ++i)
- {
- //log << "Trial " << i;
- Network net(i, config, log);
- for(auto j = 1; j <= nRound; ++j)
- net.round(i);
- //log << "\n";
- }
- }
-};
-
-} // test
-} // ripple
-
-#endif
-
-/*
-
-Try limiting threshold to 80
-Try slower increase of threshold
-Increase UNL sizes
-
-*/
diff --git a/src/ripple/unl/tests/Sim3.h b/src/ripple/unl/tests/Sim3.h
deleted file mode 100644
index ce2fd2d8c0..0000000000
--- a/src/ripple/unl/tests/Sim3.h
+++ /dev/null
@@ -1,512 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#ifndef RIPPLE_UNL_SIM3_H_INCLUDED
-#define RIPPLE_UNL_SIM3_H_INCLUDED
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-namespace test {
-
-template
-struct Sim3
-{
- struct Config
- {
- int unl;
- int peers = 100;
- int trial = 100;
- };
-
- static int const nDegree = 10; // outdegree
- static int const nItem = 10; // number of items
- static int const nUpdateMS = 700;
-
- using NodeKey = int; // identifies a consensus participant
- using ItemKey = int; // identifies a ballot item
- using ItemSet = boost::container::flat_set;
- using clock_type = std::chrono::system_clock;
- using millis = std::chrono::milliseconds;
-
- class Network;
-
- struct PosMsg
- {
- NodeKey id;
- std::size_t seq;
- ItemSet items;
- bool last;
- };
-
- // A round of consensus.
- // Each round consists of a series of votes,
- // terminating when a supermajority is reached.
- class Round
- {
- private:
- int thresh_ = 50;
-
- public:
- struct Pos
- {
- ItemSet items;
- bool last = false;
- std::size_t seq = 0;
- };
-
- NodeKey id_;
- bool failed_ = false;
- bool consensus_ = false;
- std::unordered_map pos_;
- std::size_t count_ = 0;
- clock_type::time_point t0_;
- Log& log_;
-
- public:
- // Create a new round with initial position
- Round (NodeKey id, ItemSet&& pos,
- clock_type::time_point now, Log& log)
- : id_ (id)
- , t0_ (now)
- , log_ (log)
- {
- using namespace std;
- pos_[id].items = std::move(pos);
- }
-
- std::shared_ptr
- posMsg()
- {
- auto const iter = pos_.find(id_);
- auto m = std::make_shared();
- m->id = id_;
- m->seq = ++iter->second.seq;
- m->items = iter->second.items;
- m->last = consensus_;
- return m;
- }
-
- ItemSet const&
- items() const
- {
- return pos_.find(id_)->second.items;
- }
-
- // Update a peer's position
- // Return `true` if we should relay
- bool
- receive (PosMsg const& m)
- {
- if (m.id == id_)
- return false;
- auto& pos = pos_[m.id];
- if (m.seq <= pos.seq)
- return false;
- pos.seq = m.seq;
- pos.last = m.last;
- pos.items = m.items;
- return true;
- }
-
- // Update our position
- // Returns `true` if we changed our position
- template
- bool
- update (UNL const& unl,
- clock_type::time_point const& now)
- {
- if (consensus_)
- return false;
- // count votes per item from unl
- boost::container::flat_map<
- ItemKey, std::size_t> votes;
- for(auto const& pos : pos_)
- {
- if (! unl.count(pos.first))
- continue;
- for(auto const& item : pos.second.items)
- {
- auto const result =
- votes.emplace(item, 1);
- if (! result.second)
- ++result.first->second;
- }
- }
- // calculate our new position
- ItemSet items;
- {
- auto const needed =
- (thresh_ * unl.size() + 50) / 100;
- for(auto const& v : votes)
- if (v.second >= needed)
- items.insert(v.first);
- #if 1
- thresh_ += 5;
- #endif
- #if 0
- // This causes occasional byzantine
- // failure in a large number of nodes
- if (thresh_ > 80)
- thresh_ = 80;
- #endif
- }
- // see if we reached a consensus
- std::size_t most = 0;
- std::size_t agree = 0;
- for(auto const& pos : pos_)
- {
- if (! unl.count(pos.first))
- continue;
- if (pos.first == id_ ||
- pos.second.items == items)
- ++agree;
- else if (! pos.second.last)
- ++most;
- }
- {
- auto const needed =
- (80 * unl.size() + 50) / 100;
- if (agree >= needed)
- {
- consensus_ = true;
- }
- else if (agree + most < needed)
- {
- failed_ = true;
- consensus_ = true;
- }
- }
- auto const iter = pos_.find(id_);
- if (! consensus_ &&
- iter->second.items == items)
- return false;
- iter->second.items = items;
- return true;
- }
- };
-
- //--------------------------------------------------------------------------
-
- class Peer
- {
- private:
- //beast::aged_unordered_map<
-
- public:
- NodeKey id_;
- std::set unl_;
- Config const& config_;
- boost::optional round_;
- millis delay_;
- Network& net_;
-
- Peer (int id, Config const& config,
- Network& net)
- : id_ (id)
- , config_ (config)
- , delay_ (millis(
- net.rand(5, 50)))
- , net_ (net)
- {
- unl_.insert(id_); // self
- while(unl_.size() <= config_.unl)
- unl_.insert(net_.rand(config_.peers));
- }
-
- // Called to begin the round
- void
- start()
- {
- {
- ItemSet pos;
- for(int i = 0; i < nItem; ++i)
- if (net_.rand(2))
- pos.insert(i);
- round_.emplace(id_, std::move(pos),
- net_.now(), net_.log);
- }
- broadcast(round_->posMsg());
- using namespace std::chrono;
- net_.timer(milliseconds(
- nUpdateMS + net_.rand(nUpdateMS)),
- [=]() { timer(); });
- }
-
- void
- receive (Peer& from,
- std::shared_ptr const& m)
- {
- if (round_->receive(*m))
- relay(from, m);
- else
- ++net_.dup;
- }
-
- void
- timer()
- {
- if (round_->update(unl_, net_.now()))
- broadcast(round_->posMsg());
- if (round_->consensus_)
- return;
- using namespace std::chrono;
- net_.timer(milliseconds(nUpdateMS),
- [=]() { timer(); });
- }
-
- //----------------------------------------------------------------------
-
- // Send a message to this peer
- template
- void
- send (Peer& from,
- std::shared_ptr const& m)
- {
- ++net_.sent;
- net_.send (from, *this,
- [&, m]() { receive(from, m); });
- }
-
- // Broadcast a message to all links
- template
- void
- broadcast (std::shared_ptr<
- Message const> const& m)
- {
- for(auto& link : net_.links(*this))
- link.to.send(*this, m);
- }
-
- // Relay a message to all links
- template
- void
- relay (Peer& from,
- std::shared_ptr const& m)
- {
- for(auto& link : net_.links(*this))
- if (&link.to != &from)
- link.to.send(*this, m);
- }
- };
-
- //--------------------------------------------------------------------------
-
- // The result of one round
- struct Result
- {
- std::size_t elapsed;
- std::size_t failure = 0;
- std::size_t consensus = 0;
- std::set sets;
- };
-
- // The results of several rounds
- struct Results
- {
- std::size_t rounds = 0;
- std::size_t perfect = 0;
- std::vector elapsed;
- std::vector failure;
- std::vector consensus;
-
- void
- aggregate (Result const& result)
- {
- ++rounds;
- perfect += result.sets.size() == 1;
- elapsed.push_back(result.elapsed);
- failure.push_back(result.failure);
- consensus.push_back(result.consensus);
- }
- };
-
- struct Report
- {
- std::size_t perfect;
- std::size_t elapsed_min;
- std::size_t elapsed_max;
-
- Report (Results& results, Config const& config)
- {
- perfect = results.perfect;
-#if 0
- std::sort(
- results.elapsed.begin(), results.elapsed.end());
- std::sort(
- results.consensus.begin(), results.consensus.end(),
- std::greater{});
-#endif
- elapsed_min = results.elapsed.front();
- elapsed_max = results.elapsed.back();
- }
- };
-
- class Network : public BasicNetwork
- {
- private:
- Config const& config_;
-
- public:
- std::size_t dup = 0; // total dup
- std::size_t sent = 0; // total sent
- std::vector pv;
- Log& log;
-
- Network (std::size_t seed,
- Config const& config, Log& log_)
- : config_ (config)
- , log (log_)
- {
- this->rng.seed(seed);
- using namespace std;
- using namespace std::chrono;
- pv.reserve(config.peers);
- for(std::size_t id = 0; id < config_.peers; ++id)
- pv.emplace_back(id, config, *this);
- for(auto& peer : pv)
- for(int i = 0; i < nDegree; ++i)
- connect_one(peer);
- }
-
- // Add one random connection
- void
- connect_one(Peer& from)
- {
- using namespace std::chrono;
- auto const delay = from.delay_ +
- milliseconds(this->rand(5, 200));
- for(;;)
- if (connect(from,
- pv[this->rand(pv.size())], delay))
- break;
- }
-
- // Execute one round of consensus
- Result
- run()
- {
- Result result;
- using namespace std::chrono;
- for(int i = 0; i < config_.peers; ++i)
- pv[i].start();
- auto const t0 = this->now();
- this->step();
- result.elapsed = duration_cast<
- millis>(this->now() - t0).count();
- for(auto const& p : pv)
- {
- if (p.round_->failed_)
- ++result.failure;
- if (p.round_->consensus_)
- {
- ++result.consensus;
- result.sets.insert(p.round_->items());
- }
- }
- return result;
- }
- };
-
- static
- void
- report (Log& log, Result const& result,
- Config const& config)
- {
- log <<
- result.elapsed << "\t" <<
- result.failure << "\t" <<
- result.consensus << "\t" <<
- result.sets.size();
- ;
- }
-
- static
- void
- report (Log& log, Report const& report,
- Config const& config)
- {
- log <<
- report.perfect << "\t" <<
- report.elapsed_min << "\t" <<
- report.elapsed_max << "\t" <<
- config.peers << "\t" <<
- config.unl << "\t" <<
- config.trial
- ;
- }
-
- static
- void
- run (Log& log)
- {
- log << "Sim3" << ":";
-#if 1
- log <<
- "perfect\t" <<
- "elapsed_min\t" <<
- "elapsed_max\t" <<
- "peers\t" <<
- "unl\t" <<
- "trial\t"
- ;
-#else
- log <<
- "elapsed\t" <<
- "failure\t" <<
- "consensus\t" <<
- "positions\t"
- ;
-#endif
- for (int unl = 40; unl > 5; --unl)
- {
- Results results;
- Config config;
- config.unl = unl;
- for(auto i = 1; i <= config.trial; ++i)
- {
- Network net(i, config, log);
- //report(log, net.run(), config);
- results.aggregate(net.run());
- }
- report(log, Report(results, config), config);
- }
- }
-};
-
-} // test
-} // ripple
-
-#endif
-
-/*
-
-Try limiting threshold to 80
-Try slower increase of threshold
-Increase UNL sizes
-
-*/
diff --git a/src/ripple/unl/tests/Sim4.h b/src/ripple/unl/tests/Sim4.h
deleted file mode 100644
index 7504db591f..0000000000
--- a/src/ripple/unl/tests/Sim4.h
+++ /dev/null
@@ -1,607 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#ifndef RIPPLE_UNL_SIM4_H_INCLUDED
-#define RIPPLE_UNL_SIM4_H_INCLUDED
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-namespace test {
-
-template
-struct Sim4
-{
- struct Config
- {
- int unl = 20;
- int peers = 100;
- int trials = 100;
- int rounds = 1;
- };
-
- static int const nDegree = 10; // outdegree
- static int const nItem = 10; // number of items
- enum
- {
- nUpdateMS = 700
- };
-
- using NodeKey = int; // identifies a consensus participant
- using ItemKey = int; // identifies a ballot item
- using ItemSet = boost::container::flat_set;
- using clock_type = std::chrono::system_clock;
- using millis = std::chrono::milliseconds;
-
- struct Network;
-
- struct TxMsg
- {
- ItemKey id;
- };
-
- struct PosMsg
- {
- NodeKey id;
- std::size_t ord;
- std::size_t seq;
- ItemSet items;
- bool last;
- };
-
- // A pool of items
- // This is the equivalent of the "open ledger"
- class Pool
- {
- private:
- ItemSet items_;
-
- public:
- // Insert an item into the pool
- void
- insert (ItemKey id)
- {
- items_.insert(id);
- }
-
- // Returns the items in the pool
- ItemSet const&
- items() const
- {
- return items_;
- }
- };
-
- // A round of consensus.
- // Each round consists of a series of votes,
- // terminating when a supermajority is reached.
- struct Round
- {
- struct Pos
- {
- ItemSet items;
- bool last = false;
- std::size_t seq = 0;
- };
-
- int id_;
- Log& log_;
- std::size_t ord_;
- clock_type::time_point t0_;
-
- int thresh_ = 50;
- bool failed_ = false;
- bool consensus_ = false;
- std::size_t count_ = 0;
- std::unordered_map pos_;
-
- // Create a new round with initial position
- Round (NodeKey id, std::size_t ord,
- ItemSet const& items,
- clock_type::time_point now,
- Log& log)
- : id_ (id)
- , log_ (log)
- , ord_ (ord)
- , t0_ (now)
- {
- using namespace std;
- pos_[id].items = items;
- }
-
- std::shared_ptr
- posMsg()
- {
- auto const iter = pos_.find(id_);
- auto m = std::make_shared();
- m->id = id_;
- m->seq = ++iter->second.seq;
- m->items = iter->second.items;
- m->last = consensus_;
- return m;
- }
-
- ItemSet const&
- items() const
- {
- return pos_.find(id_)->second.items;
- }
-
- // Update a peer's position
- // Return `true` if we should relay
- bool
- receive (PosMsg const& m)
- {
- if (m.id == id_)
- return false;
- using namespace std;
- auto& pos = pos_[m.id];
- if (m.seq <= pos.seq)
- return false;
- pos.seq = m.seq;
- pos.last = m.last;
- pos.items = m.items;
- return true;
- }
-
- // Update our position
- // Returns `true` if we changed our position
- template
- bool
- update (UNL const& unl,
- clock_type::time_point const& now)
- {
- if (consensus_)
- return false;
- // count votes per item from unl
- boost::container::flat_map<
- ItemKey, std::size_t> votes;
- for(auto const& pos : pos_)
- {
- if (! unl.count(pos.first))
- continue;
- for(auto const& item : pos.second.items)
- {
- auto const result =
- votes.emplace(item, 1);
- if (! result.second)
- ++result.first->second;
- }
- }
- // calculate our new position
- ItemSet items;
- {
- auto const needed =
- (thresh_ * unl.size() + 50) / 100;
- for(auto const& v : votes)
- if (v.second >= needed)
- items.insert(v.first);
- thresh_ += 5;
- }
- // see if we reached a consensus
- std::size_t most = 0;
- std::size_t agree = 0;
- for(auto const& pos : pos_)
- {
- if (! unl.count(pos.first))
- continue;
- if (pos.first == id_ ||
- pos.second.items == items)
- ++agree;
- else if (! pos.second.last)
- ++most;
- }
- {
- auto const needed =
- (80 * unl.size() + 50) / 100;
- if (agree >= needed)
- {
- consensus_ = true;
- }
- else if (agree + most < needed)
- {
- failed_ = true;
- consensus_ = true;
- }
- }
- auto const iter = pos_.find(id_);
- if (! consensus_ &&
- iter->second.items == items)
- return false;
- iter->second.items = items;
- return true;
- }
- };
-
- //--------------------------------------------------------------------------
-
- struct Peer
- {
- //beast::aged_unordered_map<
-
- NodeKey id_;
- std::size_t ord_ = 0;
- std::set unl_;
- Config const& config_;
- boost::optional round_;
- millis delay_;
- Network& net_;
- Pool pool_;
- std::unordered_map> item_tab_;
-
- Peer (int id, Config const& config,
- Network& net)
- : id_ (id)
- , config_ (config)
- , delay_ (millis(
- net.rand(5, 50)))
- , net_ (net)
- {
- unl_.insert(id_); // self
- while(unl_.size() <= config_.unl)
- unl_.insert(net_.rand(config_.peers));
- }
-
- void
- init()
- {
- net_.timer(millis(2000),
- [&]() { on_close(); });
- }
-
- // Broadcast a new item
- void
- inject (ItemKey id)
- {
- item_tab_[id].insert(this);
- TxMsg m;
- m.id = id;
- broadcast(m);
- }
-
- // Closes the pool and starts the round
- void
- on_close()
- {
- round_.emplace(id_, ++ord_, pool_.items(),
- net_.now(), net_.log);
- broadcast(round_->posMsg());
- net_.timer(millis(
- nUpdateMS + net_.rand(nUpdateMS)),
- [=]() { on_update(); });
- }
-
- // Updates our position during the round
- void
- on_update()
- {
- if (round_->update(unl_, net_.now()))
- broadcast(round_->posMsg());
- if (round_->consensus_)
- return;
- using namespace std::chrono;
- net_.timer(millis(nUpdateMS),
- [=]() { on_update(); });
- }
-
- // Called when a transaction is received
- void
- receive (Peer& from, TxMsg const& m)
- {
- auto& seen = item_tab_[m.id];
- if(! seen.empty())
- {
- ++net_.dup;
- return;
- }
- seen.insert(&from);
- net_.timer(net_.now() +
- millis(net_.rand(200, 600)),
- [&, m]()
- {
- pool_.insert(m.id);
- for(auto& link : net_.links(*this))
- if (seen.count(&link.to) == 0)
- link.to.send(*this, m);
- });
- }
-
- // Called when a position is received
- void
- receive (Peer& from,
- std::shared_ptr const& m)
- {
- if (round_->receive(*m))
- relay(from, m);
- else
- ++net_.dup;
- }
-
- //----------------------------------------------------------------------
-
- // Send a message to this peer
- template
- void
- send (Peer& from, Message const& m)
- {
- ++net_.sent;
- net_.send (from, *this,
- [&, m]() { receive(from, m); });
- }
-
- // Send a message to this peer
- template
- void
- send (Peer& from,
- std::shared_ptr const& m)
- {
- ++net_.sent;
- net_.send (from, *this,
- [&, m]() { receive(from, m); });
- }
-
- // Broadcast a message to all links
- template
- void
- broadcast (std::shared_ptr<
- Message const> const& m)
- {
- for(auto& link : net_.links(*this))
- link.to.send(*this, m);
- }
-
- // Broadcast a message to all links
- template
- void
- broadcast (Message const& m)
- {
- for(auto& link : net_.links(*this))
- link.to.send(*this, m);
- }
-
- // Relay a message to all links
- template
- void
- relay (Peer& from,
- std::shared_ptr const& m)
- {
- for(auto& link : net_.links(*this))
- if (&link.to != &from)
- link.to.send(*this, m);
- }
-
- // Relay a message to all links
- template
- void
- relay (Peer& from, Message const& m)
- {
- for(auto& link : net_.links(*this))
- if (&link.to != &from)
- link.to.send(*this, m);
- }
- };
-
- //--------------------------------------------------------------------------
-
- // The result of one round
- struct Result
- {
- std::size_t elapsed;
- std::size_t failure = 0;
- std::size_t consensus = 0;
- std::set sets;
- };
-
- // The results of several rounds
- struct Results
- {
- std::size_t rounds = 0;
- std::size_t perfect = 0;
- std::vector elapsed;
- std::vector failure;
- std::vector consensus;
-
- void
- aggregate (Result const& result)
- {
- ++rounds;
- perfect += result.sets.size() == 1;
- elapsed.push_back(result.elapsed);
- failure.push_back(result.failure);
- consensus.push_back(result.consensus);
- }
- };
-
- struct Report
- {
- std::size_t perfect;
- std::size_t elapsed_min;
- std::size_t elapsed_max;
-
- Report (Results& results, Config const& config)
- {
- perfect = results.perfect;
-#if 0
- std::sort(
- results.elapsed.begin(), results.elapsed.end());
- std::sort(
- results.consensus.begin(), results.consensus.end(),
- std::greater{});
-#endif
- elapsed_min = results.elapsed.front();
- elapsed_max = results.elapsed.back();
- }
- };
-
- class Network : public BasicNetwork
- {
- private:
- Config const& config_;
- ItemKey seq_ = 0;
-
- public:
- std::size_t dup = 0; // total dup
- std::size_t sent = 0; // total sent
- std::vector pv;
- Log& log;
-
- Network (std::size_t seed,
- Config const& config, Log& log_)
- : config_ (config)
- , log (log_)
- {
- this->rng().seed(seed);
- using namespace std;
- using namespace std::chrono;
- pv.reserve(config.peers);
- for(std::size_t id = 0; id < config_.peers; ++id)
- pv.emplace_back(id, config, *this);
- for(auto& peer : pv)
- for(int i = 0; i < nDegree; ++i)
- connect_one(peer);
- }
-
- // Add one random connection
- void
- connect_one(Peer& from)
- {
- using namespace std::chrono;
- auto const delay = from.delay_ +
- milliseconds(this->rand(5, 200));
- for(;;)
- if (this->connect(from,
- pv[this->rand(pv.size())], delay))
- break;
- }
-
- void
- report (std::size_t n,
- millis ms, Log& log)
- {
- std::size_t failed = 0;
- std::size_t consensus = 0;
- std::set unique;
- for(auto const& p : pv)
- {
- if (! p.round_)
- continue;
- unique.insert(p.round_->items());
- if (p.round_->consensus_)
- ++consensus;
- if (p.round_->failed_)
- ++failed;
- }
- log <<
- n << "\t" <<
- unique.size() << "\t" <<
- consensus << "\t" <<
- failed << "\t" <<
- ms.count() << "ms\t" <<
- sent << "\t" <<
- dup;
- }
-
- // Inject a random item
- void
- inject()
- {
- pv[this->rand(pv.size())].inject(++seq_);
- }
-
- void
- on_timer()
- {
- inject();
- if(this->now().time_since_epoch() <=
- std::chrono::seconds(4))
- this->timer(millis(250),
- [&]() { on_timer(); });
- }
-
- // Execute a round of consensus
- void
- run (std::size_t n)
- {
- using namespace std::chrono;
- for(int i = 0; i < config_.peers; ++i)
- pv[i].init();
- inject();
- this->timer(millis(250),
- [&]() { on_timer(); });
- auto const t0 = this->now();
- #if 0
- do
- {
- report(n, duration_cast<
- milliseconds>(now() - t0), log);
- }
- while (this->step_for(milliseconds(50)));
- #else
- this->step();
- #endif
- report(n, duration_cast<
- milliseconds>(this->now() - t0), log);
- }
- };
-
- static
- void
- run (Log& log)
- {
- log << "Sim4" << ":";
- log <<
- "n\t" <<
- "unique\t" <<
- "consensus\t" <<
- "failed\t" <<
- "time\t" <<
- "sent\t" <<
- "dup";
- Config config;
- for(auto i = 1; i <= config.trials; ++i)
- {
- Network net(i, config, log);
- for(auto j = 1; j <= config.rounds; ++j)
- net.run(i);
- }
- }
-};
-
-} // test
-} // ripple
-
-#endif
-
-/*
-
-Try limiting threshold to 80
-Try slower increase of threshold
-Increase UNL sizes
-
-*/
diff --git a/src/ripple/unl/tests/SlotPeer_test.cpp b/src/ripple/unl/tests/SlotPeer_test.cpp
deleted file mode 100644
index 609a0785cd..0000000000
--- a/src/ripple/unl/tests/SlotPeer_test.cpp
+++ /dev/null
@@ -1,382 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-namespace test {
-
-struct Config
-{
- static int const nStep = 100; // # of steps
- static int const nPeer = 1001; // # of peers
- static int const nDegree = 10; // outdegree
- static int const nChurn = 5; // churn per step
- static int const nValidator = 100; // # of validators
- static int const nTrusted = 5; // # of trusted validators
- static int const nAllowed = 5; // # of allowed validators
- static int const nTrustedUplinks = 3; // # of uplinks for trusted
- static int const nAllowedUplinks = 1; // # of uplinks for allowed
-
- class Network;
- class Peer;
-
- struct Policy
- {
- struct Slot
- {
- std::unordered_set up;
- std::unordered_set down;
- };
-
- std::unordered_set allowed;
- std::unordered_map slots;
-
- // Returns a slot or nullptr
- template
- Slot*
- get (int id, Peer& from,
- Links const& links)
- {
- auto iter = slots.find(id);
- if (iter != slots.end())
- return &iter->second;
- if (id > nTrusted &&
- allowed.size() >= nAllowed)
- return nullptr;
- if (id > nTrusted)
- allowed.insert(id);
- auto& slot = slots[id];
- for(auto& link : links)
- if (&link.to != &from)
- slot.down.insert(&link.to);
- return &slot;
- }
-
- // Returns `true` accepting Peer as uplink
- bool
- uplink (int id, Peer& from, Slot& slot)
- {
- if (slot.up.count(&from) > 0)
- return true;
- if (id <= nTrusted)
- {
- if (slot.up.size() >= nTrustedUplinks)
- return false;
- slot.up.insert(&from);
- return true;
- }
- if (slot.up.size() >= nAllowedUplinks)
- return false;
- slot.up.insert(&from);
- return true;
- }
-
- // Squelch a downlink
- void
- squelch (int id, Peer& from)
- {
- auto iter = slots.find(id);
- if (iter == slots.end())
- return;
- iter->second.down.erase(&from);
- }
-
- // Unsquelch a downlink
- void
- unsquelch (int id, Peer& from)
- {
- auto iter = slots.find(id);
- if (iter == slots.end())
- return;
- iter->second.down.insert(&from);
- }
-
- // Called when we hear a validation
- void
- heard (int id, int seq)
- {
- }
- };
-
- //--------------------------------------------------------------------------
-
- class Peer
- {
- private:
- Network& net_;
-
- public:
- struct ValMsg
- {
- int id;
- int seq;
- ValMsg (int id_, int seq_)
- : id(id_), seq(seq_) { }
- };
-
- struct SquelchMsg
- {
- int id;
- SquelchMsg (int id_)
- : id(id_) { }
- };
-
- struct UnsquelchMsg
- {
- int id;
- UnsquelchMsg (int id_)
- : id(id_) { }
- };
-
- int id = 0; // validator id or 0
- int seq = 0;
- Policy policy;
- std::map seen;
- std::chrono::milliseconds delay;
-
- Peer (int id_, Network& net)
- : net_ (net)
- , id (id_)
- , delay (std::chrono::milliseconds(
- net.rand(5, 50)))
- {
- }
-
- // Called when a peer disconnects
- void
- disconnect (Peer& from)
- {
- std::vector v;
- for(auto const& item : policy.slots)
- if (item.second.up.count(&from) > 0)
- v.push_back(item.first);
- for(auto id : v)
- for(auto& link : net_.links(*this))
- link.to.send(*this,
- UnsquelchMsg{ id });
- }
-
- // Broadcast a validation
- void
- broadcast()
- {
- broadcast(ValMsg{ id, ++seq });
- }
-
- // Receive a validation
- void
- receive (Peer& from, ValMsg const& m)
- {
- if (m.id == id)
- {
- ++nth(net_.dup, m.id - 1);
- return from.send(*this,
- SquelchMsg(m.id));
- }
- auto slot = policy.get(
- m.id, from, net_.links(*this));
- if (! slot || ! policy.uplink(
- m.id, from, *slot))
- return from.send(*this,
- SquelchMsg(m.id));
- auto& last = seen[m.id];
- if (last >= m.seq)
- {
- ++nth(net_.dup, m.id - 1);
- return;
- }
- last = m.seq;
- policy.heard(m.id, m.seq);
- ++nth(net_.heard, m.id - 1);
- for(auto peer : slot->down)
- peer->send(*this, m);
- }
-
- // Receive a squelch message
- void
- receive (Peer& from, SquelchMsg const& m)
- {
- policy.squelch (m.id, from);
- }
-
- // Receive an unsquelch message
- void
- receive (Peer& from, UnsquelchMsg const& m)
- {
- policy.unsquelch (m.id, from);
- }
-
- //----------------------------------------------------------------------
-
- // Send a message to this peer
- template
- void
- send (Peer& from, Message&& m)
- {
- ++net_.sent;
- using namespace std::chrono;
- net_.send (from, *this,
- [&, m]() { receive(from, m); });
- }
-
- // Broadcast a message to all links
- template
- void
- broadcast (Message const& m)
- {
- for(auto& link : net_.links(*this))
- link.to.send(*this, m);
- }
- };
-
- //--------------------------------------------------------------------------
-
- class Network : public BasicNetwork
- {
- public:
- std::size_t sent = 0;
- std::vector pv;
- std::vector heard;
- std::vector dup;
-
- Network()
- {
- using namespace std;
- using namespace std::chrono;
- pv.reserve(nPeer);
- for(std::size_t id = 1; id <=nPeer; ++id)
- pv.emplace_back(
- id <= nValidator ? id : 0, *this);
- for (auto& peer : pv)
- for (auto i = 0; i < nDegree; ++i)
- connect_one(peer);
- }
-
- // Add one random connection
- void
- connect_one(Peer& from)
- {
- using namespace std::chrono;
- auto const delay = from.delay +
- milliseconds(rand(5, 200));
- for(;;)
- if (connect(from,
- pv[rand(pv.size())], delay))
- break;
- }
-
- // Redo one random connection
- void
- churn_one()
- {
- auto& peer = pv[rand(pv.size())];
- auto const link = links(peer)[
- rand(links(peer).size())];
- link.disconnect();
- link.to.disconnect(peer);
- peer.disconnect(link.to);
- // preserve outbound counts, otherwise
- // the outdegree invariant will break.
- if (link.inbound)
- connect_one (link.to);
- else
- connect_one (peer);
- }
-
- // Redo several random connections
- void
- churn()
- {
- auto n = nChurn;
- while(n--)
- churn_one();
- }
-
- // Iterate the network
- template
- void
- run (Log& log)
- {
- for (int i = nStep; i--;)
- {
- churn();
- for(int j = 0; j < nValidator; ++j)
- pv[j].broadcast();
- step();
- }
- }
- };
-};
-
-//------------------------------------------------------------------------------
-
-class SlotPeer_test : public beast::unit_test::suite
-{
-public:
- void
- test(std::string const& name)
- {
- log << name << ":";
- using Peer = Config::Peer;
- using Network = Config::Network;
- Network net;
- net.run(log);
- std::size_t reach = 0;
- std::vector dist;
- std::vector degree;
- net.bfs(net.pv[0],
- [&](std::size_t d, Peer& peer)
- {
- ++reach;
- ++nth(dist, d);
- ++nth(degree, net.links(peer).size());
- });
- log << "reach: " << net.pv.size();
- log << "size: " << reach;
- log << "sent: " << net.sent;
- log << "diameter: " << diameter(dist);
- log << "dist: " << seq_string(dist);
- log << "heard: " << seq_string(net.heard);
- log << "dup: " << seq_string(net.dup);
- log << "degree: " << seq_string(degree);
- }
-
- void
- run()
- {
- test("SlotPeer");
- pass();
- }
-};
-
-BEAST_DEFINE_TESTSUITE_MANUAL(SlotPeer,sim,ripple);
-
-}
-}
diff --git a/src/ripple/unl/tests/metrics.h b/src/ripple/unl/tests/metrics.h
deleted file mode 100644
index eb1342fe85..0000000000
--- a/src/ripple/unl/tests/metrics.h
+++ /dev/null
@@ -1,100 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#ifndef RIPPLE_SIM_METRICS_H_INCLUDED
-#define RIPPLE_SIM_METRICS_H_INCLUDED
-
-#include
-#include
-#include