mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +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:
@@ -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
|
||||
|
||||
@@ -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