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

@@ -126,7 +126,7 @@ RCLConsensus::Adaptor::acquireLedger(LedgerHash const& ledger)
void
RCLConsensus::Adaptor::relay(RCLCxPeerPos const& peerPos)
RCLConsensus::Adaptor::share(RCLCxPeerPos const& peerPos)
{
protocol::TMProposeSet prop;
@@ -150,7 +150,7 @@ RCLConsensus::Adaptor::relay(RCLCxPeerPos const& peerPos)
}
void
RCLConsensus::Adaptor::relay(RCLCxTx const& tx)
RCLConsensus::Adaptor::share(RCLCxTx const& tx)
{
// If we didn't relay this transaction recently, relay it to all peers
if (app_.getHashRouter().shouldRelay(tx.id()))
@@ -204,7 +204,7 @@ RCLConsensus::Adaptor::propose(RCLCxPeerPos::Proposal const& proposal)
}
void
RCLConsensus::Adaptor::relay(RCLTxSet const& set)
RCLConsensus::Adaptor::share(RCLTxSet const& set)
{
inboundTransactions_.giveSet(set.id(), set.map_, false);
}
@@ -254,19 +254,7 @@ RCLConsensus::Adaptor::getPrevLedger(
app_.getValidations().currentTrustedDistribution(
ledgerID, parentID, ledgerMaster_.getValidLedgerIndex());
uint256 netLgr = ledgerID;
int netLgrCount = 0;
for (auto const & it : ledgerCounts)
{
// Switch to ledger supported by more peers
// Or stick with ours on a tie
if ((it.second > netLgrCount) ||
((it.second == netLgrCount) && (it.first == ledgerID)))
{
netLgr = it.first;
netLgrCount = it.second;
}
}
uint256 netLgr = getPreferredLedger(ledgerID, ledgerCounts);
if (netLgr != ledgerID)
{

View File

@@ -158,21 +158,21 @@ class RCLConsensus
boost::optional<RCLCxLedger>
acquireLedger(LedgerHash const& ledger);
/** Relay the given proposal to all peers
/** Share the given proposal with all peers
@param peerPos The peer position to relay.
@param peerPos The peer position to share.
*/
void
relay(RCLCxPeerPos const& peerPos);
share(RCLCxPeerPos const& peerPos);
/** Relay disputed transacction to peers.
/** Share disputed transaction to peers.
Only relay if the provided transaction hasn't been shared recently.
Only share if the provided transaction hasn't been shared recently.
@param tx The disputed transaction to relay.
@param tx The disputed transaction to share.
*/
void
relay(RCLCxTx const& tx);
share(RCLCxTx const& tx);
/** Acquire the transaction set associated with a proposal.
@@ -215,12 +215,12 @@ class RCLConsensus
void
propose(RCLCxPeerPos::Proposal const& proposal);
/** Relay the given tx set to peers.
/** Share the given tx set to peers.
@param set The TxSet to share.
*/
void
relay(RCLTxSet const& set);
share(RCLTxSet const& set);
/** Get the ID of the previous ledger/last closed ledger(LCL) on the
network

View File

@@ -37,6 +37,8 @@ class RCLCxLedger
public:
//! Unique identifier of a ledger
using ID = LedgerHash;
//! Sequence number of a ledger
using Seq = LedgerIndex;
/** Default constructor
@@ -55,28 +57,28 @@ public:
}
//! Sequence number of the ledger.
auto const&
Seq const&
seq() const
{
return ledger_->info().seq;
}
//! Unique identifier (hash) of this ledger.
auto const&
ID const&
id() const
{
return ledger_->info().hash;
}
//! Unique identifier (hash) of this ledger's parent.
auto const&
ID const&
parentID() const
{
return ledger_->info().parentHash;
}
//! Resolution used when calculating this ledger's close time.
auto
NetClock::duration
closeTimeResolution() const
{
return ledger_->info().closeTimeResolution;
@@ -90,14 +92,14 @@ public:
}
//! The close time of this ledger
auto
NetClock::time_point
closeTime() const
{
return ledger_->info().closeTime;
}
//! The close time of this ledger's parent.
auto
NetClock::time_point
parentCloseTime() const
{
return ledger_->info().parentCloseTime;

View File

@@ -41,9 +41,18 @@ namespace ripple {
allowed arithmetic operations.
*/
template <class Int, class Tag>
class tagged_integer : boost::operators<tagged_integer<Int, Tag>>
, boost::shiftable<tagged_integer<Int, Tag>>
class tagged_integer
: boost::totally_ordered<
tagged_integer<Int, Tag>,
boost::integer_arithmetic<
tagged_integer<Int, Tag>,
boost::bitwise<
tagged_integer<Int, Tag>,
boost::unit_steppable<
tagged_integer<Int, Tag>,
boost::shiftable<tagged_integer<Int, Tag>>>>>>
{
private:
Int m_value;
@@ -63,6 +72,9 @@ public:
tagged_integer(OtherInt value) noexcept
: m_value(value)
{
static_assert(
sizeof(tagged_integer) == sizeof(Int),
"tagged_integer is adding padding");
}
bool

View File

@@ -176,10 +176,11 @@ checkConsensus(
struct Ledger
{
using ID = ...;
using Seq = ...;
// Unique identifier of ledgerr
ID const id() const;
auto seq() const;
Seq seq() const;
auto closeTimeResolution() const;
auto closeAgree() const;
auto closeTime() const;
@@ -257,14 +258,14 @@ checkConsensus(
// Propose the position to peers.
void propose(ConsensusProposal<...> const & pos);
// Relay a received peer proposal on to other peer's.
void relay(PeerPosition_t const & prop);
// Share a received peer proposal with other peer's.
void share(PeerPosition_t const & prop);
// Relay a disputed transaction to peers
void relay(Txn const & tx);
// Share a disputed transaction with peers
void share(Txn const & tx);
// Share given transaction set with peers
void relay(TxSet const &s);
void share(TxSet const &s);
// Consensus timing parameters and constants
ConsensusParms const &
@@ -642,7 +643,7 @@ Consensus<Adaptor>::startRoundInternal(
closeResolution_ = getNextLedgerTimeResolution(
previousLedger_.closeTimeResolution(),
previousLedger_.closeAgree(),
previousLedger_.seq() + 1);
previousLedger_.seq() + typename Ledger_t::Seq{1});
playbackProposals();
if (currPeerPositions_.size() > (prevProposers_ / 2))
@@ -878,7 +879,7 @@ Consensus<Adaptor>::getJson(bool full) const
if (mode_.get() != ConsensusMode::wrongLedger)
{
ret["synched"] = true;
ret["ledger_seq"] = previousLedger_.seq() + 1;
ret["ledger_seq"] = static_cast<std::uint32_t>(previousLedger_.seq())+ 1;
ret["close_granularity"] = static_cast<Int>(closeResolution_.count());
}
else
@@ -1038,7 +1039,7 @@ Consensus<Adaptor>::playbackProposals()
if (pos.proposal().prevLedger() == prevLedgerID_)
{
if (peerProposalInternal(now_, pos))
adaptor_.relay(pos);
adaptor_.share(pos);
}
}
}
@@ -1158,7 +1159,7 @@ Consensus<Adaptor>::closeLedger()
// Share the newly created transaction set if we haven't already
// received it from a peer
if (acquired_.emplace(result_->set.id(), result_->set).second)
adaptor_.relay(result_->set);
adaptor_.share(result_->set);
if (mode_.get() == ConsensusMode::proposing)
adaptor_.propose(result_->position);
@@ -1264,7 +1265,7 @@ Consensus<Adaptor>::updateOurPositions()
}
if (mutableSet)
ourNewSet.emplace(*mutableSet);
ourNewSet.emplace(std::move(*mutableSet));
}
NetClock::time_point consensusCloseTime = {};
@@ -1310,7 +1311,8 @@ Consensus<Adaptor>::updateOurPositions()
for (auto const& it : closeTimeVotes)
{
JLOG(j_.debug())
<< "CCTime: seq " << previousLedger_.seq() + 1 << ": "
<< "CCTime: seq "
<< static_cast<std::uint32_t>(previousLedger_.seq()) + 1 << ": "
<< it.first.time_since_epoch().count() << " has " << it.second
<< ", " << threshVote << " required";
@@ -1361,7 +1363,7 @@ Consensus<Adaptor>::updateOurPositions()
if (acquired_.emplace(newID, result_->set).second)
{
if (!result_->position.isBowOut())
adaptor_.relay(result_->set);
adaptor_.share(result_->set);
for (auto const& it : currPeerPositions_)
{
@@ -1493,7 +1495,8 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o)
JLOG(j_.debug()) << "Transaction " << txID << " is disputed";
typename Result::Dispute_t dtx{tx, result_->set.exists(txID), j_};
typename Result::Dispute_t dtx{tx, result_->set.exists(txID),
std::max(prevProposers_, currPeerPositions_.size()), j_};
// Update all of the available peer's votes on the disputed transaction
for (auto const& pit : currPeerPositions_)
@@ -1503,7 +1506,7 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o)
if (cit != acquired_.end())
dtx.setVote(pit.first, cit->second.exists(txID));
}
adaptor_.relay(dtx.tx());
adaptor_.share(dtx.tx());
result_->disputes.emplace(txID, std::move(dtx));
}

View File

@@ -20,6 +20,7 @@
#ifndef RIPPLE_APP_CONSENSUS_IMPL_DISPUTEDTX_H_INCLUDED
#define RIPPLE_APP_CONSENSUS_IMPL_DISPUTEDTX_H_INCLUDED
#include <boost/container/flat_map.hpp>
#include <ripple/basics/Log.h>
#include <ripple/basics/base_uint.h>
#include <ripple/beast/utility/Journal.h>
@@ -48,17 +49,19 @@ template <class Tx_t, class NodeID_t>
class DisputedTx
{
using TxID_t = typename Tx_t::ID;
using Map_t = boost::container::flat_map<NodeID_t, bool>;
public:
/** Constructor
@param tx The transaction under dispute
@param ourVote Our vote on whether tx should be included
@param numPeers Anticipated number of peer votes
@param j Journal for debugging
*/
DisputedTx(Tx_t const& tx, bool ourVote, beast::Journal j)
DisputedTx(Tx_t const& tx, bool ourVote, std::size_t numPeers, beast::Journal j)
: yays_(0), nays_(0), ourVote_(ourVote), tx_(tx), j_(j)
{
votes_.reserve(numPeers);
}
//! The unique id/hash of the disputed transaction.
@@ -127,9 +130,8 @@ private:
int nays_; //< Number of no votes
bool ourVote_; //< Our vote (true is yes)
Tx_t tx_; //< Transaction under dispute
hash_map<NodeID_t, bool> votes_; //< Votes of our peers
beast::Journal j_; //< Debug journal
Map_t votes_; //< Map from NodeID to vote
beast::Journal j_;
};
// Track a peer's yes/no vote on a particular disputed tx_

View File

@@ -46,7 +46,6 @@ auto constexpr increaseLedgerTimeResolutionEvery = 8;
//! How often we decrease the close time resolution (in numbers of ledgers)
auto constexpr decreaseLedgerTimeResolutionEvery = 1;
/** Calculates the close time resolution for the specified ledger.
The Ripple protocol uses binning to represent time intervals using only one
@@ -62,15 +61,22 @@ auto constexpr decreaseLedgerTimeResolutionEvery = 1;
@pre previousResolution must be a valid bin
from @ref ledgerPossibleTimeResolutions
@tparam Rep Type representing number of ticks in std::chrono::duration
@tparam Period An std::ratio representing tick period in
std::chrono::duration
@tparam Seq Unsigned integer-like type corresponding to the ledger sequence
number. It should be comparable to 0 and support modular
division. Built-in and tagged_integers are supported.
*/
template <class duration>
duration
template <class Rep, class Period, class Seq>
std::chrono::duration<Rep, Period>
getNextLedgerTimeResolution(
duration previousResolution,
std::chrono::duration<Rep, Period> previousResolution,
bool previousAgree,
std::uint32_t ledgerSeq)
Seq ledgerSeq)
{
assert(ledgerSeq);
assert(ledgerSeq != Seq{0});
using namespace std::chrono;
// Find the current resolution:
@@ -86,7 +92,8 @@ getNextLedgerTimeResolution(
// If we did not previously agree, we try to decrease the resolution to
// improve the chance that we will agree now.
if (!previousAgree && ledgerSeq % decreaseLedgerTimeResolutionEvery == 0)
if (!previousAgree &&
(ledgerSeq % Seq{decreaseLedgerTimeResolutionEvery} == Seq{0}))
{
if (++iter != std::end(ledgerPossibleTimeResolutions))
return *iter;
@@ -94,7 +101,8 @@ getNextLedgerTimeResolution(
// If we previously agreed, we try to increase the resolution to determine
// if we can continue to agree.
if (previousAgree && ledgerSeq % increaseLedgerTimeResolutionEvery == 0)
if (previousAgree &&
(ledgerSeq % Seq{increaseLedgerTimeResolutionEvery} == Seq{0}))
{
if (iter-- != std::begin(ledgerPossibleTimeResolutions))
return *iter;
@@ -110,12 +118,13 @@ getNextLedgerTimeResolution(
@return @b closeTime rounded to the nearest multiple of @b closeResolution.
Rounds up if @b closeTime is midway between multiples of @b closeResolution.
*/
template <class time_point>
time_point
template <class Clock, class Duration, class Rep, class Period>
std::chrono::time_point<Clock, Duration>
roundCloseTime(
time_point closeTime,
typename time_point::duration closeResolution)
std::chrono::time_point<Clock, Duration> closeTime,
std::chrono::duration<Rep, Period> closeResolution)
{
using time_point = decltype(closeTime);
if (closeTime == time_point{})
return closeTime;
@@ -132,14 +141,15 @@ roundCloseTime(
@param resolution The current close time resolution
@param priorCloseTime The close time of the prior ledger
*/
template <class time_point>
time_point
template <class Clock, class Duration, class Rep, class Period>
std::chrono::time_point<Clock, Duration>
effCloseTime(
time_point closeTime,
typename time_point::duration const resolution,
time_point priorCloseTime)
std::chrono::time_point<Clock, Duration> closeTime,
std::chrono::duration<Rep, Period> resolution,
std::chrono::time_point<Clock, Duration> priorCloseTime)
{
using namespace std::chrono_literals;
using time_point = decltype(closeTime);
if (closeTime == time_point{})
return closeTime;

View File

@@ -26,7 +26,6 @@
#include <ripple/beast/container/aged_container_utility.h>
#include <ripple/beast/container/aged_unordered_map.h>
#include <ripple/beast/utility/Journal.h>
#include <ripple/beast/utility/Zero.h>
#include <boost/optional.hpp>
#include <mutex>
#include <utility>
@@ -74,6 +73,7 @@ struct ValidationParms
std::chrono::seconds validationSET_EXPIRES = std::chrono::minutes{10};
};
/** Whether a validation is still current
Determines whether a validation can still be considered the current
@@ -103,6 +103,37 @@ isCurrent(
(seenTime < (now + p.validationCURRENT_LOCAL)));
}
/** Determine the preferred ledger based on its support
@param current The current ledger the node follows
@param dist Ledger IDs and corresponding counts of support
@return The ID of the ledger with most support, preferring to stick with
current ledger in the case of equal support
*/
template <class LedgerID>
inline LedgerID
getPreferredLedger(
LedgerID const& current,
hash_map<LedgerID, std::uint32_t> const& dist)
{
LedgerID netLgr = current;
int netLgrCount = 0;
for (auto const& it : dist)
{
// Switch to ledger supported by more peers
// On a tie, prefer the current ledger, or the one with higher ID
if ((it.second > netLgrCount) ||
((it.second == netLgrCount) &&
((it.first == current) ||
(it.first > netLgr && netLgr != current))))
{
netLgr = it.first;
netLgrCount = it.second;
}
}
return netLgr;
}
/** Maintains current and recent ledger validations.
Manages storage and queries related to validations received on the network.
@@ -192,6 +223,7 @@ class Validations
decay_result_t<decltype (&Validation::ledgerID)(Validation)>;
using NodeKey = decay_result_t<decltype (&Validation::key)(Validation)>;
using NodeID = decay_result_t<decltype (&Validation::nodeID)(Validation)>;
using SeqType = decay_result_t<decltype (&Validation::seq)(Validation)>;
using ScopedLock = std::lock_guard<MutexType>;
@@ -397,11 +429,12 @@ public:
Validation& oldVal = ins.first->second.val;
LedgerID const previousLedgerID = ins.first->second.prevLedgerID;
std::uint32_t const oldSeq{oldVal.seq()};
std::uint32_t const newSeq{val.seq()};
SeqType const oldSeq{oldVal.seq()};
SeqType const newSeq{val.seq()};
// Sequence of 0 indicates a missing sequence number
if (oldSeq && newSeq && oldSeq == newSeq)
if ((oldSeq != SeqType{0}) && (newSeq != SeqType{0}) &&
oldSeq == newSeq)
{
result = AddOutcome::sameSeq;
@@ -491,7 +524,9 @@ public:
ledgers one away from the current ledger to count as the current.
@param currentLedger The identifier of the ledger we believe is current
(0 if unknown)
@param priorLedger The identifier of our previous current ledger
(0 if unknown)
@param cutoffBefore Ignore ledgers with sequence number before this
@return Map representing the distribution of ledgerID by count
@@ -500,10 +535,10 @@ public:
currentTrustedDistribution(
LedgerID const& currentLedger,
LedgerID const& priorLedger,
std::uint32_t cutoffBefore)
SeqType cutoffBefore)
{
bool const valCurrentLedger = currentLedger != beast::zero;
bool const valPriorLedger = priorLedger != beast::zero;
bool const valCurrentLedger = currentLedger != LedgerID{0};
bool const valPriorLedger = priorLedger != LedgerID{0};
hash_map<LedgerID, std::uint32_t> ret;
@@ -524,8 +559,8 @@ public:
if (!v.trusted())
return;
std::uint32_t const seq = v.seq();
if ((seq == 0) || (seq >= cutoffBefore))
SeqType const seq = v.seq();
if ((seq == SeqType{0}) || (seq >= cutoffBefore))
{
// contains a live record
bool countPreferred =