mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Improve Consensus interface and documentation (RIPD-1340):
- Add Consensus::Result, which represents the result of the establish state and includes the consensus transaction set, final proposed position and disputes. - Add Consensus::Mode to track how we are participating in consensus and ensures the onAccept callback can distinguish when we entered the round with consensus versus when we recovered from a wrong ledger during a round. - Rename Consensus::Phase to Consensus::State and eliminate the processing phase. Instead, accept is a terminal phase which notifies RCLConsensus via onAccept callbacks. Even if clients dispatch accepting to another thread, all future calls except to startRound will not change the state of consensus. - Move validate_ status from Consensus to RCLConsensus, since generic implementation does not directly reference whether a node is validating or not. - Eliminate gotTxSetInternal and handle externally received TxSets distinct from locally generated positions. - Change ConsensusProposal::changePosition to always update the internal close time and position even if we have bowed out. This enforces the invariant that our proposal's position always matches our transaction set.
This commit is contained in:
@@ -30,11 +30,11 @@
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@@ -98,58 +98,54 @@ class BasicNetwork
|
||||
public:
|
||||
using peer_type = Peer;
|
||||
|
||||
using clock_type =
|
||||
beast::manual_clock<
|
||||
std::chrono::steady_clock>;
|
||||
using clock_type = beast::manual_clock<std::chrono::steady_clock>;
|
||||
|
||||
using duration =
|
||||
typename clock_type::duration;
|
||||
using duration = typename clock_type::duration;
|
||||
|
||||
using time_point =
|
||||
typename clock_type::time_point;
|
||||
using time_point = typename clock_type::time_point;
|
||||
|
||||
private:
|
||||
struct by_to_tag {};
|
||||
struct by_from_tag {};
|
||||
struct by_when_tag {};
|
||||
struct by_to_tag
|
||||
{
|
||||
};
|
||||
struct by_from_tag
|
||||
{
|
||||
};
|
||||
struct by_when_tag
|
||||
{
|
||||
};
|
||||
|
||||
using by_to_hook =
|
||||
boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>,
|
||||
boost::intrusive::tag<by_to_tag>>;
|
||||
using by_to_hook = boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>,
|
||||
boost::intrusive::tag<by_to_tag>>;
|
||||
|
||||
using by_from_hook =
|
||||
boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>,
|
||||
boost::intrusive::tag<by_from_tag>>;
|
||||
using by_from_hook = boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>,
|
||||
boost::intrusive::tag<by_from_tag>>;
|
||||
|
||||
using by_when_hook =
|
||||
boost::intrusive::set_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>;
|
||||
using by_when_hook = boost::intrusive::set_base_hook<
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>>;
|
||||
|
||||
struct msg
|
||||
: by_to_hook, by_from_hook, by_when_hook
|
||||
struct msg : by_to_hook, by_from_hook, by_when_hook
|
||||
{
|
||||
Peer to;
|
||||
Peer from;
|
||||
time_point when;
|
||||
|
||||
msg (msg const&) = delete;
|
||||
msg& operator= (msg const&) = delete;
|
||||
msg(msg const&) = delete;
|
||||
msg&
|
||||
operator=(msg const&) = delete;
|
||||
virtual ~msg() = default;
|
||||
virtual void operator()() const = 0;
|
||||
virtual void
|
||||
operator()() const = 0;
|
||||
|
||||
msg (Peer const& from_, Peer const& to_,
|
||||
time_point when_)
|
||||
msg(Peer const& from_, Peer const& to_, time_point when_)
|
||||
: to(to_), from(from_), when(when_)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
operator< (msg const& other) const
|
||||
operator<(msg const& other) const
|
||||
{
|
||||
return when < other.when;
|
||||
}
|
||||
@@ -162,24 +158,30 @@ private:
|
||||
Handler const h_;
|
||||
|
||||
public:
|
||||
msg_impl (msg_impl const&) = delete;
|
||||
msg_impl& operator= (msg_impl const&) = delete;
|
||||
msg_impl(msg_impl const&) = delete;
|
||||
msg_impl&
|
||||
operator=(msg_impl const&) = delete;
|
||||
|
||||
msg_impl (Peer const& from_, Peer const& to_,
|
||||
time_point when_, Handler&& h)
|
||||
: msg (from_, to_, when_)
|
||||
, h_ (std::move(h))
|
||||
msg_impl(
|
||||
Peer const& from_,
|
||||
Peer const& to_,
|
||||
time_point when_,
|
||||
Handler&& h)
|
||||
: msg(from_, to_, when_), h_(std::move(h))
|
||||
{
|
||||
}
|
||||
|
||||
msg_impl (Peer const& from_, Peer const& to_,
|
||||
time_point when_, Handler const& h)
|
||||
: msg (from_, to_, when_)
|
||||
, h_ (h)
|
||||
msg_impl(
|
||||
Peer const& from_,
|
||||
Peer const& to_,
|
||||
time_point when_,
|
||||
Handler const& h)
|
||||
: msg(from_, to_, when_), h_(h)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()() const override
|
||||
void
|
||||
operator()() const override
|
||||
{
|
||||
h_();
|
||||
}
|
||||
@@ -188,19 +190,19 @@ private:
|
||||
class queue_type
|
||||
{
|
||||
private:
|
||||
using by_to_list = typename
|
||||
boost::intrusive::make_list<msg,
|
||||
boost::intrusive::base_hook<by_to_hook>,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
using by_to_list = typename boost::intrusive::make_list<
|
||||
msg,
|
||||
boost::intrusive::base_hook<by_to_hook>,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using by_from_list = typename
|
||||
boost::intrusive::make_list<msg,
|
||||
boost::intrusive::base_hook<by_from_hook>,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
using by_from_list = typename boost::intrusive::make_list<
|
||||
msg,
|
||||
boost::intrusive::base_hook<by_from_hook>,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using by_when_set = typename
|
||||
boost::intrusive::make_multiset<msg,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
using by_when_set = typename boost::intrusive::make_multiset<
|
||||
msg,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
qalloc alloc_;
|
||||
by_when_set by_when_;
|
||||
@@ -208,14 +210,13 @@ private:
|
||||
std::unordered_map<Peer, by_from_list> by_from_;
|
||||
|
||||
public:
|
||||
using iterator =
|
||||
typename by_when_set::iterator;
|
||||
using iterator = typename by_when_set::iterator;
|
||||
|
||||
queue_type (queue_type const&) = delete;
|
||||
queue_type& operator= (queue_type const&) = delete;
|
||||
queue_type(queue_type const&) = delete;
|
||||
queue_type&
|
||||
operator=(queue_type const&) = delete;
|
||||
|
||||
explicit
|
||||
queue_type (qalloc const& alloc);
|
||||
explicit queue_type(qalloc const& alloc);
|
||||
|
||||
~queue_type();
|
||||
|
||||
@@ -230,14 +231,13 @@ private:
|
||||
|
||||
template <class Handler>
|
||||
typename by_when_set::iterator
|
||||
emplace (Peer const& from, Peer const& to,
|
||||
time_point when, Handler&& h);
|
||||
emplace(Peer const& from, Peer const& to, time_point when, Handler&& h);
|
||||
|
||||
void
|
||||
erase (iterator iter);
|
||||
erase(iterator iter);
|
||||
|
||||
void
|
||||
remove (Peer const& from, Peer const& to);
|
||||
remove(Peer const& from, Peer const& to);
|
||||
};
|
||||
|
||||
struct link_type
|
||||
@@ -245,15 +245,13 @@ private:
|
||||
bool inbound;
|
||||
duration delay;
|
||||
|
||||
link_type (bool inbound_, duration delay_)
|
||||
: inbound (inbound_)
|
||||
, delay (delay_)
|
||||
link_type(bool inbound_, duration delay_)
|
||||
: inbound(inbound_), delay(delay_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using links_type =
|
||||
boost::container::flat_map<Peer, link_type>;
|
||||
using links_type = boost::container::flat_map<Peer, link_type>;
|
||||
|
||||
class link_transform;
|
||||
|
||||
@@ -265,8 +263,9 @@ private:
|
||||
std::unordered_map<Peer, links_type> links_;
|
||||
|
||||
public:
|
||||
BasicNetwork (BasicNetwork const&) = delete;
|
||||
BasicNetwork& operator= (BasicNetwork const&) = delete;
|
||||
BasicNetwork(BasicNetwork const&) = delete;
|
||||
BasicNetwork&
|
||||
operator=(BasicNetwork const&) = delete;
|
||||
|
||||
BasicNetwork();
|
||||
|
||||
@@ -308,7 +307,9 @@ public:
|
||||
@return `true` if a new connection was established
|
||||
*/
|
||||
bool
|
||||
connect (Peer const& from, Peer const& to,
|
||||
connect(
|
||||
Peer const& from,
|
||||
Peer const& to,
|
||||
duration const& delay = std::chrono::seconds{0});
|
||||
|
||||
/** Break a link.
|
||||
@@ -324,15 +325,14 @@ public:
|
||||
@return `true` if a connection was broken.
|
||||
*/
|
||||
bool
|
||||
disconnect (Peer const& peer1, Peer const& peer2);
|
||||
disconnect(Peer const& peer1, Peer const& peer2);
|
||||
|
||||
/** Return the range of active links.
|
||||
|
||||
@return A random access range.
|
||||
*/
|
||||
boost::transformed_range<
|
||||
link_transform, links_type>
|
||||
links (Peer const& from);
|
||||
boost::transformed_range<link_transform, links_type>
|
||||
links(Peer const& from);
|
||||
|
||||
/** Send a message to a peer.
|
||||
|
||||
@@ -354,8 +354,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
void
|
||||
send (Peer const& from, Peer const& to,
|
||||
Function&& f);
|
||||
send(Peer const& from, Peer const& to, Function&& f);
|
||||
|
||||
// Used to cancel timers
|
||||
struct cancel_token;
|
||||
@@ -370,8 +369,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
cancel_token
|
||||
timer (time_point const& when,
|
||||
Function&& f);
|
||||
timer(time_point const& when, Function&& f);
|
||||
|
||||
/** Deliver a timer notification.
|
||||
|
||||
@@ -383,8 +381,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
cancel_token
|
||||
timer (duration const& delay,
|
||||
Function&& f);
|
||||
timer(duration const& delay, Function&& f);
|
||||
|
||||
/** Cancel a timer.
|
||||
|
||||
@@ -394,7 +391,7 @@ public:
|
||||
timer() which has not yet been invoked.
|
||||
*/
|
||||
void
|
||||
cancel (cancel_token const& token);
|
||||
cancel(cancel_token const& token);
|
||||
|
||||
/** Perform breadth-first search.
|
||||
|
||||
@@ -407,7 +404,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
void
|
||||
bfs (Peer const& start, Function&& f);
|
||||
bfs(Peer const& start, Function&& f);
|
||||
|
||||
/** Run the network for up to one message.
|
||||
|
||||
@@ -448,7 +445,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
bool
|
||||
step_while(Function && func);
|
||||
step_while(Function&& func);
|
||||
|
||||
/** Run the network until the specified time.
|
||||
|
||||
@@ -460,7 +457,7 @@ public:
|
||||
@return `true` if any messages remain.
|
||||
*/
|
||||
bool
|
||||
step_until (time_point const& until);
|
||||
step_until(time_point const& until);
|
||||
|
||||
/** Run the network until time has elapsed.
|
||||
|
||||
@@ -473,24 +470,21 @@ public:
|
||||
*/
|
||||
template <class Period, class Rep>
|
||||
bool
|
||||
step_for (std::chrono::duration<
|
||||
Period, Rep> const& amount);
|
||||
step_for(std::chrono::duration<Period, Rep> const& amount);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class Peer>
|
||||
BasicNetwork<Peer>::queue_type::queue_type(
|
||||
qalloc const& alloc)
|
||||
: alloc_ (alloc)
|
||||
BasicNetwork<Peer>::queue_type::queue_type(qalloc const& alloc)
|
||||
: alloc_(alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
BasicNetwork<Peer>::queue_type::~queue_type()
|
||||
{
|
||||
for(auto iter = by_when_.begin();
|
||||
iter != by_when_.end();)
|
||||
for (auto iter = by_when_.begin(); iter != by_when_.end();)
|
||||
{
|
||||
auto m = &*iter;
|
||||
++iter;
|
||||
@@ -500,27 +494,22 @@ BasicNetwork<Peer>::queue_type::~queue_type()
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
bool
|
||||
inline bool
|
||||
BasicNetwork<Peer>::queue_type::empty() const
|
||||
{
|
||||
return by_when_.empty();
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::queue_type::begin() ->
|
||||
iterator
|
||||
inline auto
|
||||
BasicNetwork<Peer>::queue_type::begin() -> iterator
|
||||
{
|
||||
return by_when_.begin();
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::queue_type::end() ->
|
||||
iterator
|
||||
inline auto
|
||||
BasicNetwork<Peer>::queue_type::end() -> iterator
|
||||
{
|
||||
return by_when_.end();
|
||||
}
|
||||
@@ -529,15 +518,14 @@ template <class Peer>
|
||||
template <class Handler>
|
||||
auto
|
||||
BasicNetwork<Peer>::queue_type::emplace(
|
||||
Peer const& from, Peer const& to, time_point when,
|
||||
Handler&& h) ->
|
||||
typename by_when_set::iterator
|
||||
Peer const& from,
|
||||
Peer const& to,
|
||||
time_point when,
|
||||
Handler&& h) -> typename by_when_set::iterator
|
||||
{
|
||||
using msg_type = msg_impl<
|
||||
std::decay_t<Handler>>;
|
||||
using msg_type = msg_impl<std::decay_t<Handler>>;
|
||||
auto const p = alloc_.alloc<msg_type>(1);
|
||||
auto& m = *new(p) msg_type(from, to,
|
||||
when, std::forward<Handler>(h));
|
||||
auto& m = *new (p) msg_type(from, to, when, std::forward<Handler>(h));
|
||||
if (to)
|
||||
by_to_[to].push_back(m);
|
||||
if (from)
|
||||
@@ -547,8 +535,7 @@ BasicNetwork<Peer>::queue_type::emplace(
|
||||
|
||||
template <class Peer>
|
||||
void
|
||||
BasicNetwork<Peer>::queue_type::erase(
|
||||
iterator iter)
|
||||
BasicNetwork<Peer>::queue_type::erase(iterator iter)
|
||||
{
|
||||
auto& m = *iter;
|
||||
if (iter->to)
|
||||
@@ -568,13 +555,11 @@ BasicNetwork<Peer>::queue_type::erase(
|
||||
|
||||
template <class Peer>
|
||||
void
|
||||
BasicNetwork<Peer>::queue_type::remove(
|
||||
Peer const& from, Peer const& to)
|
||||
BasicNetwork<Peer>::queue_type::remove(Peer const& from, Peer const& to)
|
||||
{
|
||||
{
|
||||
auto& list = by_to_[to];
|
||||
for(auto iter = list.begin();
|
||||
iter != list.end();)
|
||||
for (auto iter = list.begin(); iter != list.end();)
|
||||
{
|
||||
auto& m = *iter++;
|
||||
if (m.from == from)
|
||||
@@ -583,8 +568,7 @@ BasicNetwork<Peer>::queue_type::remove(
|
||||
}
|
||||
{
|
||||
auto& list = by_to_[from];
|
||||
for(auto iter = list.begin();
|
||||
iter != list.end();)
|
||||
for (auto iter = list.begin(); iter != list.end();)
|
||||
{
|
||||
auto& m = *iter++;
|
||||
if (m.from == to)
|
||||
@@ -603,8 +587,7 @@ private:
|
||||
Peer from_;
|
||||
|
||||
public:
|
||||
using argument_type =
|
||||
typename links_type::value_type;
|
||||
using argument_type = typename links_type::value_type;
|
||||
|
||||
class result_type
|
||||
{
|
||||
@@ -612,15 +595,14 @@ public:
|
||||
Peer to;
|
||||
bool inbound;
|
||||
|
||||
result_type (result_type const&) = default;
|
||||
result_type(result_type const&) = default;
|
||||
|
||||
result_type (BasicNetwork& net,
|
||||
Peer const& from, Peer const& to_,
|
||||
bool inbound_)
|
||||
: to(to_)
|
||||
, inbound(inbound_)
|
||||
, net_(net)
|
||||
, from_(from)
|
||||
result_type(
|
||||
BasicNetwork& net,
|
||||
Peer const& from,
|
||||
Peer const& to_,
|
||||
bool inbound_)
|
||||
: to(to_), inbound(inbound_), net_(net), from_(from)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -641,18 +623,14 @@ public:
|
||||
Peer from_;
|
||||
};
|
||||
|
||||
link_transform (BasicNetwork& net,
|
||||
Peer const& from)
|
||||
: net_(net)
|
||||
, from_(from)
|
||||
link_transform(BasicNetwork& net, Peer const& from) : net_(net), from_(from)
|
||||
{
|
||||
}
|
||||
|
||||
result_type const
|
||||
operator()(argument_type const& v) const
|
||||
{
|
||||
return result_type(net_, from_,
|
||||
v.first, v.second.inbound);
|
||||
return result_type(net_, from_, v.first, v.second.inbound);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -666,14 +644,13 @@ private:
|
||||
|
||||
public:
|
||||
cancel_token() = delete;
|
||||
cancel_token (cancel_token const&) = default;
|
||||
cancel_token& operator= (cancel_token const&) = default;
|
||||
cancel_token(cancel_token const&) = default;
|
||||
cancel_token&
|
||||
operator=(cancel_token const&) = default;
|
||||
|
||||
private:
|
||||
friend class BasicNetwork;
|
||||
cancel_token(typename
|
||||
queue_type::iterator iter)
|
||||
: iter_ (iter)
|
||||
cancel_token(typename queue_type::iterator iter) : iter_(iter)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -681,33 +658,27 @@ private:
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class Peer>
|
||||
BasicNetwork<Peer>::BasicNetwork()
|
||||
: queue_ (alloc_)
|
||||
BasicNetwork<Peer>::BasicNetwork() : queue_(alloc_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
qalloc const&
|
||||
inline qalloc const&
|
||||
BasicNetwork<Peer>::alloc() const
|
||||
{
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::clock() const ->
|
||||
clock_type&
|
||||
inline auto
|
||||
BasicNetwork<Peer>::clock() const -> clock_type&
|
||||
{
|
||||
return clock_;
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::now() const ->
|
||||
time_point
|
||||
inline auto
|
||||
BasicNetwork<Peer>::now() const -> time_point
|
||||
{
|
||||
return clock_.now();
|
||||
}
|
||||
@@ -715,17 +686,16 @@ BasicNetwork<Peer>::now() const ->
|
||||
template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::connect(
|
||||
Peer const& from, Peer const& to,
|
||||
duration const& delay)
|
||||
Peer const& from,
|
||||
Peer const& to,
|
||||
duration const& delay)
|
||||
{
|
||||
if (to == from)
|
||||
return false;
|
||||
using namespace std;
|
||||
if (! links_[from].emplace(to,
|
||||
link_type{ false, delay }).second)
|
||||
if (!links_[from].emplace(to, link_type{false, delay}).second)
|
||||
return false;
|
||||
auto const result = links_[to].emplace(
|
||||
from, link_type{ true, delay });
|
||||
auto const result = links_[to].emplace(from, link_type{true, delay});
|
||||
(void)result;
|
||||
assert(result.second);
|
||||
return true;
|
||||
@@ -733,13 +703,11 @@ BasicNetwork<Peer>::connect(
|
||||
|
||||
template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::disconnect(
|
||||
Peer const& peer1, Peer const& peer2)
|
||||
BasicNetwork<Peer>::disconnect(Peer const& peer1, Peer const& peer2)
|
||||
{
|
||||
if (links_[peer1].erase(peer2) == 0)
|
||||
return false;
|
||||
auto const n =
|
||||
links_[peer2].erase(peer1);
|
||||
auto const n = links_[peer2].erase(peer1);
|
||||
(void)n;
|
||||
assert(n);
|
||||
queue_.remove(peer1, peer2);
|
||||
@@ -747,64 +715,45 @@ BasicNetwork<Peer>::disconnect(
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::links(Peer const& from) ->
|
||||
boost::transformed_range<
|
||||
link_transform, links_type>
|
||||
inline auto
|
||||
BasicNetwork<Peer>::links(Peer const& from)
|
||||
-> boost::transformed_range<link_transform, links_type>
|
||||
{
|
||||
return boost::adaptors::transform(
|
||||
links_[from],
|
||||
link_transform{ *this, from });
|
||||
links_[from], link_transform{*this, from});
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
inline
|
||||
void
|
||||
BasicNetwork<Peer>::send(
|
||||
Peer const& from, Peer const& to,
|
||||
Function&& f)
|
||||
inline void
|
||||
BasicNetwork<Peer>::send(Peer const& from, Peer const& to, Function&& f)
|
||||
{
|
||||
using namespace std;
|
||||
auto const iter =
|
||||
links_[from].find(to);
|
||||
queue_.emplace(from, to,
|
||||
clock_.now() + iter->second.delay,
|
||||
forward<Function>(f));
|
||||
auto const iter = links_[from].find(to);
|
||||
queue_.emplace(
|
||||
from, to, clock_.now() + iter->second.delay, forward<Function>(f));
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::timer(
|
||||
time_point const& when, Function&& f) ->
|
||||
cancel_token
|
||||
inline auto
|
||||
BasicNetwork<Peer>::timer(time_point const& when, Function&& f) -> cancel_token
|
||||
{
|
||||
using namespace std;
|
||||
return queue_.emplace(
|
||||
nullptr, nullptr, when,
|
||||
forward<Function>(f));
|
||||
return queue_.emplace(nullptr, nullptr, when, forward<Function>(f));
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::timer(
|
||||
duration const& delay, Function&& f) ->
|
||||
cancel_token
|
||||
inline auto
|
||||
BasicNetwork<Peer>::timer(duration const& delay, Function&& f) -> cancel_token
|
||||
{
|
||||
return timer(clock_.now() + delay,
|
||||
std::forward<Function>(f));
|
||||
return timer(clock_.now() + delay, std::forward<Function>(f));
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
void
|
||||
BasicNetwork<Peer>::cancel(
|
||||
cancel_token const& token)
|
||||
inline void
|
||||
BasicNetwork<Peer>::cancel(cancel_token const& token)
|
||||
{
|
||||
queue_.erase(token.iter_);
|
||||
}
|
||||
@@ -826,10 +775,10 @@ template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::step()
|
||||
{
|
||||
if (! step_one())
|
||||
if (!step_one())
|
||||
return false;
|
||||
for(;;)
|
||||
if (! step_one())
|
||||
for (;;)
|
||||
if (!step_one())
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
@@ -837,7 +786,7 @@ BasicNetwork<Peer>::step()
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
bool
|
||||
BasicNetwork<Peer>::step_while(Function && f)
|
||||
BasicNetwork<Peer>::step_while(Function&& f)
|
||||
{
|
||||
bool ran = false;
|
||||
while (f() && step_one())
|
||||
@@ -847,17 +796,16 @@ BasicNetwork<Peer>::step_while(Function && f)
|
||||
|
||||
template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::step_until(
|
||||
time_point const& until)
|
||||
BasicNetwork<Peer>::step_until(time_point const& until)
|
||||
{
|
||||
// VFALCO This routine needs optimizing
|
||||
if(queue_.empty())
|
||||
if (queue_.empty())
|
||||
{
|
||||
clock_.set(until);
|
||||
return false;
|
||||
}
|
||||
auto iter = queue_.begin();
|
||||
if(iter->when > until)
|
||||
if (iter->when > until)
|
||||
{
|
||||
clock_.set(until);
|
||||
return true;
|
||||
@@ -866,19 +814,15 @@ BasicNetwork<Peer>::step_until(
|
||||
{
|
||||
step_one();
|
||||
iter = queue_.begin();
|
||||
}
|
||||
while(iter != queue_.end() &&
|
||||
iter->when <= until);
|
||||
} while (iter != queue_.end() && iter->when <= until);
|
||||
clock_.set(until);
|
||||
return iter != queue_.end();
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Period, class Rep>
|
||||
inline
|
||||
bool
|
||||
BasicNetwork<Peer>::step_for(
|
||||
std::chrono::duration<Period, Rep> const& amount)
|
||||
inline bool
|
||||
BasicNetwork<Peer>::step_for(std::chrono::duration<Period, Rep> const& amount)
|
||||
{
|
||||
return step_until(now() + amount);
|
||||
}
|
||||
@@ -886,19 +830,18 @@ BasicNetwork<Peer>::step_for(
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
void
|
||||
BasicNetwork<Peer>::bfs(
|
||||
Peer const& start, Function&& f)
|
||||
BasicNetwork<Peer>::bfs(Peer const& start, Function&& f)
|
||||
{
|
||||
std::deque<std::pair<Peer, std::size_t>> q;
|
||||
std::unordered_set<Peer> seen;
|
||||
q.emplace_back(start, 0);
|
||||
seen.insert(start);
|
||||
while(! q.empty())
|
||||
while (!q.empty())
|
||||
{
|
||||
auto v = q.front();
|
||||
q.pop_front();
|
||||
f(v.second, v.first);
|
||||
for(auto const& link : links_[v.first])
|
||||
for (auto const& link : links_[v.first])
|
||||
{
|
||||
auto const& w = link.first;
|
||||
if (seen.count(w) == 0)
|
||||
@@ -910,8 +853,8 @@ BasicNetwork<Peer>::bfs(
|
||||
}
|
||||
}
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,15 +18,14 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <test/csf/BasicNetwork.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <set>
|
||||
#include <test/csf/BasicNetwork.h>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
|
||||
class BasicNetwork_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
@@ -35,28 +34,25 @@ public:
|
||||
int id;
|
||||
std::set<int> set;
|
||||
|
||||
Peer (Peer const&) = default;
|
||||
Peer (Peer&&) = default;
|
||||
Peer(Peer const&) = default;
|
||||
Peer(Peer&&) = default;
|
||||
|
||||
explicit Peer(int id_)
|
||||
: id(id_)
|
||||
explicit Peer(int id_) : id(id_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
void start(Net& net)
|
||||
void
|
||||
start(Net& net)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
auto t = net.timer(1s,
|
||||
[&]{ set.insert(0); });
|
||||
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);
|
||||
});
|
||||
for (auto const& link : net.links(this))
|
||||
net.send(this, link.to, [&, to = link.to ] {
|
||||
to->receive(net, this, 1);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -65,23 +61,23 @@ public:
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
void receive(Net& net, Peer* from, int m)
|
||||
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);
|
||||
});
|
||||
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
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
std::vector<Peer> pv;
|
||||
@@ -89,45 +85,40 @@ public:
|
||||
pv.emplace_back(1);
|
||||
pv.emplace_back(2);
|
||||
csf::BasicNetwork<Peer*> net;
|
||||
BEAST_EXPECT(! net.connect(&pv[0], &pv[0]));
|
||||
BEAST_EXPECT(!net.connect(&pv[0], &pv[0]));
|
||||
BEAST_EXPECT(net.connect(&pv[0], &pv[1], 1s));
|
||||
BEAST_EXPECT(net.connect(&pv[1], &pv[2], 1s));
|
||||
BEAST_EXPECT(! net.connect(&pv[0], &pv[1]));
|
||||
BEAST_EXPECT(!net.connect(&pv[0], &pv[1]));
|
||||
std::size_t diameter = 0;
|
||||
net.bfs(&pv[0],
|
||||
[&](auto d, Peer*)
|
||||
{ diameter = std::max(d, diameter); });
|
||||
net.bfs(
|
||||
&pv[0], [&](auto d, Peer*) { diameter = std::max(d, diameter); });
|
||||
BEAST_EXPECT(diameter == 2);
|
||||
for(auto& peer : pv)
|
||||
for (auto& peer : pv)
|
||||
peer.start(net);
|
||||
BEAST_EXPECT(net.step_for(0s));
|
||||
BEAST_EXPECT(net.step_for(1s));
|
||||
BEAST_EXPECT(net.step());
|
||||
BEAST_EXPECT(! net.step());
|
||||
BEAST_EXPECT(! net.step_for(1s));
|
||||
net.send(&pv[0], &pv[1], []{});
|
||||
net.send(&pv[1], &pv[0], []{});
|
||||
BEAST_EXPECT(!net.step());
|
||||
BEAST_EXPECT(!net.step_for(1s));
|
||||
net.send(&pv[0], &pv[1], [] {});
|
||||
net.send(&pv[1], &pv[0], [] {});
|
||||
BEAST_EXPECT(net.disconnect(&pv[0], &pv[1]));
|
||||
BEAST_EXPECT(! net.disconnect(&pv[0], &pv[1]));
|
||||
for(;;)
|
||||
BEAST_EXPECT(!net.disconnect(&pv[0], &pv[1]));
|
||||
for (;;)
|
||||
{
|
||||
auto const links = net.links(&pv[1]);
|
||||
if(links.empty())
|
||||
if (links.empty())
|
||||
break;
|
||||
BEAST_EXPECT(links[0].disconnect());
|
||||
}
|
||||
BEAST_EXPECT(pv[0].set ==
|
||||
std::set<int>({0, 2, 4}));
|
||||
BEAST_EXPECT(pv[1].set ==
|
||||
std::set<int>({1, 3}));
|
||||
BEAST_EXPECT(pv[2].set ==
|
||||
std::set<int>({2, 4}));
|
||||
net.timer(0s, []{});
|
||||
BEAST_EXPECT(pv[0].set == std::set<int>({0, 2, 4}));
|
||||
BEAST_EXPECT(pv[1].set == std::set<int>({1, 3}));
|
||||
BEAST_EXPECT(pv[2].set == std::set<int>({2, 4}));
|
||||
net.timer(0s, [] {});
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(BasicNetwork, test, ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
@@ -46,32 +46,32 @@ namespace csf {
|
||||
|
||||
class Ledger
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
struct ID
|
||||
{
|
||||
std::uint32_t seq = 0;
|
||||
TxSetType txs = TxSetType{};
|
||||
|
||||
bool operator==(ID const & o) const
|
||||
bool
|
||||
operator==(ID const& o) const
|
||||
{
|
||||
return seq == o.seq && txs == o.txs;
|
||||
}
|
||||
|
||||
bool operator!=(ID const & o) const
|
||||
bool
|
||||
operator!=(ID const& o) const
|
||||
{
|
||||
return !(*this == o);
|
||||
}
|
||||
|
||||
bool operator<(ID const & o) const
|
||||
bool
|
||||
operator<(ID const& o) const
|
||||
{
|
||||
return std::tie(seq, txs) < std::tie(o.seq, o.txs);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
auto const &
|
||||
auto const&
|
||||
id() const
|
||||
{
|
||||
return id_;
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
return parentCloseTime_;
|
||||
}
|
||||
|
||||
auto const &
|
||||
auto const&
|
||||
parentID() const
|
||||
{
|
||||
return parentID_;
|
||||
@@ -127,30 +127,28 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//! Apply the given transactions to this ledger
|
||||
Ledger
|
||||
close(TxSetType const & txs,
|
||||
close(
|
||||
TxSetType const& txs,
|
||||
NetClock::duration closeTimeResolution,
|
||||
NetClock::time_point const & consensusCloseTime,
|
||||
NetClock::time_point const& consensusCloseTime,
|
||||
bool closeTimeAgree) const
|
||||
{
|
||||
Ledger res{ *this };
|
||||
Ledger res{*this};
|
||||
res.id_.txs.insert(txs.begin(), txs.end());
|
||||
res.id_ .seq= seq() + 1;
|
||||
res.id_.seq = seq() + 1;
|
||||
res.closeTimeResolution_ = closeTimeResolution;
|
||||
res.actualCloseTime_ = consensusCloseTime;
|
||||
res.closeTime_ = effectiveCloseTime(consensusCloseTime,
|
||||
closeTimeResolution, parentCloseTime_);
|
||||
res.closeTime_ = effCloseTime(
|
||||
consensusCloseTime, closeTimeResolution, parentCloseTime_);
|
||||
res.closeTimeAgree_ = closeTimeAgree;
|
||||
res.parentCloseTime_ = closeTime();
|
||||
res.parentID_ = id();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
//! Unique identifier of ledger is combination of sequence number and id
|
||||
ID id_;
|
||||
|
||||
@@ -171,27 +169,24 @@ private:
|
||||
|
||||
//! Close time unadjusted by closeTimeResolution
|
||||
NetClock::time_point actualCloseTime_;
|
||||
|
||||
};
|
||||
|
||||
inline
|
||||
std::ostream &
|
||||
operator<<(std::ostream & o, Ledger::ID const & id)
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& o, Ledger::ID const& id)
|
||||
{
|
||||
return o << id.seq << "," << id.txs;
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
to_string(Ledger::ID const & id)
|
||||
inline std::string
|
||||
to_string(Ledger::ID const& id)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << id;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -22,15 +22,14 @@
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <test/csf/Tx.h>
|
||||
#include <test/csf/Ledger.h>
|
||||
#include <test/csf/Tx.h>
|
||||
#include <test/csf/UNL.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace csf {
|
||||
|
||||
|
||||
/** Store validations reached by peers */
|
||||
struct Validation
|
||||
{
|
||||
@@ -45,15 +44,17 @@ class Validations
|
||||
{
|
||||
//< Ledgers seen by peers, saved in order received (which should be order
|
||||
//< created)
|
||||
bc::flat_map<Ledger::ID, bc::flat_set<PeerID>> nodesFromLedger;
|
||||
bc::flat_map<Ledger::ID, bc::flat_set<PeerID>> nodesFromPrevLedger;
|
||||
bc::flat_map<Ledger::ID, bc::flat_map<Ledger::ID, std::size_t>> childLedgers;
|
||||
bc::flat_map<Ledger::ID, bc::flat_set<PeerID>> nodesFromLedger;
|
||||
bc::flat_map<Ledger::ID, bc::flat_set<PeerID>> nodesFromPrevLedger;
|
||||
bc::flat_map<Ledger::ID, bc::flat_map<Ledger::ID, std::size_t>>
|
||||
childLedgers;
|
||||
|
||||
public:
|
||||
void
|
||||
update(Validation const & v)
|
||||
update(Validation const& v)
|
||||
{
|
||||
nodesFromLedger[v.ledger].insert(v.id);
|
||||
if(v.ledger.seq > 0)
|
||||
if (v.ledger.seq > 0)
|
||||
{
|
||||
nodesFromPrevLedger[v.prevLedger].insert(v.id);
|
||||
childLedgers[v.prevLedger][v.ledger]++;
|
||||
@@ -62,10 +63,10 @@ public:
|
||||
|
||||
//< The number of peers who have validated this ledger
|
||||
std::size_t
|
||||
proposersValidated(Ledger::ID const & prevLedger) const
|
||||
proposersValidated(Ledger::ID const& prevLedger) const
|
||||
{
|
||||
auto it = nodesFromLedger.find(prevLedger);
|
||||
if(it != nodesFromLedger.end())
|
||||
if (it != nodesFromLedger.end())
|
||||
return it->second.size();
|
||||
return 0;
|
||||
}
|
||||
@@ -75,35 +76,33 @@ public:
|
||||
as an ancestor.
|
||||
*/
|
||||
std::size_t
|
||||
proposersFinished(Ledger::ID const & prevLedger) const
|
||||
proposersFinished(Ledger::ID const& prevLedger) const
|
||||
{
|
||||
auto it = nodesFromPrevLedger.find(prevLedger);
|
||||
if(it != nodesFromPrevLedger.end())
|
||||
if (it != nodesFromPrevLedger.end())
|
||||
return it->second.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Returns the ledger starting from prevLedger with the most validations.
|
||||
*/
|
||||
*/
|
||||
Ledger::ID
|
||||
getBestLCL(Ledger::ID const & currLedger,
|
||||
Ledger::ID const & prevLedger) const
|
||||
getBestLCL(Ledger::ID const& currLedger, Ledger::ID const& prevLedger) const
|
||||
{
|
||||
auto it = childLedgers.find(prevLedger);
|
||||
if (it != childLedgers.end() &&
|
||||
! it->second.empty())
|
||||
if (it != childLedgers.end() && !it->second.empty())
|
||||
{
|
||||
std::size_t bestCount = 0;
|
||||
Ledger::ID bestLedger;
|
||||
|
||||
for (auto const & b : it->second)
|
||||
for (auto const& b : it->second)
|
||||
{
|
||||
auto currCount = b.second;
|
||||
if(currLedger == b.first)
|
||||
if (currLedger == b.first)
|
||||
currCount++;
|
||||
if(currCount > bestCount)
|
||||
if (currCount > bestCount)
|
||||
bestLedger = b.first;
|
||||
if(currCount == bestCount && currLedger == b.first)
|
||||
if (currCount == bestCount && currLedger == b.first)
|
||||
bestLedger = b.first;
|
||||
}
|
||||
return bestLedger;
|
||||
@@ -122,11 +121,8 @@ struct Traits
|
||||
using Ledger_t = Ledger;
|
||||
using NodeID_t = PeerID;
|
||||
using TxSet_t = TxSet;
|
||||
using MissingTxException_t = MissingTx;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Represents a single node participating in the consensus process.
|
||||
It implements the Callbacks required by Consensus.
|
||||
*/
|
||||
@@ -144,7 +140,7 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
Ledger lastClosedLedger;
|
||||
|
||||
//! Handle to network for sending messages
|
||||
BasicNetwork<Peer*> & net;
|
||||
BasicNetwork<Peer*>& net;
|
||||
|
||||
//! UNL of trusted peers
|
||||
UNL unl;
|
||||
@@ -173,12 +169,12 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
//! Delay in acquiring missing ledger from the network
|
||||
std::chrono::milliseconds missingLedgerDelay{0};
|
||||
|
||||
bool validating = true;
|
||||
bool proposing = true;
|
||||
bool validating_ = true;
|
||||
bool proposing_ = true;
|
||||
|
||||
//! All peers start from the default constructed ledger
|
||||
Peer(PeerID i, BasicNetwork<Peer*> & n, UNL const & u)
|
||||
: Consensus<Peer, Traits>( n.clock(), beast::Journal{})
|
||||
Peer(PeerID i, BasicNetwork<Peer*>& n, UNL const& u)
|
||||
: Consensus<Peer, Traits>(n.clock(), beast::Journal{})
|
||||
, id{i}
|
||||
, net{n}
|
||||
, unl(u)
|
||||
@@ -186,19 +182,8 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
ledgers[lastClosedLedger.id()] = lastClosedLedger;
|
||||
}
|
||||
|
||||
|
||||
// @return whether we are proposing,validating
|
||||
// TODO: Bit akward that this is in callbacks, would be nice to extract
|
||||
std::pair<bool, bool>
|
||||
getMode()
|
||||
{
|
||||
// in RCL this hits NetworkOps to decide whether we are proposing
|
||||
// validating
|
||||
return{ proposing, validating };
|
||||
}
|
||||
|
||||
Ledger const *
|
||||
acquireLedger(Ledger::ID const & ledgerHash)
|
||||
Ledger const*
|
||||
acquireLedger(Ledger::ID const& ledgerHash)
|
||||
{
|
||||
auto it = ledgers.find(ledgerHash);
|
||||
if (it != ledgers.end())
|
||||
@@ -208,16 +193,16 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
|
||||
for (auto const& link : net.links(this))
|
||||
{
|
||||
auto const & p = *link.to;
|
||||
auto const& p = *link.to;
|
||||
auto it = p.ledgers.find(ledgerHash);
|
||||
if (it != p.ledgers.end())
|
||||
{
|
||||
schedule(missingLedgerDelay,
|
||||
[this, ledgerHash, ledger = it->second]()
|
||||
{
|
||||
schedule(
|
||||
missingLedgerDelay,
|
||||
[ this, ledgerHash, ledger = it->second ]() {
|
||||
ledgers.emplace(ledgerHash, ledger);
|
||||
});
|
||||
if(missingLedgerDelay == 0ms)
|
||||
if (missingLedgerDelay == 0ms)
|
||||
return &ledgers[ledgerHash];
|
||||
break;
|
||||
}
|
||||
@@ -225,23 +210,22 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto const &
|
||||
proposals(Ledger::ID const & ledgerHash)
|
||||
auto const&
|
||||
proposals(Ledger::ID const& ledgerHash)
|
||||
{
|
||||
return peerPositions_[ledgerHash];
|
||||
}
|
||||
|
||||
TxSet const *
|
||||
acquireTxSet(TxSet::ID const & setId)
|
||||
TxSet const*
|
||||
acquireTxSet(TxSet::ID const& setId)
|
||||
{
|
||||
auto it = txSets.find(setId);
|
||||
if(it != txSets.end())
|
||||
if (it != txSets.end())
|
||||
return &(it->second);
|
||||
// TODO Get from network/oracle instead!
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
hasOpenTransactions() const
|
||||
{
|
||||
@@ -249,114 +233,68 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
}
|
||||
|
||||
std::size_t
|
||||
proposersValidated(Ledger::ID const & prevLedger)
|
||||
proposersValidated(Ledger::ID const& prevLedger)
|
||||
{
|
||||
return peerValidations.proposersValidated(prevLedger);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
proposersFinished(Ledger::ID const & prevLedger)
|
||||
proposersFinished(Ledger::ID const& prevLedger)
|
||||
{
|
||||
return peerValidations.proposersFinished(prevLedger);
|
||||
}
|
||||
|
||||
void
|
||||
onStartRound(Ledger const &) {}
|
||||
|
||||
void
|
||||
onClose(Ledger const &, bool ) {}
|
||||
|
||||
// don't really offload
|
||||
void
|
||||
dispatchAccept(TxSet const & f)
|
||||
Result
|
||||
onClose(Ledger const& prevLedger, NetClock::time_point closeTime, Mode mode)
|
||||
{
|
||||
Base::accept(f);
|
||||
TxSet res{openTxs};
|
||||
|
||||
return Result{TxSet{openTxs},
|
||||
Proposal{prevLedger.id(),
|
||||
Proposal::seqJoin,
|
||||
res.id(),
|
||||
closeTime,
|
||||
now(),
|
||||
id}};
|
||||
}
|
||||
|
||||
void
|
||||
share(TxSet const &s)
|
||||
onForceAccept(
|
||||
Result const& result,
|
||||
Ledger const& prevLedger,
|
||||
NetClock::duration const& closeResolution,
|
||||
CloseTimes const& rawCloseTimes,
|
||||
Mode const& mode)
|
||||
{
|
||||
relay(s);
|
||||
}
|
||||
|
||||
Ledger::ID
|
||||
getLCL(Ledger::ID const & currLedger,
|
||||
Ledger::ID const & priorLedger,
|
||||
bool haveCorrectLCL)
|
||||
{
|
||||
// TODO: Use generic validation code
|
||||
if(currLedger.seq > 0 && priorLedger.seq > 0)
|
||||
return peerValidations.getBestLCL(currLedger, priorLedger);
|
||||
return currLedger;
|
||||
onAccept(result, prevLedger, closeResolution, rawCloseTimes, mode);
|
||||
}
|
||||
|
||||
void
|
||||
propose(Proposal const & pos)
|
||||
onAccept(
|
||||
Result const& result,
|
||||
Ledger const& prevLedger,
|
||||
NetClock::duration const& closeResolution,
|
||||
CloseTimes const& rawCloseTimes,
|
||||
Mode const& mode)
|
||||
{
|
||||
if(proposing)
|
||||
relay(pos);
|
||||
}
|
||||
|
||||
void
|
||||
relay(DisputedTx<Tx, PeerID> const & dispute)
|
||||
{
|
||||
relay(dispute.tx());
|
||||
}
|
||||
|
||||
std::pair <TxSet, Proposal>
|
||||
makeInitialPosition(
|
||||
Ledger const & prevLedger,
|
||||
bool isProposing,
|
||||
bool isCorrectLCL,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now)
|
||||
{
|
||||
TxSet res{ openTxs };
|
||||
|
||||
return { res,
|
||||
Proposal{prevLedger.id(), Proposal::seqJoin, res.id(), closeTime, now, id} };
|
||||
}
|
||||
|
||||
// Process the accepted transaction set, generating the newly closed ledger
|
||||
// and clearing out the openTxs that were included.
|
||||
// TODO: Kinda nasty it takes so many arguments . . . sign of bad coupling
|
||||
bool
|
||||
accept(TxSet const& set,
|
||||
NetClock::time_point consensusCloseTime,
|
||||
bool proposing_,
|
||||
bool validating_,
|
||||
bool haveCorrectLCL_,
|
||||
bool consensusFail_,
|
||||
Ledger::ID const & prevLedgerHash_,
|
||||
Ledger const & previousLedger_,
|
||||
NetClock::duration closeResolution_,
|
||||
NetClock::time_point const & now,
|
||||
std::chrono::milliseconds const & roundTime_,
|
||||
hash_map<Tx::ID, DisputedTx <Tx, PeerID>> const & disputes_,
|
||||
std::map <NetClock::time_point, int> closeTimes_,
|
||||
NetClock::time_point const & closeTime)
|
||||
{
|
||||
auto newLedger = previousLedger_.close(set.txs_, closeResolution_,
|
||||
closeTime, consensusCloseTime != NetClock::time_point{});
|
||||
auto newLedger = prevLedger.close(
|
||||
result.set.txs_,
|
||||
closeResolution,
|
||||
rawCloseTimes.self,
|
||||
result.position.closeTime() != NetClock::time_point{});
|
||||
ledgers[newLedger.id()] = newLedger;
|
||||
|
||||
lastClosedLedger = newLedger;
|
||||
|
||||
auto it = std::remove_if(openTxs.begin(), openTxs.end(),
|
||||
[&](Tx const & tx)
|
||||
{
|
||||
return set.exists(tx.id());
|
||||
auto it =
|
||||
std::remove_if(openTxs.begin(), openTxs.end(), [&](Tx const& tx) {
|
||||
return result.set.exists(tx.id());
|
||||
});
|
||||
openTxs.erase(it, openTxs.end());
|
||||
|
||||
if(validating)
|
||||
if (validating_)
|
||||
relay(Validation{id, newLedger.id(), newLedger.parentID()});
|
||||
return validating_;
|
||||
}
|
||||
|
||||
void
|
||||
endConsensus(bool correct)
|
||||
{
|
||||
// kick off the next round...
|
||||
// in the actual implementation, this passes back through
|
||||
// network ops
|
||||
@@ -364,41 +302,58 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
// startRound sets the LCL state, so we need to call it once after
|
||||
// the last requested round completes
|
||||
// TODO: reconsider this and instead just save LCL generated here?
|
||||
if(completedLedgers <= targetLedgers)
|
||||
if (completedLedgers <= targetLedgers)
|
||||
{
|
||||
startRound(now(), lastClosedLedger.id(),
|
||||
lastClosedLedger);
|
||||
startRound(
|
||||
now(), lastClosedLedger.id(), lastClosedLedger, proposing_);
|
||||
}
|
||||
}
|
||||
|
||||
Ledger::ID
|
||||
getPrevLedger(Ledger::ID const& ledgerID, Ledger const& ledger, Mode mode)
|
||||
{
|
||||
// TODO: Use generic validation code
|
||||
if (mode != Mode::wrongLedger && ledgerID.seq > 0 &&
|
||||
ledger.id().seq > 0)
|
||||
return peerValidations.getBestLCL(ledgerID, ledger.parentID());
|
||||
return ledgerID;
|
||||
}
|
||||
|
||||
void
|
||||
propose(Proposal const& pos)
|
||||
{
|
||||
if (proposing_)
|
||||
relay(pos);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// non-callback helpers
|
||||
void
|
||||
receive(Proposal const & p)
|
||||
receive(Proposal const& p)
|
||||
{
|
||||
if(unl.find(p.nodeID()) == unl.end())
|
||||
if (unl.find(p.nodeID()) == unl.end())
|
||||
return;
|
||||
|
||||
// TODO: Be sure this is a new proposal!!!!!
|
||||
auto & dest = peerPositions_[p.prevLedger()];
|
||||
if(std::find(dest.begin(), dest.end(), p) != dest.end())
|
||||
auto& dest = peerPositions_[p.prevLedger()];
|
||||
if (std::find(dest.begin(), dest.end(), p) != dest.end())
|
||||
return;
|
||||
|
||||
dest.push_back(p);
|
||||
peerProposal(now(), p);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
receive(TxSet const & txs)
|
||||
receive(TxSet const& txs)
|
||||
{
|
||||
// save and map complete?
|
||||
auto it = txSets.insert(std::make_pair(txs.id(), txs));
|
||||
if(it.second)
|
||||
if (it.second)
|
||||
gotTxSet(now(), txs);
|
||||
}
|
||||
|
||||
void
|
||||
receive(Tx const & tx)
|
||||
receive(Tx const& tx)
|
||||
{
|
||||
if (openTxs.find(tx.id()) == openTxs.end())
|
||||
{
|
||||
@@ -409,33 +364,26 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
}
|
||||
|
||||
void
|
||||
receive(Validation const & v)
|
||||
receive(Validation const& v)
|
||||
{
|
||||
if(unl.find(v.id) != unl.end())
|
||||
if (unl.find(v.id) != unl.end())
|
||||
{
|
||||
schedule(validationDelay,
|
||||
[&, v]()
|
||||
{
|
||||
peerValidations.update(v);
|
||||
});
|
||||
schedule(validationDelay, [&, v]() { peerValidations.update(v); });
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
relay(T const & t)
|
||||
relay(T const& t)
|
||||
{
|
||||
for(auto const& link : net.links(this))
|
||||
net.send(this, link.to,
|
||||
[msg = t, to = link.to]
|
||||
{
|
||||
to->receive(msg);
|
||||
});
|
||||
for (auto const& link : net.links(this))
|
||||
net.send(
|
||||
this, link.to, [ msg = t, to = link.to ] { to->receive(msg); });
|
||||
}
|
||||
|
||||
// Receive and relay locally submitted transaction
|
||||
void
|
||||
submit(Tx const & tx)
|
||||
submit(Tx const& tx)
|
||||
{
|
||||
receive(tx);
|
||||
relay(tx);
|
||||
@@ -446,7 +394,7 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
{
|
||||
Base::timerEntry(now());
|
||||
// only reschedule if not completed
|
||||
if(completedLedgers < targetLedgers)
|
||||
if (completedLedgers < targetLedgers)
|
||||
net.timer(LEDGER_GRANULARITY, [&]() { timerEntry(); });
|
||||
}
|
||||
void
|
||||
@@ -456,10 +404,9 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
// The ID is the one we have seen the most validations for
|
||||
// In practice, we might not actually have that ledger itself yet,
|
||||
// so there is no gaurantee that bestLCL == lastClosedLedger.id()
|
||||
auto bestLCL = peerValidations.getBestLCL(lastClosedLedger.id(),
|
||||
lastClosedLedger.parentID());
|
||||
startRound(now(), bestLCL,
|
||||
lastClosedLedger);
|
||||
auto bestLCL = peerValidations.getBestLCL(
|
||||
lastClosedLedger.id(), lastClosedLedger.parentID());
|
||||
startRound(now(), bestLCL, lastClosedLedger, proposing_);
|
||||
}
|
||||
|
||||
NetClock::time_point
|
||||
@@ -470,23 +417,24 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
// any subtractions of two NetClock::time_point in the consensu
|
||||
// code are positive. (e.g. PROPOSE_FRESHNESS)
|
||||
using namespace std::chrono;
|
||||
return NetClock::time_point(duration_cast<NetClock::duration>
|
||||
(net.now().time_since_epoch()+ 86400s + clockSkew));
|
||||
return NetClock::time_point(duration_cast<NetClock::duration>(
|
||||
net.now().time_since_epoch() + 86400s + clockSkew));
|
||||
}
|
||||
|
||||
// Schedule the provided callback in `when` duration, but if
|
||||
// `when` is 0, call immediately
|
||||
template <class T>
|
||||
void schedule(std::chrono::nanoseconds when, T && what)
|
||||
void
|
||||
schedule(std::chrono::nanoseconds when, T&& what)
|
||||
{
|
||||
if(when == 0ns)
|
||||
if (when == 0ns)
|
||||
what();
|
||||
else
|
||||
net.timer(when, std::forward<T>(what));
|
||||
}
|
||||
};
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
#endif
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#ifndef RIPPLE_TEST_CSF_SIM_H_INCLUDED
|
||||
#define RIPPLE_TEST_CSF_SIM_H_INCLUDED
|
||||
|
||||
#include <test/csf/UNL.h>
|
||||
#include <test/csf/BasicNetwork.h>
|
||||
#include <test/csf/UNL.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -50,19 +50,19 @@ public:
|
||||
|
||||
*/
|
||||
template <class Topology>
|
||||
Sim(TrustGraph const & g, Topology const & top)
|
||||
Sim(TrustGraph const& g, Topology const& top)
|
||||
{
|
||||
peers.reserve(g.numPeers());
|
||||
for(int i = 0; i < g.numPeers(); ++i)
|
||||
for (int i = 0; i < g.numPeers(); ++i)
|
||||
peers.emplace_back(i, net, g.unl(i));
|
||||
|
||||
for(int i = 0; i < peers.size(); ++i)
|
||||
for (int i = 0; i < peers.size(); ++i)
|
||||
{
|
||||
for(int j = 0; j < peers.size(); ++j)
|
||||
for (int j = 0; j < peers.size(); ++j)
|
||||
{
|
||||
if( i != j)
|
||||
if (i != j)
|
||||
{
|
||||
auto d = top(i,j);
|
||||
auto d = top(i, j);
|
||||
if (d)
|
||||
{
|
||||
net.connect(&peers[i], &peers[j], *d);
|
||||
@@ -81,10 +81,10 @@ public:
|
||||
void
|
||||
run(int ledgers)
|
||||
{
|
||||
for (auto & p : peers)
|
||||
for (auto& p : peers)
|
||||
{
|
||||
if(p.completedLedgers == 0)
|
||||
p.relay(Validation{p.id, p.LCL(), p.LCL()});
|
||||
if (p.completedLedgers == 0)
|
||||
p.relay(Validation{p.id, p.prevLedgerID(), p.prevLedgerID()});
|
||||
p.targetLedgers = p.completedLedgers + ledgers;
|
||||
p.start();
|
||||
}
|
||||
@@ -93,12 +93,10 @@ public:
|
||||
|
||||
std::vector<Peer> peers;
|
||||
BasicNetwork<Peer*> net;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
#include <ripple/beast/hash/hash_append.h>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -35,7 +35,9 @@ class Tx
|
||||
public:
|
||||
using ID = std::uint32_t;
|
||||
|
||||
Tx(ID i) : id_{ i } {}
|
||||
Tx(ID i) : id_{i}
|
||||
{
|
||||
}
|
||||
|
||||
ID
|
||||
id() const
|
||||
@@ -44,21 +46,19 @@ public:
|
||||
}
|
||||
|
||||
bool
|
||||
operator<(Tx const & o) const
|
||||
operator<(Tx const& o) const
|
||||
{
|
||||
return id_ < o.id_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(Tx const & o) const
|
||||
operator==(Tx const& o) const
|
||||
{
|
||||
return id_ == o.id_;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
ID id_;
|
||||
|
||||
};
|
||||
|
||||
//!-------------------------------------------------------------------------
|
||||
@@ -74,37 +74,39 @@ public:
|
||||
using MutableTxSet = TxSet;
|
||||
|
||||
TxSet() = default;
|
||||
TxSet(TxSetType const & s) : txs_{ s } {}
|
||||
TxSet(TxSetType const& s) : txs_{s}
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
insert(Tx const & t)
|
||||
insert(Tx const& t)
|
||||
{
|
||||
return txs_.insert(t).second;
|
||||
}
|
||||
|
||||
bool
|
||||
erase(Tx::ID const & txId)
|
||||
erase(Tx::ID const& txId)
|
||||
{
|
||||
return txs_.erase(Tx{ txId }) > 0;
|
||||
return txs_.erase(Tx{txId}) > 0;
|
||||
}
|
||||
|
||||
bool
|
||||
exists(Tx::ID const txId) const
|
||||
{
|
||||
auto it = txs_.find(Tx{ txId });
|
||||
auto it = txs_.find(Tx{txId});
|
||||
return it != txs_.end();
|
||||
}
|
||||
|
||||
Tx const *
|
||||
Tx const*
|
||||
find(Tx::ID const& txId) const
|
||||
{
|
||||
auto it = txs_.find(Tx{ txId });
|
||||
auto it = txs_.find(Tx{txId});
|
||||
if (it != txs_.end())
|
||||
return &(*it);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto const &
|
||||
auto const&
|
||||
id() const
|
||||
{
|
||||
return txs_;
|
||||
@@ -119,19 +121,14 @@ public:
|
||||
{
|
||||
std::map<Tx::ID, bool> res;
|
||||
|
||||
auto populate_diffs = [&res](auto const & a, auto const & b, bool s)
|
||||
{
|
||||
auto populator = [&](auto const & tx)
|
||||
{
|
||||
res[tx.id()] = s;
|
||||
};
|
||||
auto populate_diffs = [&res](auto const& a, auto const& b, bool s) {
|
||||
auto populator = [&](auto const& tx) { res[tx.id()] = s; };
|
||||
std::set_difference(
|
||||
a.begin(), a.end(),
|
||||
b.begin(), b.end(),
|
||||
boost::make_function_output_iterator(
|
||||
std::ref(populator)
|
||||
)
|
||||
);
|
||||
a.begin(),
|
||||
a.end(),
|
||||
b.begin(),
|
||||
b.end(),
|
||||
boost::make_function_output_iterator(std::ref(populator)));
|
||||
};
|
||||
|
||||
populate_diffs(txs_, other.txs_, true);
|
||||
@@ -143,55 +140,35 @@ public:
|
||||
TxSetType txs_;
|
||||
};
|
||||
|
||||
|
||||
/** The RCL consensus process catches missing node SHAMap error
|
||||
in several points. This exception is meant to represent a similar
|
||||
case for the unit test.
|
||||
*/
|
||||
class MissingTx : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
MissingTx()
|
||||
: std::runtime_error("MissingTx")
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper functions for debug printing
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream & o, const Tx & t)
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& o, const Tx& t)
|
||||
{
|
||||
return o << t.id();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream & o, boost::container::flat_set<T> const & ts)
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& o, boost::container::flat_set<T> const& ts)
|
||||
{
|
||||
o << "{ ";
|
||||
bool do_comma = false;
|
||||
for (auto const & t : ts)
|
||||
for (auto const& t : ts)
|
||||
{
|
||||
if (do_comma)
|
||||
o << ", ";
|
||||
else
|
||||
do_comma = true;
|
||||
o << t;
|
||||
|
||||
|
||||
}
|
||||
o << " }";
|
||||
return o;
|
||||
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
to_string(TxSetType const & txs)
|
||||
inline std::string
|
||||
to_string(TxSetType const& txs)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << txs;
|
||||
@@ -199,23 +176,15 @@ to_string(TxSetType const & txs)
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
inline
|
||||
void
|
||||
hash_append(Hasher& h, Tx const & tx)
|
||||
inline void
|
||||
hash_append(Hasher& h, Tx const& tx)
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, tx.id());
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream & o, MissingTx const &m)
|
||||
{
|
||||
return o << m.what();
|
||||
}
|
||||
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -42,7 +42,7 @@ namespace csf {
|
||||
*/
|
||||
template <class T, class G>
|
||||
std::vector<T>
|
||||
random_weighted_shuffle(std::vector<T> v, std::vector<double> w, G & g)
|
||||
random_weighted_shuffle(std::vector<T> v, std::vector<double> w, G& g)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
@@ -57,7 +57,6 @@ random_weighted_shuffle(std::vector<T> v, std::vector<double> w, G & g)
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/** Power-law distribution with PDF
|
||||
|
||||
P(x) = (x/xmin)^-a
|
||||
@@ -69,25 +68,22 @@ class PowerLawDistribution
|
||||
double xmin_;
|
||||
double a_;
|
||||
double inv_;
|
||||
std::uniform_real_distribution<double> uf_{0,1};
|
||||
std::uniform_real_distribution<double> uf_{0, 1};
|
||||
|
||||
public:
|
||||
PowerLawDistribution(double xmin, double a)
|
||||
: xmin_{xmin}, a_{a}
|
||||
{
|
||||
inv_ = 1.0/(1.0 - a_);
|
||||
}
|
||||
PowerLawDistribution(double xmin, double a) : xmin_{xmin}, a_{a}
|
||||
{
|
||||
inv_ = 1.0 / (1.0 - a_);
|
||||
}
|
||||
|
||||
template <class Generator>
|
||||
inline
|
||||
double
|
||||
operator()(Generator & g)
|
||||
inline double
|
||||
operator()(Generator& g)
|
||||
{
|
||||
// use inverse transform of CDF to sample
|
||||
// CDF is P(X <= x): 1 - (x/xmin)^(1-a)
|
||||
return xmin_ * std::pow(1 - uf_(g), inv_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//< Unique identifier for each node in the network
|
||||
@@ -110,25 +106,23 @@ class TrustGraph
|
||||
std::vector<UNL> UNLs_;
|
||||
|
||||
std::vector<int> assignment_;
|
||||
public:
|
||||
|
||||
public:
|
||||
//< Constructor
|
||||
TrustGraph(std::vector<UNL> UNLs, std::vector<int> assignment)
|
||||
: UNLs_{UNLs}
|
||||
, assignment_{assignment}
|
||||
{}
|
||||
: UNLs_{UNLs}, assignment_{assignment}
|
||||
{
|
||||
}
|
||||
|
||||
//< Whether node `i` trusts node `j`
|
||||
inline
|
||||
bool
|
||||
inline bool
|
||||
trusts(PeerID i, PeerID j) const
|
||||
{
|
||||
return unl(i).find(j) != unl(i).end();
|
||||
}
|
||||
|
||||
//< Get the UNL for node `i`
|
||||
inline
|
||||
UNL const &
|
||||
inline UNL const&
|
||||
unl(PeerID i) const
|
||||
{
|
||||
return UNLs_[assignment_[i]];
|
||||
@@ -138,7 +132,6 @@ public:
|
||||
bool
|
||||
canFork(double quorum) const;
|
||||
|
||||
|
||||
auto
|
||||
numPeers() const
|
||||
{
|
||||
@@ -147,7 +140,7 @@ public:
|
||||
|
||||
//< Save grapviz dot file reprentation of the trust graph
|
||||
void
|
||||
save_dot(std::string const & fileName);
|
||||
save_dot(std::string const& fileName);
|
||||
|
||||
/** Generate a random trust graph based on random ranking of peers
|
||||
|
||||
@@ -176,28 +169,23 @@ public:
|
||||
|
||||
*/
|
||||
template <class RankPDF, class SizePDF, class Generator>
|
||||
static
|
||||
TrustGraph
|
||||
makeRandomRanked(int size,
|
||||
int numUNLs,
|
||||
RankPDF rankPDF,
|
||||
SizePDF unlSizePDF,
|
||||
Generator & g)
|
||||
static TrustGraph
|
||||
makeRandomRanked(
|
||||
int size,
|
||||
int numUNLs,
|
||||
RankPDF rankPDF,
|
||||
SizePDF unlSizePDF,
|
||||
Generator& g)
|
||||
{
|
||||
|
||||
|
||||
// 1. Generate ranks
|
||||
std::vector<double> weights(size);
|
||||
std::generate(weights.begin(), weights.end(), [&]()
|
||||
{
|
||||
return rankPDF(g);
|
||||
});
|
||||
std::generate(
|
||||
weights.begin(), weights.end(), [&]() { return rankPDF(g); });
|
||||
|
||||
// 2. Generate UNLs based on sampling without replacement according
|
||||
// to weights
|
||||
std::vector<UNL> unls(numUNLs);
|
||||
std::generate(unls.begin(), unls.end(), [&]()
|
||||
{
|
||||
std::generate(unls.begin(), unls.end(), [&]() {
|
||||
std::vector<PeerID> ids(size);
|
||||
std::iota(ids.begin(), ids.end(), 0);
|
||||
auto res = random_weighted_shuffle(ids, weights, g);
|
||||
@@ -206,12 +194,9 @@ public:
|
||||
|
||||
// 3. Assign membership
|
||||
std::vector<int> assignment(size);
|
||||
std::uniform_int_distribution<int> u(0, numUNLs-1);
|
||||
std::generate(assignment.begin(), assignment.end(),
|
||||
[&]()
|
||||
{
|
||||
return u(g);
|
||||
});
|
||||
std::uniform_int_distribution<int> u(0, numUNLs - 1);
|
||||
std::generate(
|
||||
assignment.begin(), assignment.end(), [&]() { return u(g); });
|
||||
|
||||
return TrustGraph(unls, assignment);
|
||||
}
|
||||
@@ -226,8 +211,7 @@ public:
|
||||
@param size The number of nodes in the trust graph
|
||||
@param overlap The number of nodes trusting both cliques
|
||||
*/
|
||||
static
|
||||
TrustGraph
|
||||
static TrustGraph
|
||||
makeClique(int size, int overlap);
|
||||
|
||||
/** Generate a complete (fully-connect) trust graph
|
||||
@@ -237,21 +221,17 @@ public:
|
||||
|
||||
@param size The number of nodes in the trust graph
|
||||
*/
|
||||
static
|
||||
TrustGraph
|
||||
static TrustGraph
|
||||
makeComplete(int size);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//< Make the TrustGraph into a topology with delays given by DelayModel
|
||||
template <class DelayModel>
|
||||
auto
|
||||
topology(TrustGraph const & tg, DelayModel const & d)
|
||||
topology(TrustGraph const& tg, DelayModel const& d)
|
||||
{
|
||||
return [&](PeerID i, PeerID j)
|
||||
{
|
||||
return tg.trusts(i,j) ? boost::make_optional(d(i,j)) : boost::none;
|
||||
return [&](PeerID i, PeerID j) {
|
||||
return tg.trusts(i, j) ? boost::make_optional(d(i, j)) : boost::none;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -260,18 +240,19 @@ class fixed
|
||||
std::chrono::nanoseconds d_;
|
||||
|
||||
public:
|
||||
fixed(std::chrono::nanoseconds const & d) : d_{d} {}
|
||||
fixed(std::chrono::nanoseconds const& d) : d_{d}
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
std::chrono::nanoseconds
|
||||
operator()(PeerID const & i, PeerID const & j) const
|
||||
inline std::chrono::nanoseconds
|
||||
operator()(PeerID const& i, PeerID const& j) const
|
||||
{
|
||||
return d_;
|
||||
}
|
||||
};
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <test/csf/UNL.h>
|
||||
#include <boost/iterator/counting_iterator.hpp>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <test/csf/UNL.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -38,8 +38,8 @@ TrustGraph::canFork(double quorum) const
|
||||
|
||||
for (int i = 0; i < assignment_.size(); ++i)
|
||||
{
|
||||
auto const & myUNL = UNLs_[assignment_[i]];
|
||||
if(myUNL.find(i) == myUNL.end())
|
||||
auto const& myUNL = UNLs_[assignment_[i]];
|
||||
if (myUNL.find(i) == myUNL.end())
|
||||
{
|
||||
auto myUNLcopy = myUNL;
|
||||
myUNLcopy.insert(i);
|
||||
@@ -50,21 +50,20 @@ TrustGraph::canFork(double quorum) const
|
||||
// Loop over all pairs of uniqueUNLs
|
||||
for (int i = 0; i < uniqueUNLs.size(); ++i)
|
||||
{
|
||||
for (int j = (i+1); j < uniqueUNLs.size(); ++j)
|
||||
for (int j = (i + 1); j < uniqueUNLs.size(); ++j)
|
||||
{
|
||||
auto const & unlA = uniqueUNLs[i];
|
||||
auto const & unlB = uniqueUNLs[j];
|
||||
auto const& unlA = uniqueUNLs[i];
|
||||
auto const& unlB = uniqueUNLs[j];
|
||||
|
||||
double rhs = 2.0*(1.-quorum) *
|
||||
std::max(unlA.size(), unlB.size() );
|
||||
double rhs =
|
||||
2.0 * (1. - quorum) * std::max(unlA.size(), unlB.size());
|
||||
|
||||
int intersectionSize = std::count_if(unlA.begin(), unlA.end(),
|
||||
[&](PeerID id)
|
||||
{
|
||||
int intersectionSize =
|
||||
std::count_if(unlA.begin(), unlA.end(), [&](PeerID id) {
|
||||
return unlB.find(id) != unlB.end();
|
||||
});
|
||||
|
||||
if(intersectionSize < rhs)
|
||||
if (intersectionSize < rhs)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -80,56 +79,53 @@ TrustGraph::makeClique(int size, int overlap)
|
||||
// Clique A has nodes [0,endA) and Clique B has [startB,numPeers)
|
||||
// Note: Clique B will have an extra peer when numPeers - overlap
|
||||
// is odd
|
||||
int endA = (size + overlap)/2;
|
||||
int startB = (size - overlap)/2;
|
||||
int endA = (size + overlap) / 2;
|
||||
int startB = (size - overlap) / 2;
|
||||
|
||||
std::vector<UNL> unls;
|
||||
unls.emplace_back(bci(0), bci(endA));
|
||||
unls.emplace_back(bci(startB), bci(size));
|
||||
unls.emplace_back(bci(0), bci(size));
|
||||
|
||||
std::vector<int> assignment(size,0);
|
||||
std::vector<int> assignment(size, 0);
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
if(i < startB)
|
||||
if (i < startB)
|
||||
assignment[i] = 0;
|
||||
else if(i > endA)
|
||||
else if (i > endA)
|
||||
assignment[i] = 1;
|
||||
else
|
||||
assignment[i] = 2;
|
||||
}
|
||||
|
||||
|
||||
return TrustGraph(unls, assignment);
|
||||
}
|
||||
|
||||
TrustGraph
|
||||
TrustGraph::makeComplete(int size)
|
||||
{
|
||||
UNL all{ boost::counting_iterator<PeerID>( 0 ),
|
||||
boost::counting_iterator<PeerID>( size ) };
|
||||
UNL all{boost::counting_iterator<PeerID>(0),
|
||||
boost::counting_iterator<PeerID>(size)};
|
||||
|
||||
return TrustGraph(std::vector<UNL>(1,all),
|
||||
std::vector<int>(size, 0));
|
||||
return TrustGraph(std::vector<UNL>(1, all), std::vector<int>(size, 0));
|
||||
}
|
||||
|
||||
inline void TrustGraph::save_dot(std::string const & fileName)
|
||||
inline void
|
||||
TrustGraph::save_dot(std::string const& fileName)
|
||||
{
|
||||
std::ofstream out(fileName);
|
||||
out << "digraph {\n";
|
||||
for (int i = 0; i < assignment_.size(); ++i)
|
||||
{
|
||||
for (auto & j : UNLs_[assignment_[i]])
|
||||
for (auto& j : UNLs_[assignment_[i]])
|
||||
{
|
||||
out << i << " -> " << j << ";\n";
|
||||
}
|
||||
|
||||
}
|
||||
out << "}\n";
|
||||
|
||||
}
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
Reference in New Issue
Block a user