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:
Brad Chase
2017-02-03 12:31:08 -05:00
committed by Scott Schurr
parent d5dc715d9c
commit 00c60d408a
33 changed files with 2935 additions and 2639 deletions

View File

@@ -17,22 +17,20 @@
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/beast/clock/manual_clock.h>
#include <ripple/beast/unit_test.h>
#include <ripple/consensus/Consensus.h>
#include <ripple/consensus/ConsensusProposal.h>
#include <ripple/beast/clock/manual_clock.h>
#include <boost/function_output_iterator.hpp>
#include <test/csf.h>
#include <utility>
namespace ripple {
namespace test {
class Consensus_test : public beast::unit_test::suite
{
public:
void
testStandalone()
{
@@ -41,21 +39,22 @@ public:
auto tg = TrustGraph::makeComplete(1);
Sim s(tg, topology(tg, fixed{LEDGER_GRANULARITY}));
auto & p = s.peers[0];
auto& p = s.peers[0];
p.targetLedgers = 1;
p.start();
p.submit(Tx{ 1 });
p.submit(Tx{1});
s.net.step();
// Inspect that the proper ledger was created
BEAST_EXPECT(p.LCL().seq == 1);
BEAST_EXPECT(p.LCL() == p.lastClosedLedger.id());
BEAST_EXPECT(p.prevLedgerID().seq == 1);
BEAST_EXPECT(p.prevLedgerID() == p.lastClosedLedger.id());
BEAST_EXPECT(p.lastClosedLedger.id().txs.size() == 1);
BEAST_EXPECT(p.lastClosedLedger.id().txs.find(Tx{ 1 })
!= p.lastClosedLedger.id().txs.end());
BEAST_EXPECT(p.getLastCloseProposers() == 0);
BEAST_EXPECT(
p.lastClosedLedger.id().txs.find(Tx{1}) !=
p.lastClosedLedger.id().txs.end());
BEAST_EXPECT(p.prevProposers() == 0);
}
void
@@ -65,24 +64,25 @@ public:
using namespace std::chrono;
auto tg = TrustGraph::makeComplete(5);
Sim sim(tg,
Sim sim(
tg,
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
// everyone submits their own ID as a TX and relay it to peers
for (auto & p : sim.peers)
for (auto& p : sim.peers)
p.submit(Tx(p.id));
// Verify all peers have the same LCL and it has all the Txs
sim.run(1);
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
auto const &lgrID = p.LCL();
auto const& lgrID = p.prevLedgerID();
BEAST_EXPECT(lgrID.seq == 1);
BEAST_EXPECT(p.getLastCloseProposers() == sim.peers.size() - 1);
for(std::uint32_t i = 0; i < sim.peers.size(); ++i)
BEAST_EXPECT(lgrID.txs.find(Tx{ i }) != lgrID.txs.end());
BEAST_EXPECT(p.prevProposers() == sim.peers.size() - 1);
for (std::uint32_t i = 0; i < sim.peers.size(); ++i)
BEAST_EXPECT(lgrID.txs.find(Tx{i}) != lgrID.txs.end());
// Matches peer 0 ledger
BEAST_EXPECT(lgrID.txs == sim.peers[0].LCL().txs);
BEAST_EXPECT(lgrID.txs == sim.peers[0].prevLedgerID().txs);
}
}
@@ -96,70 +96,69 @@ public:
// 1. The slow peer is participating in consensus
// 2. The slow peer is just observing
for(auto isParticipant : {true, false})
for (auto isParticipant : {true, false})
{
auto tg = TrustGraph::makeComplete(5);
Sim sim(tg, topology(tg,[](PeerID i, PeerID j)
{
auto delayFactor = (i == 0 || j == 0) ? 1.1 : 0.2;
return round<milliseconds>(delayFactor* LEDGER_GRANULARITY);
}));
Sim sim(tg, topology(tg, [](PeerID i, PeerID j) {
auto delayFactor = (i == 0 || j == 0) ? 1.1 : 0.2;
return round<milliseconds>(
delayFactor * LEDGER_GRANULARITY);
}));
sim.peers[0].proposing = sim.peers[0].validating = isParticipant;
sim.peers[0].proposing_ = sim.peers[0].validating_ = isParticipant;
// All peers submit their own ID as a transaction and relay it to peers
for (auto & p : sim.peers)
// All peers submit their own ID as a transaction and relay it to
// peers
for (auto& p : sim.peers)
{
p.submit(Tx{ p.id });
p.submit(Tx{p.id});
}
sim.run(1);
// Verify all peers have same LCL but are missing transaction 0 which
// was not received by all peers before the ledger closed
for (auto & p : sim.peers)
// Verify all peers have same LCL but are missing transaction 0
// which was not received by all peers before the ledger closed
for (auto& p : sim.peers)
{
auto const &lgrID = p.LCL();
auto const& lgrID = p.prevLedgerID();
BEAST_EXPECT(lgrID.seq == 1);
// If peer 0 is participating
if(isParticipant)
if (isParticipant)
{
BEAST_EXPECT(p.getLastCloseProposers()
== sim.peers.size() - 1);
// Peer 0 closes first because it sees a quorum of agreeing positions
// from all other peers in one hop (1->0, 2->0, ..)
// The other peers take an extra timer period before they find that
// Peer 0 agrees with them ( 1->0->1, 2->0->2, ...)
if(p.id != 0)
BEAST_EXPECT(p.getLastConvergeDuration()
> sim.peers[0].getLastConvergeDuration());
BEAST_EXPECT(p.prevProposers() == sim.peers.size() - 1);
// Peer 0 closes first because it sees a quorum of agreeing
// positions from all other peers in one hop (1->0, 2->0,
// ..) The other peers take an extra timer period before
// they find that Peer 0 agrees with them ( 1->0->1,
// 2->0->2, ...)
if (p.id != 0)
BEAST_EXPECT(
p.prevRoundTime() > sim.peers[0].prevRoundTime());
}
else // peer 0 is not participating
else // peer 0 is not participating
{
auto const proposers = p.getLastCloseProposers();
if(p.id == 0)
auto const proposers = p.prevProposers();
if (p.id == 0)
BEAST_EXPECT(proposers == sim.peers.size() - 1);
else
BEAST_EXPECT(proposers == sim.peers.size() - 2);
// so all peers should have closed together
BEAST_EXPECT(p.getLastConvergeDuration()
== sim.peers[0].getLastConvergeDuration());
BEAST_EXPECT(
p.prevRoundTime() == sim.peers[0].prevRoundTime());
}
BEAST_EXPECT(lgrID.txs.find(Tx{ 0 }) == lgrID.txs.end());
for(std::uint32_t i = 1; i < sim.peers.size(); ++i)
BEAST_EXPECT(lgrID.txs.find(Tx{ i }) != lgrID.txs.end());
BEAST_EXPECT(lgrID.txs.find(Tx{0}) == lgrID.txs.end());
for (std::uint32_t i = 1; i < sim.peers.size(); ++i)
BEAST_EXPECT(lgrID.txs.find(Tx{i}) != lgrID.txs.end());
// Matches peer 0 ledger
BEAST_EXPECT(lgrID.txs == sim.peers[0].LCL().txs);
BEAST_EXPECT(lgrID.txs == sim.peers[0].prevLedgerID().txs);
}
BEAST_EXPECT(sim.peers[0].openTxs.find(Tx{ 0 })
!= sim.peers[0].openTxs.end());
}
BEAST_EXPECT(
sim.peers[0].openTxs.find(Tx{0}) != sim.peers[0].openTxs.end());
}
}
void
@@ -185,32 +184,35 @@ public:
// Complicating this matter is that nodes will ignore proposals
// with times more than PROPOSE_FRESHNESS =20s in the past. So at
// the minimum granularity, we have at most 3 types of skews (0s,10s,20s).
// the minimum granularity, we have at most 3 types of skews
// (0s,10s,20s).
// This test therefore has 6 nodes, with 2 nodes having each type of
// skew. Then no majority (1/3 < 1/2) of nodes will agree on an
// actual close time.
auto tg = TrustGraph::makeComplete(6);
Sim sim(tg,
Sim sim(
tg,
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
// Run consensus without skew until we have a short close time resolution
while(sim.peers.front().lastClosedLedger.closeTimeResolution() >=
PROPOSE_FRESHNESS)
// Run consensus without skew until we have a short close time
// resolution
while (sim.peers.front().lastClosedLedger.closeTimeResolution() >=
PROPOSE_FRESHNESS)
sim.run(1);
// Introduce a shift on the time of half the peers
sim.peers[0].clockSkew = PROPOSE_FRESHNESS/2;
sim.peers[1].clockSkew = PROPOSE_FRESHNESS/2;
sim.peers[0].clockSkew = PROPOSE_FRESHNESS / 2;
sim.peers[1].clockSkew = PROPOSE_FRESHNESS / 2;
sim.peers[2].clockSkew = PROPOSE_FRESHNESS;
sim.peers[3].clockSkew = PROPOSE_FRESHNESS;
// Verify all peers have the same LCL and it has all the Txs
sim.run(1);
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
BEAST_EXPECT(! p.lastClosedLedger.closeAgree());
BEAST_EXPECT(!p.lastClosedLedger.closeAgree());
}
}
@@ -224,9 +226,8 @@ public:
// Vary the time it takes to process validations to exercise detecting
// the wrong LCL at different phases of consensus
for(auto validationDelay : {0s, LEDGER_MIN_CLOSE})
for (auto validationDelay : {0s, LEDGER_MIN_CLOSE})
{
// Consider 10 peers:
// 0 1 2 3 4 5 6 7 8 9
//
@@ -241,19 +242,21 @@ public:
// since nodes 2-4 will validate a different ledger.
// Nodes 0-1 will acquire the proper ledger from the network and
// resume consensus and eventually generate the dominant network ledger
// resume consensus and eventually generate the dominant network
// ledger
std::vector<UNL> unls;
unls.push_back({2,3,4,5,6,7,8,9});
unls.push_back({0,1,2,3,4});
std::vector<int> membership(10,0);
unls.push_back({2, 3, 4, 5, 6, 7, 8, 9});
unls.push_back({0, 1, 2, 3, 4});
std::vector<int> membership(10, 0);
membership[0] = 1;
membership[1] = 1;
TrustGraph tg{unls, membership};
// This topology can fork, which is why we are using it for this test.
BEAST_EXPECT(tg.canFork(minimumConsensusPercentage/100.));
// This topology can fork, which is why we are using it for this
// test.
BEAST_EXPECT(tg.canFork(minimumConsensusPercentage / 100.));
auto netDelay = round<milliseconds>(0.2 * LEDGER_GRANULARITY);
Sim sim(tg, topology(tg, fixed{netDelay}));
@@ -261,8 +264,9 @@ public:
// initial round to set prior state
sim.run(1);
// Nodes in smaller UNL have seen tx 0, nodes in other unl have seen tx 1
for (auto & p : sim.peers)
// Nodes in smaller UNL have seen tx 0, nodes in other unl have seen
// tx 1
for (auto& p : sim.peers)
{
p.validationDelay = validationDelay;
p.missingLedgerDelay = netDelay;
@@ -272,16 +276,29 @@ public:
p.openTxs.insert(Tx{1});
}
// Run for 2 additional rounds
// - One round to generate different ledgers
// - One round to detect different prior ledgers (but still generate
// wrong ones) and recover
sim.run(2);
// Run for additional rounds
// With no validation delay, only 2 more rounds are needed.
// 1. Round to generate different ledgers
// 2. Round to detect different prior ledgers (but still generate
// wrong ones) and recover within that round since wrong LCL
// is detected before we close
//
// With a validation delay of LEDGER_MIN_CLOSE, we need 3 more
// rounds.
// 1. Round to generate different ledgers
// 2. Round to detect different prior ledgers (but still generate
// wrong ones) but end up declaring consensus on wrong LCL (but
// with the right transaction set!). This is because we detect
// the wrong LCL after we have closed the ledger, so we declare
// consensus based solely on our peer proposals. But we haven't
// had time to acquire the right LCL
// 3. Round to correct
sim.run(3);
bc::flat_map<int, bc::flat_set<Ledger::ID>> ledgers;
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
for (auto const & l : p.ledgers)
for (auto const& l : p.ledgers)
{
ledgers[l.first.seq].insert(l.first);
}
@@ -289,12 +306,19 @@ public:
BEAST_EXPECT(ledgers[0].size() == 1);
BEAST_EXPECT(ledgers[1].size() == 1);
BEAST_EXPECT(ledgers[2].size() == 2);
BEAST_EXPECT(ledgers[3].size() == 1);
if (validationDelay == 0s)
{
BEAST_EXPECT(ledgers[2].size() == 2);
BEAST_EXPECT(ledgers[3].size() == 1);
BEAST_EXPECT(ledgers[4].size() == 1);
}
else
{
BEAST_EXPECT(ledgers[2].size() == 2);
BEAST_EXPECT(ledgers[3].size() == 2);
BEAST_EXPECT(ledgers[4].size() == 1);
}
}
// Additional test engineered to switch LCL during the establish phase.
// This was added to trigger a scenario that previously crashed, in which
// switchLCL switched from establish to open phase, but still processed
@@ -330,7 +354,7 @@ public:
// Check all peers recovered
for (auto &p : sim.peers)
BEAST_EXPECT(p.LCL() == sim.peers[0].LCL());
BEAST_EXPECT(p.prevLedgerID() == sim.peers[0].prevLedgerID());
}
}
@@ -341,43 +365,43 @@ public:
using namespace std::chrono;
int numPeers = 10;
for(int overlap = 0; overlap <= numPeers; ++overlap)
for (int overlap = 0; overlap <= numPeers; ++overlap)
{
auto tg = TrustGraph::makeClique(numPeers, overlap);
Sim sim(tg, topology(tg,
fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
Sim sim(
tg,
topology(
tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
// Initial round to set prior state
sim.run(1);
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
// Nodes have only seen transactions from their neighbors
p.openTxs.insert(Tx{p.id});
for(auto const link : sim.net.links(&p))
for (auto const link : sim.net.links(&p))
p.openTxs.insert(Tx{link.to->id});
}
sim.run(1);
// See if the network forked
bc::flat_set<Ledger::ID> ledgers;
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
ledgers.insert(p.LCL());
ledgers.insert(p.prevLedgerID());
}
// Fork should not happen for 40% or greater overlap
// Since the overlapped nodes have a UNL that is the union of the
// two cliques, the maximum sized UNL list is the number of peers
if(overlap > 0.4 * numPeers)
if (overlap > 0.4 * numPeers)
BEAST_EXPECT(ledgers.size() == 1);
else // Even if we do fork, there shouldn't be more than 3 ledgers
// One for cliqueA, one for cliqueB and one for nodes in both
BEAST_EXPECT(ledgers.size() <= 3);
else // Even if we do fork, there shouldn't be more than 3 ledgers
// One for cliqueA, one for cliqueB and one for nodes in both
BEAST_EXPECT(ledgers.size() <= 3);
}
}
void
simClockSkew()
{
@@ -396,52 +420,44 @@ public:
// Disabled while continuing to understand testt.
for(auto stagger : {800ms, 1600ms, 3200ms, 30000ms, 45000ms, 300000ms})
for (auto stagger : {800ms, 1600ms, 3200ms, 30000ms, 45000ms, 300000ms})
{
auto tg = TrustGraph::makeComplete(5);
Sim sim(tg, topology(tg, [](PeerID i, PeerID)
{
return 200ms * (i + 1);
}));
Sim sim(tg, topology(tg, [](PeerID i, PeerID) {
return 200ms * (i + 1);
}));
// all transactions submitted before starting
// Initial round to set prior state
sim.run(1);
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
p.openTxs.insert(Tx{ 0 });
p.openTxs.insert(Tx{0});
p.targetLedgers = p.completedLedgers + 1;
}
// stagger start of consensus
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
p.start();
sim.net.step_for(stagger);
}
// run until all peers have accepted all transactions
sim.net.step_while([&]()
{
for(auto & p : sim.peers)
{
if(p.LCL().txs.size() != 1)
sim.net.step_while([&]() {
for (auto& p : sim.peers)
{
if (p.prevLedgerID().txs.size() != 1)
{
return true;
}
}
return false;
}
return false;
});
}
}
void
simScaleFree()
{
@@ -450,45 +466,46 @@ public:
// Generate a quasi-random scale free network and simulate consensus
// for a single transaction
int N = 100; // Peers
int N = 100; // Peers
int numUNLs = 15; // UNL lists
int minUNLSize = N/4, maxUNLSize = N / 2;
int minUNLSize = N / 4, maxUNLSize = N / 2;
double transProb = 0.5;
std::mt19937_64 rng;
auto tg = TrustGraph::makeRandomRanked(N, numUNLs,
PowerLawDistribution{1,3},
std::uniform_int_distribution<>{minUNLSize, maxUNLSize},
rng);
auto tg = TrustGraph::makeRandomRanked(
N,
numUNLs,
PowerLawDistribution{1, 3},
std::uniform_int_distribution<>{minUNLSize, maxUNLSize},
rng);
Sim sim{tg, topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)})};
Sim sim{
tg,
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)})};
// Initial round to set prior state
sim.run(1);
std::uniform_real_distribution<> u{};
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
// 50-50 chance to have seen a transaction
if(u(rng) >= transProb)
if (u(rng) >= transProb)
p.openTxs.insert(Tx{0});
}
sim.run(1);
// See if the network forked
bc::flat_set<Ledger::ID> ledgers;
for (auto & p : sim.peers)
for (auto& p : sim.peers)
{
ledgers.insert(p.LCL());
ledgers.insert(p.prevLedgerID());
}
BEAST_EXPECT(ledgers.size() == 1);
}
void
@@ -507,5 +524,5 @@ public:
};
BEAST_DEFINE_TESTSUITE(Consensus, consensus, ripple);
} // test
} // ripple
} // test
} // ripple

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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