mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +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>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\shamap\TreeNodeCache.h">
|
<ClInclude Include="..\..\src\ripple\shamap\TreeNodeCache.h">
|
||||||
</ClInclude>
|
</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">
|
<ClCompile Include="..\..\src\ripple\test\impl\ManualTimeKeeper.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|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)'=='debug.classic|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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 Include="..\..\src\ripple\unity\websocket02.cpp">
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\unity\websocket04.cpp">
|
<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.classic|x64'">..\..\src\websocketpp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release|x64'">..\..\src\websocketpp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release|x64'">..\..\src\websocketpp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</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 Include="..\..\src\ripple\websocket\AutoSocket.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\websocket\Config04.h">
|
<ClInclude Include="..\..\src\ripple\websocket\Config04.h">
|
||||||
|
|||||||
@@ -424,12 +424,6 @@
|
|||||||
<Filter Include="ripple\unity">
|
<Filter Include="ripple\unity">
|
||||||
<UniqueIdentifier>{5DB3CD0B-B361-B301-9562-697CA8A52B68}</UniqueIdentifier>
|
<UniqueIdentifier>{5DB3CD0B-B361-B301-9562-697CA8A52B68}</UniqueIdentifier>
|
||||||
</Filter>
|
</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">
|
<Filter Include="ripple\websocket">
|
||||||
<UniqueIdentifier>{44780F86-42D3-2F2B-0846-5AEE2CA6D7FE}</UniqueIdentifier>
|
<UniqueIdentifier>{44780F86-42D3-2F2B-0846-5AEE2CA6D7FE}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
@@ -4245,6 +4239,12 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\shamap\TreeNodeCache.h">
|
<ClInclude Include="..\..\src\ripple\shamap\TreeNodeCache.h">
|
||||||
<Filter>ripple\shamap</Filter>
|
<Filter>ripple\shamap</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClCompile Include="..\..\src\ripple\test\impl\ManualTimeKeeper.cpp">
|
||||||
<Filter>ripple\test\impl</Filter>
|
<Filter>ripple\test\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -4539,42 +4539,12 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\unity\test.cpp">
|
<ClCompile Include="..\..\src\ripple\unity\test.cpp">
|
||||||
<Filter>ripple\unity</Filter>
|
<Filter>ripple\unity</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\unity\unl.cpp">
|
|
||||||
<Filter>ripple\unity</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\..\src\ripple\unity\websocket02.cpp">
|
<ClCompile Include="..\..\src\ripple\unity\websocket02.cpp">
|
||||||
<Filter>ripple\unity</Filter>
|
<Filter>ripple\unity</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\unity\websocket04.cpp">
|
<ClCompile Include="..\..\src\ripple\unity\websocket04.cpp">
|
||||||
<Filter>ripple\unity</Filter>
|
<Filter>ripple\unity</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClInclude Include="..\..\src\ripple\websocket\AutoSocket.h">
|
||||||
<Filter>ripple\websocket</Filter>
|
<Filter>ripple\websocket</Filter>
|
||||||
</ClInclude>
|
</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/rpc', '.cpp'))
|
||||||
append_sources(result, *list_sources('src/ripple/shamap', '.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/test', '.cpp'))
|
||||||
append_sources(result, *list_sources('src/ripple/unl', '.cpp'))
|
|
||||||
|
|
||||||
if use_shp(toolchain):
|
if use_shp(toolchain):
|
||||||
cc_flags = {'CCFLAGS': ['--system-header-prefix=rocksdb2']}
|
cc_flags = {'CCFLAGS': ['--system-header-prefix=rocksdb2']}
|
||||||
@@ -928,7 +927,6 @@ def get_unity_sources(toolchain):
|
|||||||
'src/ripple/unity/rpcx.cpp',
|
'src/ripple/unity/rpcx.cpp',
|
||||||
'src/ripple/unity/shamap.cpp',
|
'src/ripple/unity/shamap.cpp',
|
||||||
'src/ripple/unity/test.cpp',
|
'src/ripple/unity/test.cpp',
|
||||||
'src/ripple/unity/unl.cpp',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if use_shp(toolchain):
|
if use_shp(toolchain):
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef RIPPLE_SIM_BASICNETWORK_H_INCLUDED
|
#ifndef RIPPLE_TEST_BASICNETWORK_H_INCLUDED
|
||||||
#define RIPPLE_SIM_BASICNETWORK_H_INCLUDED
|
#define RIPPLE_TEST_BASICNETWORK_H_INCLUDED
|
||||||
|
|
||||||
#include <ripple/basics/qalloc.h>
|
#include <ripple/basics/qalloc.h>
|
||||||
#include <beast/chrono/manual_clock.h>
|
#include <beast/chrono/manual_clock.h>
|
||||||
@@ -76,7 +76,11 @@ namespace test {
|
|||||||
of the step, step_one, step_for, and step_until functions
|
of the step, step_one, step_for, and step_until functions
|
||||||
to iterate the network,
|
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
|
Expression Type Requirements
|
||||||
---------- ---- ------------
|
---------- ---- ------------
|
||||||
@@ -87,6 +91,7 @@ namespace test {
|
|||||||
u == v bool EqualityComparable
|
u == v bool EqualityComparable
|
||||||
u < v bool LessThanComparable
|
u < v bool LessThanComparable
|
||||||
std::hash<P> class std::hash is defined for P
|
std::hash<P> class std::hash is defined for P
|
||||||
|
! u bool true if u is not-a-peer
|
||||||
*/
|
*/
|
||||||
template <class Peer>
|
template <class Peer>
|
||||||
class BasicNetwork
|
class BasicNetwork
|
||||||
@@ -96,7 +101,7 @@ public:
|
|||||||
|
|
||||||
using clock_type =
|
using clock_type =
|
||||||
beast::manual_clock<
|
beast::manual_clock<
|
||||||
std::chrono::system_clock>;
|
std::chrono::steady_clock>;
|
||||||
|
|
||||||
using duration =
|
using duration =
|
||||||
typename clock_type::duration;
|
typename clock_type::duration;
|
||||||
@@ -129,8 +134,8 @@ private:
|
|||||||
struct msg
|
struct msg
|
||||||
: by_to_hook, by_from_hook, by_when_hook
|
: by_to_hook, by_from_hook, by_when_hook
|
||||||
{
|
{
|
||||||
Peer* to;
|
Peer to;
|
||||||
Peer* from;
|
Peer from;
|
||||||
time_point when;
|
time_point when;
|
||||||
|
|
||||||
msg (msg const&) = delete;
|
msg (msg const&) = delete;
|
||||||
@@ -138,7 +143,8 @@ private:
|
|||||||
virtual ~msg() = default;
|
virtual ~msg() = default;
|
||||||
virtual void operator()() const = 0;
|
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_)
|
: to(to_), from(from_), when(when_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -160,14 +166,14 @@ private:
|
|||||||
msg_impl (msg_impl const&) = delete;
|
msg_impl (msg_impl const&) = delete;
|
||||||
msg_impl& operator= (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)
|
time_point when_, Handler&& h)
|
||||||
: msg (from_, to_, when_)
|
: msg (from_, to_, when_)
|
||||||
, h_ (std::move(h))
|
, h_ (std::move(h))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
msg_impl (Peer* from_, Peer* to_,
|
msg_impl (Peer const& from_, Peer const& to_,
|
||||||
time_point when_, Handler const& h)
|
time_point when_, Handler const& h)
|
||||||
: msg (from_, to_, when_)
|
: msg (from_, to_, when_)
|
||||||
, h_ (h)
|
, h_ (h)
|
||||||
@@ -197,10 +203,10 @@ private:
|
|||||||
boost::intrusive::make_multiset<msg,
|
boost::intrusive::make_multiset<msg,
|
||||||
boost::intrusive::constant_time_size<false>>::type;
|
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_;
|
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:
|
public:
|
||||||
using iterator =
|
using iterator =
|
||||||
@@ -225,14 +231,14 @@ private:
|
|||||||
|
|
||||||
template <class Handler>
|
template <class Handler>
|
||||||
typename by_when_set::iterator
|
typename by_when_set::iterator
|
||||||
emplace (Peer* from, Peer* to,
|
emplace (Peer const& from, Peer const& to,
|
||||||
time_point when, Handler&& h);
|
time_point when, Handler&& h);
|
||||||
|
|
||||||
void
|
void
|
||||||
erase (iterator iter);
|
erase (iterator iter);
|
||||||
|
|
||||||
void
|
void
|
||||||
remove (Peer* from, Peer* to);
|
remove (Peer const& from, Peer const& to);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct link_type
|
struct link_type
|
||||||
@@ -240,8 +246,7 @@ private:
|
|||||||
bool inbound;
|
bool inbound;
|
||||||
duration delay;
|
duration delay;
|
||||||
|
|
||||||
link_type (bool inbound_,
|
link_type (bool inbound_, duration delay_)
|
||||||
duration delay_)
|
|
||||||
: inbound (inbound_)
|
: inbound (inbound_)
|
||||||
, delay (delay_)
|
, delay (delay_)
|
||||||
{
|
{
|
||||||
@@ -249,15 +254,17 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
using links_type =
|
using links_type =
|
||||||
boost::container::flat_map<Peer*, link_type>;
|
boost::container::flat_map<Peer, link_type>;
|
||||||
|
|
||||||
class link_transform;
|
class link_transform;
|
||||||
|
|
||||||
qalloc alloc_;
|
qalloc alloc_;
|
||||||
queue_type queue_;
|
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::mt19937_64 rng_;
|
||||||
std::unordered_map<Peer*, links_type> links_;
|
std::unordered_map<Peer, links_type> links_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BasicNetwork (BasicNetwork const&) = delete;
|
BasicNetwork (BasicNetwork const&) = delete;
|
||||||
@@ -273,6 +280,10 @@ public:
|
|||||||
qalloc const&
|
qalloc const&
|
||||||
alloc() const;
|
alloc() const;
|
||||||
|
|
||||||
|
/** Return the clock. */
|
||||||
|
clock_type&
|
||||||
|
clock() const;
|
||||||
|
|
||||||
/** Return the current network time.
|
/** Return the current network time.
|
||||||
|
|
||||||
@note The epoch is unspecified
|
@note The epoch is unspecified
|
||||||
@@ -311,7 +322,7 @@ public:
|
|||||||
@return `true` if a new connection was established
|
@return `true` if a new connection was established
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
connect (Peer& from, Peer& to,
|
connect (Peer const& from, Peer const& to,
|
||||||
duration const& delay = std::chrono::seconds{0});
|
duration const& delay = std::chrono::seconds{0});
|
||||||
|
|
||||||
/** Break a link.
|
/** Break a link.
|
||||||
@@ -327,7 +338,7 @@ public:
|
|||||||
@return `true` if a connection was broken.
|
@return `true` if a connection was broken.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
disconnect (Peer& peer1, Peer& peer2);
|
disconnect (Peer const& peer1, Peer const& peer2);
|
||||||
|
|
||||||
/** Return the range of active links.
|
/** Return the range of active links.
|
||||||
|
|
||||||
@@ -335,7 +346,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
boost::transformed_range<
|
boost::transformed_range<
|
||||||
link_transform, links_type>
|
link_transform, links_type>
|
||||||
links (Peer& from);
|
links (Peer const& from);
|
||||||
|
|
||||||
/** Send a message to a peer.
|
/** Send a message to a peer.
|
||||||
|
|
||||||
@@ -357,7 +368,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <class Function>
|
template <class Function>
|
||||||
void
|
void
|
||||||
send (Peer& from, Peer& to, Function&& f);
|
send (Peer const& from, Peer const& to,
|
||||||
|
Function&& f);
|
||||||
|
|
||||||
// Used to cancel timers
|
// Used to cancel timers
|
||||||
struct cancel_token;
|
struct cancel_token;
|
||||||
@@ -409,7 +421,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <class Function>
|
template <class Function>
|
||||||
void
|
void
|
||||||
bfs (Peer& start, Function&& f);
|
bfs (Peer const& start, Function&& f);
|
||||||
|
|
||||||
/** Run the network for up to one message.
|
/** Run the network for up to one message.
|
||||||
|
|
||||||
@@ -514,7 +526,7 @@ template <class Peer>
|
|||||||
template <class Handler>
|
template <class Handler>
|
||||||
auto
|
auto
|
||||||
BasicNetwork<Peer>::queue_type::emplace(
|
BasicNetwork<Peer>::queue_type::emplace(
|
||||||
Peer* from, Peer* to, time_point when,
|
Peer const& from, Peer const& to, time_point when,
|
||||||
Handler&& h) ->
|
Handler&& h) ->
|
||||||
typename by_when_set::iterator
|
typename by_when_set::iterator
|
||||||
{
|
{
|
||||||
@@ -554,42 +566,26 @@ BasicNetwork<Peer>::queue_type::erase(
|
|||||||
template <class Peer>
|
template <class Peer>
|
||||||
void
|
void
|
||||||
BasicNetwork<Peer>::queue_type::remove(
|
BasicNetwork<Peer>::queue_type::remove(
|
||||||
Peer* from, Peer* to)
|
Peer const& from, Peer const& to)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto& list = by_to_[to];
|
auto& list = by_to_[to];
|
||||||
for(auto iter = list.begin();
|
for(auto iter = list.begin();
|
||||||
iter != list.end();)
|
iter != list.end();)
|
||||||
{
|
{
|
||||||
if (iter->from == from)
|
auto& m = *iter++;
|
||||||
{
|
if (m.from == from)
|
||||||
auto& m = *iter;
|
erase(by_when_.iterator_to(m));
|
||||||
iter = list.erase(iter);
|
|
||||||
m.~msg();
|
|
||||||
alloc_.dealloc(&m, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto& list = by_from_[from];
|
auto& list = by_to_[from];
|
||||||
for(auto iter = list.begin();
|
for(auto iter = list.begin();
|
||||||
iter != list.end();)
|
iter != list.end();)
|
||||||
{
|
{
|
||||||
if (iter->to == to)
|
auto& m = *iter++;
|
||||||
{
|
if (m.from == to)
|
||||||
auto& m = *iter;
|
erase(by_when_.iterator_to(m));
|
||||||
iter = list.erase(iter);
|
|
||||||
m.~msg();
|
|
||||||
alloc_.dealloc(&m, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,7 +597,7 @@ class BasicNetwork<Peer>::link_transform
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
BasicNetwork& net_;
|
BasicNetwork& net_;
|
||||||
Peer& from_;
|
Peer from_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using argument_type =
|
using argument_type =
|
||||||
@@ -610,13 +606,14 @@ public:
|
|||||||
class result_type
|
class result_type
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Peer& to;
|
Peer to;
|
||||||
bool inbound;
|
bool inbound;
|
||||||
|
|
||||||
result_type (result_type const&) = default;
|
result_type (result_type const&) = default;
|
||||||
|
|
||||||
result_type (BasicNetwork& net, Peer& from,
|
result_type (BasicNetwork& net,
|
||||||
Peer& to_, bool inbound_)
|
Peer const& from, Peer const& to_,
|
||||||
|
bool inbound_)
|
||||||
: to(to_)
|
: to(to_)
|
||||||
, inbound(inbound_)
|
, inbound(inbound_)
|
||||||
, net_(net)
|
, net_(net)
|
||||||
@@ -630,18 +627,19 @@ public:
|
|||||||
|
|
||||||
The connection is removed at both ends.
|
The connection is removed at both ends.
|
||||||
*/
|
*/
|
||||||
void
|
bool
|
||||||
disconnect() const
|
disconnect() const
|
||||||
{
|
{
|
||||||
net_.disconnect(from_, to);
|
return net_.disconnect(from_, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BasicNetwork& net_;
|
BasicNetwork& net_;
|
||||||
Peer& from_;
|
Peer from_;
|
||||||
};
|
};
|
||||||
|
|
||||||
link_transform (BasicNetwork& net, Peer& from)
|
link_transform (BasicNetwork& net,
|
||||||
|
Peer const& from)
|
||||||
: net_(net)
|
: net_(net)
|
||||||
, from_(from)
|
, from_(from)
|
||||||
{
|
{
|
||||||
@@ -651,7 +649,7 @@ public:
|
|||||||
operator()(argument_type const& v) const
|
operator()(argument_type const& v) const
|
||||||
{
|
{
|
||||||
return result_type(net_, from_,
|
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>
|
template <class Peer>
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
BasicNetwork<Peer>::clock() const ->
|
||||||
|
clock_type&
|
||||||
|
{
|
||||||
|
return clock_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Peer>
|
||||||
|
inline
|
||||||
auto
|
auto
|
||||||
BasicNetwork<Peer>::now() const ->
|
BasicNetwork<Peer>::now() const ->
|
||||||
time_point
|
time_point
|
||||||
@@ -730,16 +738,17 @@ BasicNetwork<Peer>::rand(
|
|||||||
template <class Peer>
|
template <class Peer>
|
||||||
bool
|
bool
|
||||||
BasicNetwork<Peer>::connect(
|
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;
|
return false;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
if (! links_[&from].emplace(&to,
|
if (! links_[from].emplace(to,
|
||||||
link_type{ false, delay }).second)
|
link_type{ false, delay }).second)
|
||||||
return false;
|
return false;
|
||||||
auto const result = links_[&to].emplace(
|
auto const result = links_[to].emplace(
|
||||||
&from, link_type{ true, delay });
|
from, link_type{ true, delay });
|
||||||
(void)result;
|
(void)result;
|
||||||
assert(result.second);
|
assert(result.second);
|
||||||
return true;
|
return true;
|
||||||
@@ -748,27 +757,27 @@ BasicNetwork<Peer>::connect(
|
|||||||
template <class Peer>
|
template <class Peer>
|
||||||
bool
|
bool
|
||||||
BasicNetwork<Peer>::disconnect(
|
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;
|
return false;
|
||||||
auto const n =
|
auto const n =
|
||||||
links_[&peer2].erase(&peer1);
|
links_[peer2].erase(peer1);
|
||||||
(void)n;
|
(void)n;
|
||||||
assert(n);
|
assert(n);
|
||||||
queue_.remove(&peer1, &peer2);
|
queue_.remove(peer1, peer2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Peer>
|
template <class Peer>
|
||||||
inline
|
inline
|
||||||
auto
|
auto
|
||||||
BasicNetwork<Peer>::links(Peer& from) ->
|
BasicNetwork<Peer>::links(Peer const& from) ->
|
||||||
boost::transformed_range<
|
boost::transformed_range<
|
||||||
link_transform, links_type>
|
link_transform, links_type>
|
||||||
{
|
{
|
||||||
return boost::adaptors::transform(
|
return boost::adaptors::transform(
|
||||||
links_[&from],
|
links_[from],
|
||||||
link_transform{ *this, from });
|
link_transform{ *this, from });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,12 +786,13 @@ template <class Function>
|
|||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
BasicNetwork<Peer>::send(
|
BasicNetwork<Peer>::send(
|
||||||
Peer& from, Peer& to, Function&& f)
|
Peer const& from, Peer const& to,
|
||||||
|
Function&& f)
|
||||||
{
|
{
|
||||||
using namespace std;
|
using namespace std;
|
||||||
auto const iter =
|
auto const iter =
|
||||||
links_[&from].find(&to);
|
links_[from].find(to);
|
||||||
queue_.emplace(&from, &to,
|
queue_.emplace(from, to,
|
||||||
clock_.now() + iter->second.delay,
|
clock_.now() + iter->second.delay,
|
||||||
forward<Function>(f));
|
forward<Function>(f));
|
||||||
}
|
}
|
||||||
@@ -852,14 +862,14 @@ bool
|
|||||||
BasicNetwork<Peer>::step_until(
|
BasicNetwork<Peer>::step_until(
|
||||||
time_point const& until)
|
time_point const& until)
|
||||||
{
|
{
|
||||||
// VFALCO This routine needs optimize
|
// VFALCO This routine needs optimizing
|
||||||
if(queue_.empty())
|
if(queue_.empty())
|
||||||
{
|
{
|
||||||
clock_.set(until);
|
clock_.set(until);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto iter = queue_.begin();
|
auto iter = queue_.begin();
|
||||||
if (iter->when > until)
|
if(iter->when > until)
|
||||||
{
|
{
|
||||||
clock_.set(until);
|
clock_.set(until);
|
||||||
return true;
|
return true;
|
||||||
@@ -882,28 +892,27 @@ bool
|
|||||||
BasicNetwork<Peer>::step_for(
|
BasicNetwork<Peer>::step_for(
|
||||||
std::chrono::duration<Period, Rep> const& amount)
|
std::chrono::duration<Period, Rep> const& amount)
|
||||||
{
|
{
|
||||||
return step_until(
|
return step_until(now() + amount);
|
||||||
clock_.now() + amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Peer>
|
template <class Peer>
|
||||||
template <class Function>
|
template <class Function>
|
||||||
void
|
void
|
||||||
BasicNetwork<Peer>::bfs(
|
BasicNetwork<Peer>::bfs(
|
||||||
Peer& start, Function&& f)
|
Peer const& start, Function&& f)
|
||||||
{
|
{
|
||||||
std::deque<std::pair<Peer*, std::size_t>> q;
|
std::deque<std::pair<Peer, std::size_t>> q;
|
||||||
std::unordered_set<Peer*> seen;
|
std::unordered_set<Peer> seen;
|
||||||
q.emplace_back(&start, 0);
|
q.emplace_back(start, 0);
|
||||||
seen.insert(&start);
|
seen.insert(start);
|
||||||
while(! q.empty())
|
while(! q.empty())
|
||||||
{
|
{
|
||||||
auto v = q.front();
|
auto v = q.front();
|
||||||
q.pop_front();
|
q.pop_front();
|
||||||
f(v.second, *v.first);
|
f(v.second, v.first);
|
||||||
for(auto const& link : links_[v.first])
|
for(auto const& link : links_[v.first])
|
||||||
{
|
{
|
||||||
auto w = link.first;
|
auto const& w = link.first;
|
||||||
if (seen.count(w) == 0)
|
if (seen.count(w) == 0)
|
||||||
{
|
{
|
||||||
q.emplace_back(w, v.second + 1);
|
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.cpp>
|
||||||
#include <ripple/test/mao/impl/Net_test.cpp>
|
#include <ripple/test/mao/impl/Net_test.cpp>
|
||||||
|
|
||||||
|
#include <ripple/test/impl/BasicNetwork_test.cpp>
|
||||||
#include <ripple/test/impl/ManualTimeKeeper.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