Redesign CSF framework (RIPD-1361):

- Separate `Scheduler` from `BasicNetwork`.
- Add an event/collector framework for monitoring invariants and calculating statistics.
- Allow distinct network and trust connections between Peers.
- Add a simple routing strategy to support broadcasting arbitrary messages.
- Add a common directed graph (`Digraph`) class for representing network and trust topologies.
- Add a `PeerGroup` class for simpler specification of the trust and network topologies.
- Add a `LedgerOracle` class to ensure distinct ledger histories and simplify branch checking.
- Add a `Submitter` to send transactions in at fixed or random intervals to fixed or random peers.

Co-authored-by: Joseph McGee
This commit is contained in:
Brad Chase
2017-06-14 11:59:06 -04:00
committed by seelabs
parent b9fc9f6334
commit 2c13d9eb57
51 changed files with 6642 additions and 2473 deletions

View File

@@ -20,84 +20,142 @@
#ifndef RIPPLE_TEST_CSF_SIM_H_INCLUDED
#define RIPPLE_TEST_CSF_SIM_H_INCLUDED
#include <test/csf/Digraph.h>
#include <test/csf/SimTime.h>
#include <test/csf/BasicNetwork.h>
#include <test/csf/UNL.h>
#include <test/csf/Scheduler.h>
#include <test/csf/Peer.h>
#include <test/csf/PeerGroup.h>
#include <test/csf/TrustGraph.h>
#include <test/csf/CollectorRef.h>
#include <iostream>
#include <deque>
#include <random>
namespace ripple {
namespace test {
namespace csf {
/** Sink that prepends simulation time to messages */
class BasicSink : public beast::Journal::Sink
{
Scheduler::clock_type const & clock_;
public:
BasicSink (Scheduler::clock_type const & clock)
: Sink (beast::severities::kDisabled, false)
, clock_{clock}
{
}
void
write (beast::severities::Severity level,
std::string const& text) override
{
if (level < threshold())
return;
std::cout << clock_.now().time_since_epoch().count() << " " << text
<< std::endl;
}
};
class Sim
{
// Use a deque to have stable pointers even when dynamically adding peers
// - Alternatively consider using unique_ptrs allocated from arena
std::deque<Peer> peers;
public:
/** Create a simulator for the given trust graph and network topology.
std::mt19937_64 rng;
Scheduler scheduler;
BasicSink sink;
beast::Journal j;
LedgerOracle oracle;
BasicNetwork<Peer*> net;
TrustGraph<Peer*> trustGraph;
CollectorRefs collectors;
Create a simulator for consensus over the given trust graph and connect
the network links between nodes based on the provided topology.
/** Create a simulation
Topology is is a functor with signature
boost::optional<std::chrono::duration> (NodeId i, NodeId j)
that returns the delay sending messages from node i to node j.
In general, this network graph is distinct from the trust graph, but
users can use adaptors to present a TrustGraph as a Topology by
specifying the delay between nodes.
@param g The trust graph between peers.
@param top The network topology between peers.
@param parms Consensus parameters to use in the simulation
Creates a new simulation. The simulation has no peers, no trust links
and no network connections.
*/
template <class Topology>
Sim(ConsensusParms parms, TrustGraph const& g, Topology const& top)
Sim() : sink{scheduler.clock()}, j{sink}, net{scheduler}
{
peers.reserve(g.numPeers());
for (int i = 0; i < g.numPeers(); ++i)
peers.emplace_back(i, net, g.unl(i), parms);
}
for (int i = 0; i < peers.size(); ++i)
/** Create a new group of peers.
Creates a new group of peers. The peers do not have any trust relations
or network connections by default. Those must be configured by the client.
@param numPeers The number of peers in the group
@return PeerGroup representing these new peers
@note This increases the number of peers in the simulation by numPeers.
*/
PeerGroup
createGroup(std::size_t numPeers)
{
std::vector<Peer*> newPeers;
newPeers.reserve(numPeers);
for (std::size_t i = 0; i < numPeers; ++i)
{
for (int j = 0; j < peers.size(); ++j)
{
if (i != j)
{
auto d = top(i, j);
if (d)
{
net.connect(&peers[i], &peers[j], *d);
}
}
}
peers.emplace_back(
PeerID{static_cast<std::uint32_t>(peers.size())},
scheduler,
oracle,
net,
trustGraph,
collectors,
j);
newPeers.emplace_back(&peers.back());
}
return PeerGroup{newPeers};
}
//! The number of peers in the simulation
std::size_t
size() const
{
return peers.size();
}
/** Run consensus protocol to generate the provided number of ledgers.
Has each peer run consensus until it creates `ledgers` more ledgers.
Has each peer run consensus until it closes `ledgers` more ledgers.
@param ledgers The number of additional ledgers to create
@param ledgers The number of additional ledgers to close
*/
void
run(int ledgers)
{
for (auto& p : peers)
{
if (p.completedLedgers == 0)
p.relay(Validation{p.id, p.prevLedgerID(), p.prevLedgerID()});
p.targetLedgers = p.completedLedgers + ledgers;
p.start();
}
net.step();
}
run(int ledgers);
/** Run consensus for the given duration */
void
run(SimDuration const& dur);
/** Check whether all peers in the network are synchronized.
Nodes in the network are synchronized if they share the same last
fully validated and last generated ledger.
*/
bool
synchronized() const;
/** Calculate the number of branches in the network.
A branch occurs if two peers have fullyValidatedLedgers that are not on
the same chain of ledgers.
*/
std::size_t
branches() const;
std::vector<Peer> peers;
BasicNetwork<Peer*> net;
};
} // csf
} // test
} // ripple
} // namespace csf
} // namespace test
} // namespace ripple
#endif