mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
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:
76
src/test/csf/impl/Sim.cpp
Normal file
76
src/test/csf/impl/Sim.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2017 Ripple Labs Inc
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <BeastConfig.h>
|
||||
#include <test/csf/Sim.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace csf {
|
||||
|
||||
void
|
||||
Sim::run(int ledgers)
|
||||
{
|
||||
for (auto& p : peers)
|
||||
{
|
||||
p.targetLedgers = p.completedLedgers + ledgers;
|
||||
p.start();
|
||||
}
|
||||
scheduler.step();
|
||||
}
|
||||
|
||||
void
|
||||
Sim::run(SimDuration const & dur)
|
||||
{
|
||||
for (auto& p : peers)
|
||||
{
|
||||
p.targetLedgers = std::numeric_limits<decltype(p.targetLedgers)>::max();
|
||||
p.start();
|
||||
}
|
||||
scheduler.step_for(dur);
|
||||
}
|
||||
|
||||
bool
|
||||
Sim::synchronized() const
|
||||
{
|
||||
if (peers.size() < 1)
|
||||
return true;
|
||||
Peer const& ref = peers.front();
|
||||
return std::all_of(peers.begin(), peers.end(), [&ref](Peer const& p) {
|
||||
return p.lastClosedLedger.id() ==
|
||||
ref.lastClosedLedger.id() &&
|
||||
p.fullyValidatedLedger.id() ==
|
||||
ref.fullyValidatedLedger.id();
|
||||
});
|
||||
}
|
||||
|
||||
std::size_t
|
||||
Sim::branches() const
|
||||
{
|
||||
if(peers.size() < 1)
|
||||
return 0;
|
||||
std::set<Ledger> ledgers;
|
||||
for(auto const & peer : peers)
|
||||
ledgers.insert(peer.fullyValidatedLedger);
|
||||
|
||||
return oracle.branches(ledgers);
|
||||
}
|
||||
|
||||
} // namespace csf
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -1,131 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2017 Ripple Labs Inc
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <boost/iterator/counting_iterator.hpp>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <test/csf/UNL.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace csf {
|
||||
|
||||
bool
|
||||
TrustGraph::canFork(double quorum) const
|
||||
{
|
||||
// Check the forking condition by looking at intersection
|
||||
// between all pairs of UNLs.
|
||||
|
||||
// First check if some nodes uses a UNL they are not members of, since
|
||||
// this creates an implicit UNL with that ndoe.
|
||||
|
||||
auto uniqueUNLs = UNLs_;
|
||||
|
||||
for (int i = 0; i < assignment_.size(); ++i)
|
||||
{
|
||||
auto const& myUNL = UNLs_[assignment_[i]];
|
||||
if (myUNL.find(i) == myUNL.end())
|
||||
{
|
||||
auto myUNLcopy = myUNL;
|
||||
myUNLcopy.insert(i);
|
||||
uniqueUNLs.push_back(std::move(myUNLcopy));
|
||||
}
|
||||
}
|
||||
|
||||
// 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 rhs =
|
||||
2.0 * (1. - quorum) * std::max(unlA.size(), unlB.size());
|
||||
|
||||
int intersectionSize =
|
||||
std::count_if(unlA.begin(), unlA.end(), [&](PeerID id) {
|
||||
return unlB.find(id) != unlB.end();
|
||||
});
|
||||
|
||||
if (intersectionSize < rhs)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TrustGraph
|
||||
TrustGraph::makeClique(int size, int overlap)
|
||||
{
|
||||
using bci = boost::counting_iterator<PeerID>;
|
||||
|
||||
// Split network into two cliques with the given 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;
|
||||
|
||||
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);
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
if (i < startB)
|
||||
assignment[i] = 0;
|
||||
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)};
|
||||
|
||||
return TrustGraph(std::vector<UNL>(1, all), std::vector<int>(size, 0));
|
||||
}
|
||||
|
||||
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]])
|
||||
{
|
||||
out << i << " -> " << j << ";\n";
|
||||
}
|
||||
}
|
||||
out << "}\n";
|
||||
}
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
140
src/test/csf/impl/ledgers.cpp
Normal file
140
src/test/csf/impl/ledgers.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2017 Ripple Labs Inc
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <BeastConfig.h>
|
||||
#include <test/csf/ledgers.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace csf {
|
||||
|
||||
Ledger::Instance const Ledger::genesis;
|
||||
|
||||
Json::Value
|
||||
Ledger::getJson() const
|
||||
{
|
||||
Json::Value res(Json::objectValue);
|
||||
res["id"] = static_cast<ID::value_type>(id());
|
||||
res["seq"] = static_cast<Seq::value_type>(seq());
|
||||
return res;
|
||||
}
|
||||
|
||||
LedgerOracle::LedgerOracle()
|
||||
{
|
||||
instances_.insert(InstanceEntry{Ledger::genesis, nextID()});
|
||||
}
|
||||
|
||||
Ledger::ID
|
||||
LedgerOracle::nextID() const
|
||||
{
|
||||
return Ledger::ID{static_cast<Ledger::ID::value_type>(instances_.size())};
|
||||
}
|
||||
|
||||
Ledger
|
||||
LedgerOracle::accept(
|
||||
Ledger const& parent,
|
||||
TxSetType const& txs,
|
||||
NetClock::duration closeTimeResolution,
|
||||
NetClock::time_point const& consensusCloseTime)
|
||||
{
|
||||
Ledger::Instance next(*parent.instance_);
|
||||
next.txs.insert(txs.begin(), txs.end());
|
||||
next.seq = parent.seq() + Ledger::Seq{1};
|
||||
next.closeTimeResolution = closeTimeResolution;
|
||||
next.closeTimeAgree = consensusCloseTime != NetClock::time_point{};
|
||||
if(next.closeTimeAgree)
|
||||
next.closeTime = effCloseTime(
|
||||
consensusCloseTime, closeTimeResolution, parent.closeTime());
|
||||
else
|
||||
next.closeTime = parent.closeTime() + 1s;
|
||||
|
||||
next.parentCloseTime = parent.closeTime();
|
||||
next.parentID = parent.id();
|
||||
auto it = instances_.left.find(next);
|
||||
if (it == instances_.left.end())
|
||||
{
|
||||
using Entry = InstanceMap::left_value_type;
|
||||
it = instances_.left.insert(Entry{next, nextID()}).first;
|
||||
}
|
||||
return Ledger(it->second, &(it->first));
|
||||
}
|
||||
|
||||
boost::optional<Ledger>
|
||||
LedgerOracle::lookup(Ledger::ID const & id) const
|
||||
{
|
||||
auto const it = instances_.right.find(id);
|
||||
if(it != instances_.right.end())
|
||||
{
|
||||
return Ledger(it->first, &(it->second));
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LedgerOracle::isAncestor(Ledger const & ancestor, Ledger const& descendant) const
|
||||
{
|
||||
// The ancestor must have an earlier sequence number than the descendent
|
||||
if(ancestor.seq() >= descendant.seq())
|
||||
return false;
|
||||
|
||||
boost::optional<Ledger> current{descendant};
|
||||
while(current && current->seq() > ancestor.seq())
|
||||
current = lookup(current->parentID());
|
||||
return current && (current->id() == ancestor.id());
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LedgerOracle::branches(std::set<Ledger> const & ledgers) const
|
||||
{
|
||||
// Tips always maintains the Ledgers with largest sequence number
|
||||
// along all known chains.
|
||||
std::vector<Ledger> tips;
|
||||
tips.reserve(ledgers.size());
|
||||
|
||||
for (Ledger const & ledger : ledgers)
|
||||
{
|
||||
// Three options,
|
||||
// 1. ledger is on a new branch
|
||||
// 2. ledger is on a branch that we have seen tip for
|
||||
// 3. ledger is the new tip for a branch
|
||||
bool found = false;
|
||||
for (auto idx = 0; idx < tips.size() && !found; ++idx)
|
||||
{
|
||||
bool const idxEarlier = tips[idx].seq() < ledger.seq();
|
||||
Ledger const & earlier = idxEarlier ? tips[idx] : ledger;
|
||||
Ledger const & later = idxEarlier ? ledger : tips[idx] ;
|
||||
if (isAncestor(earlier, later))
|
||||
{
|
||||
tips[idx] = later;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
tips.push_back(ledger);
|
||||
|
||||
}
|
||||
// The size of tips is the number of branches
|
||||
return tips.size();
|
||||
}
|
||||
} // namespace csf
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
Reference in New Issue
Block a user