mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
Peer to peer network simulator:
* Refine the Peer concept * Remove incremental consensus simulations (coverage) * Add unit test (coverage) * Fix BasicNetwork::remove
This commit is contained in:
committed by
Edward Hennis
parent
0fca91c6c1
commit
49c86768e6
@@ -3594,6 +3594,12 @@
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\shamap\TreeNodeCache.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\test\BasicNetwork.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\test\impl\BasicNetwork_test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\test\impl\ManualTimeKeeper.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
@@ -3910,10 +3916,6 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\unl.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\websocket02.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\websocket04.cpp">
|
||||
@@ -3922,30 +3924,6 @@
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\websocketpp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release|x64'">..\..\src\websocketpp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\BasicNetwork.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\unl\tests\Consensus_test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\metrics.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\unl\tests\Network_test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\Sim1.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\Sim2.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\Sim3.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\Sim4.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\unl\tests\SlotPeer_test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\websocket\AutoSocket.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\websocket\Config04.h">
|
||||
|
||||
@@ -424,12 +424,6 @@
|
||||
<Filter Include="ripple\unity">
|
||||
<UniqueIdentifier>{5DB3CD0B-B361-B301-9562-697CA8A52B68}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ripple\unl">
|
||||
<UniqueIdentifier>{843C622F-AA52-E6C5-D3EB-D4B6D564B395}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ripple\unl\tests">
|
||||
<UniqueIdentifier>{2C910562-8D0A-B115-B829-556779436F2F}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ripple\websocket">
|
||||
<UniqueIdentifier>{44780F86-42D3-2F2B-0846-5AEE2CA6D7FE}</UniqueIdentifier>
|
||||
</Filter>
|
||||
@@ -4245,6 +4239,12 @@
|
||||
<ClInclude Include="..\..\src\ripple\shamap\TreeNodeCache.h">
|
||||
<Filter>ripple\shamap</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\test\BasicNetwork.h">
|
||||
<Filter>ripple\test</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\test\impl\BasicNetwork_test.cpp">
|
||||
<Filter>ripple\test\impl</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\test\impl\ManualTimeKeeper.cpp">
|
||||
<Filter>ripple\test\impl</Filter>
|
||||
</ClCompile>
|
||||
@@ -4539,42 +4539,12 @@
|
||||
<ClCompile Include="..\..\src\ripple\unity\test.cpp">
|
||||
<Filter>ripple\unity</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\unl.cpp">
|
||||
<Filter>ripple\unity</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\websocket02.cpp">
|
||||
<Filter>ripple\unity</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\ripple\unity\websocket04.cpp">
|
||||
<Filter>ripple\unity</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\BasicNetwork.h">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\unl\tests\Consensus_test.cpp">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\metrics.h">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\unl\tests\Network_test.cpp">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\Sim1.h">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\Sim2.h">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\Sim3.h">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\unl\tests\Sim4.h">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\unl\tests\SlotPeer_test.cpp">
|
||||
<Filter>ripple\unl\tests</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\websocket\AutoSocket.h">
|
||||
<Filter>ripple\websocket</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -884,7 +884,6 @@ def get_classic_sources(toolchain):
|
||||
append_sources(result, *list_sources('src/ripple/rpc', '.cpp'))
|
||||
append_sources(result, *list_sources('src/ripple/shamap', '.cpp'))
|
||||
append_sources(result, *list_sources('src/ripple/test', '.cpp'))
|
||||
append_sources(result, *list_sources('src/ripple/unl', '.cpp'))
|
||||
|
||||
if use_shp(toolchain):
|
||||
cc_flags = {'CCFLAGS': ['--system-header-prefix=rocksdb2']}
|
||||
@@ -928,7 +927,6 @@ def get_unity_sources(toolchain):
|
||||
'src/ripple/unity/rpcx.cpp',
|
||||
'src/ripple/unity/shamap.cpp',
|
||||
'src/ripple/unity/test.cpp',
|
||||
'src/ripple/unity/unl.cpp',
|
||||
)
|
||||
|
||||
if use_shp(toolchain):
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_SIM_BASICNETWORK_H_INCLUDED
|
||||
#define RIPPLE_SIM_BASICNETWORK_H_INCLUDED
|
||||
#ifndef RIPPLE_TEST_BASICNETWORK_H_INCLUDED
|
||||
#define RIPPLE_TEST_BASICNETWORK_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/qalloc.h>
|
||||
#include <beast/chrono/manual_clock.h>
|
||||
@@ -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<P> class std::hash is defined for P
|
||||
! u bool true if u is not-a-peer
|
||||
*/
|
||||
template <class Peer>
|
||||
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<msg,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
std::unordered_map<Peer*, by_to_list> by_to_;
|
||||
std::unordered_map<Peer*, by_from_list> by_from_;
|
||||
by_when_set by_when_;
|
||||
qalloc alloc_;
|
||||
by_when_set by_when_;
|
||||
std::unordered_map<Peer, by_to_list> by_to_;
|
||||
std::unordered_map<Peer, by_from_list> by_from_;
|
||||
|
||||
public:
|
||||
using iterator =
|
||||
@@ -225,14 +231,14 @@ private:
|
||||
|
||||
template <class Handler>
|
||||
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<Peer*, link_type>;
|
||||
boost::container::flat_map<Peer, link_type>;
|
||||
|
||||
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<Peer*, links_type> links_;
|
||||
std::unordered_map<Peer, links_type> 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 <class Function>
|
||||
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 <class Function>
|
||||
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 <class Peer>
|
||||
template <class Handler>
|
||||
auto
|
||||
BasicNetwork<Peer>::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<Peer>::queue_type::erase(
|
||||
template <class Peer>
|
||||
void
|
||||
BasicNetwork<Peer>::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<Peer>::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<Peer>::alloc() const
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::clock() const ->
|
||||
clock_type&
|
||||
{
|
||||
return clock_;
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::now() const ->
|
||||
time_point
|
||||
@@ -730,16 +738,17 @@ BasicNetwork<Peer>::rand(
|
||||
template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::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<Peer>::connect(
|
||||
template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::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 <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::links(Peer& from) ->
|
||||
BasicNetwork<Peer>::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 <class Function>
|
||||
inline
|
||||
void
|
||||
BasicNetwork<Peer>::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<Function>(f));
|
||||
}
|
||||
@@ -852,14 +862,14 @@ bool
|
||||
BasicNetwork<Peer>::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<Peer>::step_for(
|
||||
std::chrono::duration<Period, Rep> const& amount)
|
||||
{
|
||||
return step_until(
|
||||
clock_.now() + amount);
|
||||
return step_until(now() + amount);
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
void
|
||||
BasicNetwork<Peer>::bfs(
|
||||
Peer& start, Function&& f)
|
||||
Peer const& start, Function&& f)
|
||||
{
|
||||
std::deque<std::pair<Peer*, std::size_t>> q;
|
||||
std::unordered_set<Peer*> seen;
|
||||
q.emplace_back(&start, 0);
|
||||
seen.insert(&start);
|
||||
std::deque<std::pair<Peer, std::size_t>> q;
|
||||
std::unordered_set<Peer> 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);
|
||||
133
src/ripple/test/impl/BasicNetwork_test.cpp
Normal file
133
src/ripple/test/impl/BasicNetwork_test.cpp
Normal file
@@ -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 <BeastConfig.h>
|
||||
#include <ripple/test/BasicNetwork.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class BasicNetwork_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
struct Peer
|
||||
{
|
||||
int id;
|
||||
std::set<int> set;
|
||||
|
||||
Peer (Peer const&) = default;
|
||||
Peer (Peer&&) = default;
|
||||
|
||||
explicit Peer(int id_)
|
||||
: id(id_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
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 <class Net>
|
||||
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<Peer> pv;
|
||||
pv.emplace_back(0);
|
||||
pv.emplace_back(1);
|
||||
pv.emplace_back(2);
|
||||
BasicNetwork<Peer*> 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<int>({0, 2, 4}));
|
||||
expect(pv[1].set ==
|
||||
std::set<int>({1, 3}));
|
||||
expect(pv[2].set ==
|
||||
std::set<int>({2, 4}));
|
||||
net.timer(0s, []{});
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(BasicNetwork, test, ripple)
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
@@ -48,4 +48,5 @@
|
||||
#include <ripple/test/mao/impl/Net.cpp>
|
||||
#include <ripple/test/mao/impl/Net_test.cpp>
|
||||
|
||||
#include <ripple/test/impl/BasicNetwork_test.cpp>
|
||||
#include <ripple/test/impl/ManualTimeKeeper.cpp>
|
||||
|
||||
@@ -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 <BeastConfig.h>
|
||||
|
||||
#include <ripple/unl/tests/Consensus_test.cpp>
|
||||
#include <ripple/unl/tests/Network_test.cpp>
|
||||
#include <ripple/unl/tests/SlotPeer_test.cpp>
|
||||
@@ -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
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Chosen Validators</td>
|
||||
<td>A set of validators chosen by the Validators module. This is the new term
|
||||
for what was formerly known as the Unique Node List.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Source</td>
|
||||
<td>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.
|
||||
</td></tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Validation</td>
|
||||
<td>A closed ledger hash signed by a validator.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Validator</td>
|
||||
<td>A publicly verifiable entity which signs ledger hashes with its private
|
||||
key, and makes its public key available through out of band means.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -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 <BeastConfig.h>
|
||||
#include <ripple/unl/tests/metrics.h>
|
||||
#include <ripple/unl/tests/Sim1.h>
|
||||
#include <ripple/unl/tests/Sim2.h>
|
||||
#include <ripple/unl/tests/Sim3.h>
|
||||
#include <ripple/unl/tests/Sim4.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Consensus_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
run()
|
||||
{
|
||||
Sim4<log_t>::run(log);
|
||||
//Sim3<log_t>::run(log);
|
||||
//Sim2<log_t>::run(log);
|
||||
//Sim1::run(log);
|
||||
pass();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_MANUAL(Consensus,sim,ripple);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 <BeastConfig.h>
|
||||
#include <ripple/unl/tests/BasicNetwork.h>
|
||||
#include <ripple/unl/tests/metrics.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
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 <class Net>
|
||||
std::chrono::seconds
|
||||
delay(Net&) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Net, class Message>
|
||||
void
|
||||
send (Net& net, Peer& from, Message&& m)
|
||||
{
|
||||
net.send (from, *this,
|
||||
[&, m]() { receive(net, from, m); });
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
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 <class Net>
|
||||
std::chrono::milliseconds
|
||||
delay(Net& net) const
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return milliseconds(net.rand(5, 200));
|
||||
}
|
||||
|
||||
template <class Net, class Message>
|
||||
void
|
||||
send (Net& net, Peer& from, Message&& m)
|
||||
{
|
||||
net.send (from, *this,
|
||||
[&, m]() { receive(net, from, m); });
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
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 <class Peer>
|
||||
struct Network : BasicNetwork<Peer>
|
||||
{
|
||||
static std::size_t const nPeer = 10000;
|
||||
static std::size_t const nDegree = 10;
|
||||
|
||||
std::vector<Peer> 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 <class Peer>
|
||||
void
|
||||
testDiameter(std::string const& name)
|
||||
{
|
||||
using Net = Network<Peer>;
|
||||
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<int> dist;
|
||||
std::vector<int> hops;
|
||||
std::vector<int> degree;
|
||||
for(auto& peer : net.pv)
|
||||
{
|
||||
hops.resize(std::max<std::size_t>(
|
||||
peer.hops + 1, hops.size()));
|
||||
++hops[peer.hops];
|
||||
}
|
||||
net.bfs(net.pv[0],
|
||||
[&](std::size_t d, Peer& peer)
|
||||
{
|
||||
++reach;
|
||||
dist.resize(std::max<std::size_t>(
|
||||
d + 1, dist.size()));
|
||||
++dist[d];
|
||||
auto const n = net.links(peer).size();
|
||||
degree.resize(std::max<std::size_t>(
|
||||
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>("InstantPeer");
|
||||
testDiameter<LatencyPeer>("LatencyPeer");
|
||||
pass();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_MANUAL(Net,sim,ripple);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 <ripple/unl/tests/BasicNetwork.h>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
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<int, std::pair<
|
||||
std::size_t, bool>> 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<std::size_t, bool>
|
||||
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 <class UNL>
|
||||
bool
|
||||
update (UNL const& unl,
|
||||
clock_type::time_point const& now)
|
||||
{
|
||||
if (consensus_)
|
||||
return false;
|
||||
++count_;
|
||||
std::array<std::size_t, 2> 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<milliseconds>(
|
||||
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<int> unl_;
|
||||
Config const& config_;
|
||||
boost::optional<Round> 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 <class Message>
|
||||
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 <class Message>
|
||||
void
|
||||
broadcast (Message const& m)
|
||||
{
|
||||
for(auto& link : net_.links(*this))
|
||||
link.to.send(*this, m);
|
||||
}
|
||||
|
||||
// Relay a message to all links
|
||||
template <class Message>
|
||||
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<Peer>
|
||||
{
|
||||
std::size_t dup = 0; // total dup
|
||||
std::size_t sent = 0; // total sent
|
||||
std::vector<Peer> 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 <class Log>
|
||||
void
|
||||
report (std::chrono::milliseconds ms, Log& log)
|
||||
{
|
||||
std::array<std::size_t, 2> n;
|
||||
std::vector<std::size_t> 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 <class Log>
|
||||
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 <class Log>
|
||||
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
|
||||
@@ -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 <ripple/unl/tests/BasicNetwork.h>
|
||||
#include <beast/container/aged_unordered_map.h>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
template <class Log>
|
||||
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<ItemKey>;
|
||||
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<NodeKey, Pos> 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 const>
|
||||
posMsg()
|
||||
{
|
||||
auto const iter = pos_.find(id_);
|
||||
auto m = std::make_shared<PosMsg>();
|
||||
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 <class UNL>
|
||||
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<NodeKey> unl_;
|
||||
Config const& config_;
|
||||
boost::optional<Round> 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<PosMsg const> 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 <class Message>
|
||||
void
|
||||
send (Peer& from,
|
||||
std::shared_ptr<Message const> const& m)
|
||||
{
|
||||
++net_.sent;
|
||||
net_.send (from, *this,
|
||||
[&, m]() { receive(from, m); });
|
||||
}
|
||||
|
||||
// Broadcast a message to all links
|
||||
template <class Message>
|
||||
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 <class Message>
|
||||
void
|
||||
relay (Peer& from,
|
||||
std::shared_ptr<Message const> const& m)
|
||||
{
|
||||
for(auto& link : net_.links(*this))
|
||||
if (&link.to != &from)
|
||||
link.to.send(*this, m);
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
struct Network : BasicNetwork<Peer>
|
||||
{
|
||||
std::size_t dup = 0; // total dup
|
||||
std::size_t sent = 0; // total sent
|
||||
std::vector<Peer> 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<std::size_t> 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
|
||||
|
||||
*/
|
||||
@@ -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 <ripple/unl/tests/BasicNetwork.h>
|
||||
#include <beast/container/aged_unordered_map.h>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
template <class Log>
|
||||
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<ItemKey>;
|
||||
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<NodeKey, Pos> 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 const>
|
||||
posMsg()
|
||||
{
|
||||
auto const iter = pos_.find(id_);
|
||||
auto m = std::make_shared<PosMsg>();
|
||||
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 <class UNL>
|
||||
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<NodeKey> unl_;
|
||||
Config const& config_;
|
||||
boost::optional<Round> 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<PosMsg const> 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 <class Message>
|
||||
void
|
||||
send (Peer& from,
|
||||
std::shared_ptr<Message const> const& m)
|
||||
{
|
||||
++net_.sent;
|
||||
net_.send (from, *this,
|
||||
[&, m]() { receive(from, m); });
|
||||
}
|
||||
|
||||
// Broadcast a message to all links
|
||||
template <class Message>
|
||||
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 <class Message>
|
||||
void
|
||||
relay (Peer& from,
|
||||
std::shared_ptr<Message const> 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<ItemSet> sets;
|
||||
};
|
||||
|
||||
// The results of several rounds
|
||||
struct Results
|
||||
{
|
||||
std::size_t rounds = 0;
|
||||
std::size_t perfect = 0;
|
||||
std::vector<std::size_t> elapsed;
|
||||
std::vector<std::size_t> failure;
|
||||
std::vector<std::size_t> 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<std::size_t>{});
|
||||
#endif
|
||||
elapsed_min = results.elapsed.front();
|
||||
elapsed_max = results.elapsed.back();
|
||||
}
|
||||
};
|
||||
|
||||
class Network : public BasicNetwork<Peer>
|
||||
{
|
||||
private:
|
||||
Config const& config_;
|
||||
|
||||
public:
|
||||
std::size_t dup = 0; // total dup
|
||||
std::size_t sent = 0; // total sent
|
||||
std::vector<Peer> 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
|
||||
|
||||
*/
|
||||
@@ -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 <ripple/unl/tests/BasicNetwork.h>
|
||||
#include <beast/container/aged_unordered_map.h>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
template <class Log>
|
||||
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<ItemKey>;
|
||||
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<NodeKey, Pos> 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 const>
|
||||
posMsg()
|
||||
{
|
||||
auto const iter = pos_.find(id_);
|
||||
auto m = std::make_shared<PosMsg>();
|
||||
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 <class UNL>
|
||||
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<NodeKey> unl_;
|
||||
Config const& config_;
|
||||
boost::optional<Round> round_;
|
||||
millis delay_;
|
||||
Network& net_;
|
||||
Pool pool_;
|
||||
std::unordered_map<ItemKey,
|
||||
boost::container::flat_set<Peer*>> 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<PosMsg const> const& m)
|
||||
{
|
||||
if (round_->receive(*m))
|
||||
relay(from, m);
|
||||
else
|
||||
++net_.dup;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// Send a message to this peer
|
||||
template <class Message>
|
||||
void
|
||||
send (Peer& from, Message const& m)
|
||||
{
|
||||
++net_.sent;
|
||||
net_.send (from, *this,
|
||||
[&, m]() { receive(from, m); });
|
||||
}
|
||||
|
||||
// Send a message to this peer
|
||||
template <class Message>
|
||||
void
|
||||
send (Peer& from,
|
||||
std::shared_ptr<Message const> const& m)
|
||||
{
|
||||
++net_.sent;
|
||||
net_.send (from, *this,
|
||||
[&, m]() { receive(from, m); });
|
||||
}
|
||||
|
||||
// Broadcast a message to all links
|
||||
template <class Message>
|
||||
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 <class Message>
|
||||
void
|
||||
broadcast (Message const& m)
|
||||
{
|
||||
for(auto& link : net_.links(*this))
|
||||
link.to.send(*this, m);
|
||||
}
|
||||
|
||||
// Relay a message to all links
|
||||
template <class Message>
|
||||
void
|
||||
relay (Peer& from,
|
||||
std::shared_ptr<Message const> const& m)
|
||||
{
|
||||
for(auto& link : net_.links(*this))
|
||||
if (&link.to != &from)
|
||||
link.to.send(*this, m);
|
||||
}
|
||||
|
||||
// Relay a message to all links
|
||||
template <class Message>
|
||||
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<ItemSet> sets;
|
||||
};
|
||||
|
||||
// The results of several rounds
|
||||
struct Results
|
||||
{
|
||||
std::size_t rounds = 0;
|
||||
std::size_t perfect = 0;
|
||||
std::vector<std::size_t> elapsed;
|
||||
std::vector<std::size_t> failure;
|
||||
std::vector<std::size_t> 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<std::size_t>{});
|
||||
#endif
|
||||
elapsed_min = results.elapsed.front();
|
||||
elapsed_max = results.elapsed.back();
|
||||
}
|
||||
};
|
||||
|
||||
class Network : public BasicNetwork<Peer>
|
||||
{
|
||||
private:
|
||||
Config const& config_;
|
||||
ItemKey seq_ = 0;
|
||||
|
||||
public:
|
||||
std::size_t dup = 0; // total dup
|
||||
std::size_t sent = 0; // total sent
|
||||
std::vector<Peer> 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<ItemSet> 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
|
||||
|
||||
*/
|
||||
@@ -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 <BeastConfig.h>
|
||||
#include <ripple/unl/tests/BasicNetwork.h>
|
||||
#include <ripple/unl/tests/metrics.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
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<Peer*> up;
|
||||
std::unordered_set<Peer*> down;
|
||||
};
|
||||
|
||||
std::unordered_set<int> allowed;
|
||||
std::unordered_map<int, Slot> slots;
|
||||
|
||||
// Returns a slot or nullptr
|
||||
template <class Links>
|
||||
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<int, int> 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<int> 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 <class Message>
|
||||
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 <class Message>
|
||||
void
|
||||
broadcast (Message const& m)
|
||||
{
|
||||
for(auto& link : net_.links(*this))
|
||||
link.to.send(*this, m);
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class Network : public BasicNetwork<Peer>
|
||||
{
|
||||
public:
|
||||
std::size_t sent = 0;
|
||||
std::vector<Peer> pv;
|
||||
std::vector<std::size_t> heard;
|
||||
std::vector<std::size_t> 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 <class Log>
|
||||
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<int> dist;
|
||||
std::vector<int> 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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
template <class FwdRange>
|
||||
std::string
|
||||
seq_string (FwdRange const& r, int width = 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
auto iter = std::begin(r);
|
||||
if (iter == std::end(r))
|
||||
return ss.str();
|
||||
ss << std::setw(width) << *iter++;
|
||||
while(iter != std::end(r))
|
||||
ss << ", " <<
|
||||
std::setw(width) << *iter++;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <class FwdRange>
|
||||
typename FwdRange::value_type
|
||||
seq_sum (FwdRange const& r)
|
||||
{
|
||||
typename FwdRange::value_type sum = 0;
|
||||
for (auto const& n : r)
|
||||
sum += n;
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <class RanRange>
|
||||
double
|
||||
diameter (RanRange const& r)
|
||||
{
|
||||
if (r.empty())
|
||||
return 0;
|
||||
if (r.size() == 1)
|
||||
return r.front();
|
||||
auto h0 = *(r.end() - 2);
|
||||
auto h1 = r.back();
|
||||
return (r.size() - 2) +
|
||||
double(h1) / (h0 + h1);
|
||||
}
|
||||
|
||||
template <class Container>
|
||||
typename Container::value_type&
|
||||
nth (Container& c, std::size_t n)
|
||||
{
|
||||
c.resize(std::max(c.size(), n + 1));
|
||||
return c[n];
|
||||
}
|
||||
|
||||
template <class Hist, class FwdRange>
|
||||
void
|
||||
hist_accum (Hist& h, FwdRange const& r)
|
||||
{
|
||||
for(auto const& v : r)
|
||||
++nth(h, v);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class = void>
|
||||
inline
|
||||
std::string
|
||||
pad (std::string s, std::size_t n)
|
||||
{
|
||||
if (s.size() < n)
|
||||
s.insert(0, n - s.size(), ' ');
|
||||
return s;
|
||||
}
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user