#pragma once #include #include #include #include #include #include #include #include #include #include #include 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 peers; PeerGroup allPeers; public: std::mt19937_64 rng; Scheduler scheduler; BasicSink sink; beast::Journal j; LedgerOracle oracle; BasicNetwork net; TrustGraph 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 newPeers; newPeers.reserve(numPeers); for (std::size_t i = 0; i < numPeers; ++i) { peers.emplace_back( PeerID{static_cast(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