#pragma once #include #include #include #include #include #include #include namespace xrpl::test::csf { /** Trust graph Trust is a directed relationship from a node i to node j. If node i trusts node j, then node i has node j in its UNL. This class wraps a digraph representing the trust relationships for all peers in the simulation. */ template class TrustGraph { using Graph = Digraph; Graph graph_; public: /** Create an empty trust graph */ TrustGraph() = default; Graph const& graph() { return graph_; } /** Create trust Establish trust between Peer `from` and Peer `to`; as if `from` put `to` in its UNL. @param from The peer granting trust @param to The peer receiving trust */ void trust(Peer const& from, Peer const& to) { graph_.connect(from, to); } /** Remove trust Revoke trust from Peer `from` to Peer `to`; as if `from` removed `to` from its UNL. @param from The peer revoking trust @param to The peer being revoked */ void untrust(Peer const& from, Peer const& to) { graph_.disconnect(from, to); } //< Whether from trusts to [[nodiscard]] bool trusts(Peer const& from, Peer const& to) const { return graph_.connected(from, to); } /** Range over trusted peers @param a The node granting trust @return boost transformed range over nodes `a` trusts, i.e. the nodes in its UNL */ [[nodiscard]] auto trustedPeers(Peer const& a) const { return graph_.outVertices(a); } /** An example of nodes that fail the whitepaper no-forking condition */ struct ForkInfo { std::set unlA; std::set unlB; int overlap; double required; }; //< Return nodes that fail the white-paper no-forking condition [[nodiscard]] std::vector forkablePairs(double quorum) const { // Check the forking condition by looking at intersection // of UNL between all pairs of nodes. // TODO: Use the improved bound instead of the whitepaper bound. using UNL = std::set; std::set unique; for (Peer const peer : graph_.outVertices()) { unique.emplace(std::begin(trustedPeers(peer)), std::end(trustedPeers(peer))); } std::vector uniqueUNLs(unique.begin(), unique.end()); std::vector res; // Loop over all pairs of uniqueUNLs for (int i = 0; i < uniqueUNLs.size(); ++i) { for (int j = (i + 1); j < uniqueUNLs.size(); ++j) { auto const& unlA = uniqueUNLs[i]; auto const& unlB = uniqueUNLs[j]; double const rhs = 2.0 * (1. - quorum) * std::max(unlA.size(), unlB.size()); int const intersectionSize = std::count_if( unlA.begin(), unlA.end(), [&](Peer p) { return unlB.find(p) != unlB.end(); }); if (intersectionSize < rhs) { res.emplace_back(ForkInfo{unlA, unlB, intersectionSize, rhs}); } } } return res; } /** Check whether this trust graph satisfies the whitepaper no-forking condition */ [[nodiscard]] bool canFork(double quorum) const { return !forkablePairs(quorum).empty(); } }; } // namespace xrpl::test::csf