Files
rippled/src/test/csf/Sim.h
2026-03-24 15:42:12 +00:00

157 lines
3.9 KiB
C++

#pragma once
#include <test/csf/BasicNetwork.h>
#include <test/csf/CollectorRef.h>
#include <test/csf/Digraph.h>
#include <test/csf/Peer.h>
#include <test/csf/PeerGroup.h>
#include <test/csf/Scheduler.h>
#include <test/csf/SimTime.h>
#include <test/csf/TrustGraph.h>
#include <deque>
#include <iostream>
#include <random>
namespace xrpl {
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;
}
void
writeAlways(beast::severities::Severity level, std::string const& text) override
{
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;
PeerGroup allPeers;
public:
std::mt19937_64 rng;
Scheduler scheduler;
BasicSink sink;
beast::Journal j;
LedgerOracle oracle;
BasicNetwork<Peer*> net;
TrustGraph<Peer*> trustGraph;
CollectorRefs collectors;
/** Create a simulation
Creates a new simulation. The simulation has no peers, no trust links
and no network connections.
*/
Sim() : sink{scheduler.clock()}, j{sink}, net{scheduler}
{
}
/** 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)
{
peers.emplace_back(
PeerID{static_cast<std::uint32_t>(peers.size())},
scheduler,
oracle,
net,
trustGraph,
collectors,
j);
newPeers.emplace_back(&peers.back());
}
PeerGroup res{newPeers};
allPeers = allPeers + res;
return res;
}
//! 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 closes `ledgers` more ledgers.
@param ledgers The number of additional ledgers to close
*/
void
run(int ledgers);
/** Run consensus for the given duration */
void
run(SimDuration const& dur);
/** Check whether all peers in the group are synchronized.
Nodes in the group are synchronized if they share the same last
fully validated and last generated ledger.
*/
static bool
synchronized(PeerGroup const& g);
/** Check whether all peers in the network are synchronized
*/
bool
synchronized() const;
/** Calculate the number of branches in the group.
A branch occurs if two nodes in the group have fullyValidatedLedgers
that are not on the same chain of ledgers.
*/
std::size_t
branches(PeerGroup const& g) const;
/** Calculate the number of branches in the network
*/
std::size_t
branches() const;
};
} // namespace csf
} // namespace test
} // namespace xrpl