mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Refactor consensus for simulation (RIPD-1011):
This is a substantial refactor of the consensus code and also introduces a basic consensus simulation and testing framework. The new generic/templated version is in src/ripple/consensus and documents the current type requirements. The version adapted for the RCL is in src/ripple/app/consensus. The testing framework is in src/test/csf. Minor behavioral changes/fixes include: * Adjust close time offset even when not validating. * Remove spurious proposing_ = false call at end of handleLCL. * Remove unused functionality provided by checkLastValidation. * Separate open and converge time * Don't send a bow out if we're not proposing * Prevent consensus stopping if NetworkOPs switches to disconnect mode while consensus accepts a ledger * Prevent a corner case in which Consensus::gotTxSet or Consensus::peerProposal has the potential to update internal state while an dispatched accept job is running. * Distinguish external and internal calls to startNewRound. Only external calls can reset the proposing_ state of consensus
This commit is contained in:
909
src/ripple/app/consensus/RCLConsensus.cpp
Normal file
909
src/ripple/app/consensus/RCLConsensus.cpp
Normal file
@@ -0,0 +1,909 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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 <ripple/beast/core/LexicalCast.h>
|
||||
#include <ripple/app/consensus/RCLConsensus.h>
|
||||
#include <ripple/app/ledger/InboundTransactions.h>
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/InboundLedgers.h>
|
||||
#include <ripple/overlay/Overlay.h>
|
||||
#include <ripple/app/ledger/OpenLedger.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/overlay/predicates.h>
|
||||
#include <ripple/app/misc/AmendmentTable.h>
|
||||
#include <ripple/app/misc/HashRouter.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/basics/make_lock.h>
|
||||
#include <ripple/app/ledger/LocalTxs.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/app/tx/apply.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/app/misc/LoadFeeTrack.h>
|
||||
#include <ripple/app/misc/ValidatorList.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
|
||||
RCLConsensus::RCLConsensus(
|
||||
Application& app,
|
||||
std::unique_ptr<FeeVote> && feeVote,
|
||||
LedgerMaster& ledgerMaster,
|
||||
LocalTxs& localTxs,
|
||||
InboundTransactions& inboundTransactions,
|
||||
typename Base::clock_type const & clock,
|
||||
beast::Journal journal)
|
||||
: Base(clock, journal)
|
||||
, app_ (app)
|
||||
, feeVote_ (std::move(feeVote))
|
||||
, ledgerMaster_ (ledgerMaster)
|
||||
, localTxs_(localTxs)
|
||||
, inboundTransactions_{ inboundTransactions }
|
||||
, j_ (journal)
|
||||
, nodeID_{ calcNodeID(app.nodeIdentity().first) }
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::onStartRound(RCLCxLedger const & ledger)
|
||||
{
|
||||
inboundTransactions_.newRound(ledger.seq());
|
||||
}
|
||||
|
||||
// First bool is whether or not we can propose
|
||||
// Second bool is whether or not we can validate
|
||||
std::pair <bool, bool>
|
||||
RCLConsensus::getMode ()
|
||||
{
|
||||
bool propose = false;
|
||||
bool validate = false;
|
||||
|
||||
if (! app_.getOPs().isNeedNetworkLedger() && (valPublic_.size() != 0))
|
||||
{
|
||||
// We have a key, and we have some idea what the ledger is
|
||||
validate = true;
|
||||
|
||||
// propose only if we're in sync with the network
|
||||
propose = app_.getOPs().getOperatingMode() == NetworkOPs::omFULL;
|
||||
}
|
||||
return { propose, validate };
|
||||
}
|
||||
|
||||
boost::optional<RCLCxLedger>
|
||||
RCLConsensus::acquireLedger(LedgerHash const & ledger)
|
||||
{
|
||||
|
||||
// we need to switch the ledger we're working from
|
||||
auto buildLCL = ledgerMaster_.getLedgerByHash(ledger);
|
||||
if (! buildLCL)
|
||||
{
|
||||
if (acquiringLedger_ != ledger)
|
||||
{
|
||||
// need to start acquiring the correct consensus LCL
|
||||
JLOG (j_.warn()) <<
|
||||
"Need consensus ledger " << ledger;
|
||||
|
||||
// Tell the ledger acquire system that we need the consensus ledger
|
||||
acquiringLedger_ = ledger;
|
||||
|
||||
auto app = &app_;
|
||||
auto hash = acquiringLedger_;
|
||||
app_.getJobQueue().addJob (
|
||||
jtADVANCE, "getConsensusLedger",
|
||||
[app, hash] (Job&) {
|
||||
app->getInboundLedgers().acquire(
|
||||
hash, 0, InboundLedger::fcCONSENSUS);
|
||||
});
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
assert (!buildLCL->open() && buildLCL->isImmutable ());
|
||||
assert (buildLCL->info().hash == ledger);
|
||||
|
||||
return RCLCxLedger(buildLCL);
|
||||
}
|
||||
|
||||
|
||||
std::vector<RCLCxPeerPos>
|
||||
RCLConsensus::proposals (LedgerHash const& prevLedger)
|
||||
{
|
||||
std::vector <RCLCxPeerPos> ret;
|
||||
{
|
||||
std::lock_guard <std::mutex> _(peerPositionsLock_);
|
||||
|
||||
for (auto const& it : peerPositions_)
|
||||
for (auto const& pos : it.second)
|
||||
if (pos->proposal().prevLedger() == prevLedger)
|
||||
ret.emplace_back (*pos);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::storeProposal (
|
||||
RCLCxPeerPos::ref peerPos,
|
||||
NodeID const& nodeID)
|
||||
{
|
||||
std::lock_guard <std::mutex> _(peerPositionsLock_);
|
||||
|
||||
auto& props = peerPositions_[nodeID];
|
||||
|
||||
if (props.size () >= 10)
|
||||
props.pop_front ();
|
||||
|
||||
props.push_back (peerPos);
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::relay(RCLCxPeerPos const & peerPos)
|
||||
{
|
||||
protocol::TMProposeSet prop;
|
||||
|
||||
auto const & proposal = peerPos.proposal();
|
||||
|
||||
prop.set_proposeseq (
|
||||
proposal.proposeSeq ());
|
||||
prop.set_closetime (
|
||||
proposal.closeTime ().time_since_epoch().count());
|
||||
|
||||
prop.set_currenttxhash (
|
||||
proposal.position().begin(), proposal.position().size());
|
||||
prop.set_previousledger (
|
||||
proposal.prevLedger().begin(), proposal.position().size());
|
||||
|
||||
auto const pk = peerPos.getPublicKey().slice();
|
||||
prop.set_nodepubkey (pk.data(), pk.size());
|
||||
|
||||
auto const sig = peerPos.getSignature();
|
||||
prop.set_signature (sig.data(), sig.size());
|
||||
|
||||
app_.overlay().relay (prop, peerPos.getSuppressionID ());
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::relay(DisputedTx <RCLCxTx, NodeID> const & dispute)
|
||||
{
|
||||
// If we didn't relay this transaction recently, relay it to all peers
|
||||
auto const & tx = dispute.tx();
|
||||
if (app_.getHashRouter ().shouldRelay (tx.id()))
|
||||
{
|
||||
auto const slice = tx.tx_.slice();
|
||||
|
||||
protocol::TMTransaction msg;
|
||||
msg.set_rawtransaction (slice.data(), slice.size());
|
||||
msg.set_status (protocol::tsNEW);
|
||||
msg.set_receivetimestamp (
|
||||
app_.timeKeeper().now().time_since_epoch().count());
|
||||
app_.overlay ().foreach (send_always (
|
||||
std::make_shared<Message> (
|
||||
msg, protocol::mtTRANSACTION)));
|
||||
}
|
||||
}
|
||||
void
|
||||
RCLConsensus::propose (RCLCxPeerPos::Proposal const& proposal)
|
||||
{
|
||||
JLOG (j_.trace()) << "We propose: " <<
|
||||
(proposal.isBowOut () ? std::string ("bowOut") :
|
||||
to_string (proposal.position ()));
|
||||
|
||||
protocol::TMProposeSet prop;
|
||||
|
||||
prop.set_currenttxhash (proposal.position().begin(),
|
||||
proposal.position().size());
|
||||
prop.set_previousledger (proposal.prevLedger().begin(),
|
||||
proposal.position().size());
|
||||
prop.set_proposeseq (proposal.proposeSeq());
|
||||
prop.set_closetime (
|
||||
proposal.closeTime().time_since_epoch().count());
|
||||
|
||||
prop.set_nodepubkey (valPublic_.data(), valPublic_.size());
|
||||
|
||||
auto signingHash = sha512Half(
|
||||
HashPrefix::proposal,
|
||||
std::uint32_t(proposal.proposeSeq()),
|
||||
proposal.closeTime().time_since_epoch().count(),
|
||||
proposal.prevLedger(), proposal.position());
|
||||
|
||||
auto sig = signDigest (
|
||||
valPublic_, valSecret_, signingHash);
|
||||
|
||||
prop.set_signature (sig.data(), sig.size());
|
||||
|
||||
app_.overlay().send(prop);
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::share (RCLTxSet const& set)
|
||||
{
|
||||
inboundTransactions_.giveSet (set.id(),
|
||||
set.map_, false);
|
||||
}
|
||||
|
||||
boost::optional<RCLTxSet>
|
||||
RCLConsensus::acquireTxSet(RCLTxSet::ID const & setId)
|
||||
{
|
||||
if (auto set = inboundTransactions_.getSet(setId, true))
|
||||
{
|
||||
return RCLTxSet{std::move(set)};
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
RCLConsensus::hasOpenTransactions() const
|
||||
{
|
||||
return ! app_.openLedger().empty();
|
||||
}
|
||||
|
||||
std::size_t
|
||||
RCLConsensus::proposersValidated(LedgerHash const & h) const
|
||||
{
|
||||
return app_.getValidations().getTrustedValidationCount(h);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
RCLConsensus::proposersFinished(LedgerHash const & h) const
|
||||
{
|
||||
return app_.getValidations().getNodesAfter(h);
|
||||
}
|
||||
|
||||
uint256
|
||||
RCLConsensus::getLCL (
|
||||
uint256 const& currentLedger,
|
||||
uint256 const& priorLedger,
|
||||
bool believedCorrect)
|
||||
{
|
||||
// Get validators that are on our ledger, or "close" to being on
|
||||
// our ledger.
|
||||
auto vals =
|
||||
app_.getValidations().getCurrentValidations(
|
||||
currentLedger, priorLedger,
|
||||
ledgerMaster_.getValidLedgerIndex());
|
||||
|
||||
uint256 netLgr = currentLedger;
|
||||
int netLgrCount = 0;
|
||||
for (auto& it : vals)
|
||||
{
|
||||
// Switch to ledger supported by more peers
|
||||
// Or stick with ours on a tie
|
||||
if ((it.second.first > netLgrCount) ||
|
||||
((it.second.first == netLgrCount) && (it.first == currentLedger)))
|
||||
{
|
||||
netLgr = it.first;
|
||||
netLgrCount = it.second.first;
|
||||
}
|
||||
}
|
||||
|
||||
if(netLgr != currentLedger)
|
||||
{
|
||||
if (believedCorrect)
|
||||
app_.getOPs().consensusViewChange();
|
||||
if (auto stream = j_.debug())
|
||||
{
|
||||
for (auto& it : vals)
|
||||
stream << "V: " << it.first << ", " << it.second.first;
|
||||
stream << getJson (true);
|
||||
}
|
||||
}
|
||||
|
||||
return netLgr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RCLConsensus::onClose(RCLCxLedger const & ledger, bool haveCorrectLCL)
|
||||
{
|
||||
notify(protocol::neCLOSING_LEDGER, ledger, haveCorrectLCL);
|
||||
}
|
||||
|
||||
std::pair <RCLTxSet, typename RCLCxPeerPos::Proposal>
|
||||
RCLConsensus::makeInitialPosition (RCLCxLedger const & prevLedgerT,
|
||||
bool proposing,
|
||||
bool correctLCL,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now)
|
||||
{
|
||||
auto const &prevLedger = prevLedgerT.ledger_;
|
||||
ledgerMaster_.applyHeldTransactions ();
|
||||
// Tell the ledger master not to acquire the ledger we're probably building
|
||||
ledgerMaster_.setBuildingLedger (prevLedger->info().seq + 1);
|
||||
|
||||
auto initialLedger = app_.openLedger().current();
|
||||
|
||||
auto initialSet = std::make_shared <SHAMap> (
|
||||
SHAMapType::TRANSACTION, app_.family(), SHAMap::version{1});
|
||||
initialSet->setUnbacked ();
|
||||
|
||||
// Build SHAMap containing all transactions in our open ledger
|
||||
for (auto const& tx : initialLedger->txs)
|
||||
{
|
||||
Serializer s (2048);
|
||||
tx.first->add(s);
|
||||
initialSet->addItem (
|
||||
SHAMapItem (tx.first->getTransactionID(), std::move (s)), true, false);
|
||||
}
|
||||
|
||||
// Add pseudo-transactions to the set
|
||||
if ((app_.config().standalone() || (proposing && correctLCL))
|
||||
&& ((prevLedger->info().seq % 256) == 0))
|
||||
{
|
||||
// previous ledger was flag ledger, add pseudo-transactions
|
||||
auto const validations =
|
||||
app_.getValidations().getValidations (
|
||||
prevLedger->info().parentHash);
|
||||
|
||||
std::size_t const count = std::count_if (
|
||||
validations.begin(), validations.end(),
|
||||
[](auto const& v)
|
||||
{
|
||||
return v.second->isTrusted();
|
||||
});
|
||||
|
||||
if (count >= app_.validators ().quorum ())
|
||||
{
|
||||
feeVote_->doVoting (
|
||||
prevLedger,
|
||||
validations,
|
||||
initialSet);
|
||||
app_.getAmendmentTable ().doVoting (
|
||||
prevLedger,
|
||||
validations,
|
||||
initialSet);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need an immutable snapshot
|
||||
initialSet = initialSet->snapShot(false);
|
||||
auto setHash = initialSet->getHash().as_uint256();
|
||||
|
||||
return std::make_pair<RCLTxSet, RCLCxPeerPos::Proposal> (
|
||||
std::move (initialSet),
|
||||
RCLCxPeerPos::Proposal {
|
||||
initialLedger->info().parentHash,
|
||||
RCLCxPeerPos::Proposal::seqJoin,
|
||||
setHash,
|
||||
closeTime,
|
||||
now,
|
||||
nodeID_ });
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::dispatchAccept(RCLTxSet const & txSet)
|
||||
{
|
||||
app_.getJobQueue().addJob(jtACCEPT, "acceptLedger",
|
||||
[that = this->shared_from_this(),
|
||||
consensusSet = txSet]
|
||||
(auto &)
|
||||
{
|
||||
that->accept(consensusSet);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
RCLConsensus::accept(
|
||||
RCLTxSet const& set,
|
||||
NetClock::time_point consensusCloseTime,
|
||||
bool proposing_,
|
||||
bool validating_,
|
||||
bool haveCorrectLCL_,
|
||||
bool consensusFail_,
|
||||
LedgerHash const &prevLedgerHash_,
|
||||
RCLCxLedger const & previousLedger_,
|
||||
NetClock::duration closeResolution_,
|
||||
NetClock::time_point const & now_,
|
||||
std::chrono::milliseconds const & roundTime_,
|
||||
hash_map<RCLCxTx::ID, DisputedTx <RCLCxTx, NodeID>> const & disputes_,
|
||||
std::map <NetClock::time_point, int> closeTimes_,
|
||||
NetClock::time_point const & closeTime_
|
||||
)
|
||||
{
|
||||
bool closeTimeCorrect;
|
||||
|
||||
if (consensusCloseTime == NetClock::time_point{})
|
||||
{
|
||||
// We agreed to disagree on the close time
|
||||
consensusCloseTime = previousLedger_.closeTime() + 1s;
|
||||
closeTimeCorrect = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We agreed on a close time
|
||||
consensusCloseTime = effectiveCloseTime(consensusCloseTime,
|
||||
closeResolution_, previousLedger_.closeTime());
|
||||
closeTimeCorrect = true;
|
||||
}
|
||||
|
||||
JLOG (j_.debug())
|
||||
<< "Report: Prop=" << (proposing_ ? "yes" : "no")
|
||||
<< " val=" << (validating_ ? "yes" : "no")
|
||||
<< " corLCL=" << (haveCorrectLCL_ ? "yes" : "no")
|
||||
<< " fail=" << (consensusFail_ ? "yes" : "no");
|
||||
JLOG (j_.debug())
|
||||
<< "Report: Prev = " << prevLedgerHash_
|
||||
<< ":" << previousLedger_.seq();
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Put transactions into a deterministic, but unpredictable, order
|
||||
CanonicalTXSet retriableTxs{ set.id() };
|
||||
|
||||
auto sharedLCL = buildLCL(previousLedger_, set, consensusCloseTime,
|
||||
closeTimeCorrect, closeResolution_, now_, roundTime_, retriableTxs);
|
||||
|
||||
|
||||
auto const newLCLHash = sharedLCL.id();
|
||||
JLOG (j_.debug())
|
||||
<< "Report: NewL = " << newLCLHash
|
||||
<< ":" << sharedLCL.seq();
|
||||
|
||||
// Tell directly connected peers that we have a new LCL
|
||||
notify (protocol::neACCEPTED_LEDGER, sharedLCL, haveCorrectLCL_);
|
||||
|
||||
if (validating_)
|
||||
validating_ = ledgerMaster_.isCompatible(*sharedLCL.ledger_,
|
||||
app_.journal("LedgerConsensus").warn(), "Not validating");
|
||||
|
||||
if (validating_ && ! consensusFail_)
|
||||
{
|
||||
validate(sharedLCL, now_, proposing_);
|
||||
JLOG (j_.info())
|
||||
<< "CNF Val " << newLCLHash;
|
||||
}
|
||||
else
|
||||
JLOG (j_.info())
|
||||
<< "CNF buildLCL " << newLCLHash;
|
||||
|
||||
// See if we can accept a ledger as fully-validated
|
||||
ledgerMaster_.consensusBuilt (sharedLCL.ledger_, getJson(true));
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
{
|
||||
// Apply disputed transactions that didn't get in
|
||||
//
|
||||
// The first crack of transactions to get into the new
|
||||
// open ledger goes to transactions proposed by a validator
|
||||
// we trust but not included in the consensus set.
|
||||
//
|
||||
// These are done first because they are the most likely
|
||||
// to receive agreement during consensus. They are also
|
||||
// ordered logically "sooner" than transactions not mentioned
|
||||
// in the previous consensus round.
|
||||
//
|
||||
bool anyDisputes = false;
|
||||
for (auto& it : disputes_)
|
||||
{
|
||||
if (!it.second.getOurVote ())
|
||||
{
|
||||
// we voted NO
|
||||
try
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Test applying disputed transaction that did"
|
||||
<< " not get in";
|
||||
|
||||
SerialIter sit (it.second.tx().tx_.slice());
|
||||
auto txn = std::make_shared<STTx const>(sit);
|
||||
retriableTxs.insert (txn);
|
||||
|
||||
anyDisputes = true;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Failed to apply transaction we voted NO on";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build new open ledger
|
||||
auto lock = make_lock(
|
||||
app_.getMasterMutex(), std::defer_lock);
|
||||
auto sl = make_lock(
|
||||
ledgerMaster_.peekMutex (), std::defer_lock);
|
||||
std::lock(lock, sl);
|
||||
|
||||
auto const lastVal = ledgerMaster_.getValidatedLedger();
|
||||
boost::optional<Rules> rules;
|
||||
if (lastVal)
|
||||
rules.emplace(*lastVal, app_.config().features);
|
||||
else
|
||||
rules.emplace(app_.config().features);
|
||||
app_.openLedger().accept(app_, *rules,
|
||||
sharedLCL.ledger_, localTxs_.getTxSet(), anyDisputes, retriableTxs, tapNONE,
|
||||
"consensus",
|
||||
[&](OpenView& view, beast::Journal j)
|
||||
{
|
||||
// Stuff the ledger with transactions from the queue.
|
||||
return app_.getTxQ().accept(app_, view);
|
||||
});
|
||||
|
||||
// Signal a potential fee change to subscribers after the open ledger
|
||||
// is created
|
||||
app_.getOPs().reportFeeChange();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
{
|
||||
ledgerMaster_.switchLCL (sharedLCL.ledger_);
|
||||
|
||||
// Do these need to exist?
|
||||
assert (ledgerMaster_.getClosedLedger()->info().hash == sharedLCL.id());
|
||||
assert (app_.openLedger().current()->info().parentHash == sharedLCL.id());
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
if (haveCorrectLCL_ && ! consensusFail_)
|
||||
{
|
||||
// we entered the round with the network,
|
||||
// see how close our close time is to other node's
|
||||
// close time reports, and update our clock.
|
||||
JLOG (j_.info())
|
||||
<< "We closed at " << closeTime_.time_since_epoch().count();
|
||||
using usec64_t = std::chrono::duration<std::uint64_t>;
|
||||
usec64_t closeTotal = std::chrono::duration_cast<usec64_t>(closeTime_.time_since_epoch());
|
||||
int closeCount = 1;
|
||||
|
||||
for (auto const& p : closeTimes_)
|
||||
{
|
||||
// FIXME: Use median, not average
|
||||
JLOG (j_.info())
|
||||
<< std::to_string(p.second)
|
||||
<< " time votes for "
|
||||
<< std::to_string(p.first.time_since_epoch().count());
|
||||
closeCount += p.second;
|
||||
closeTotal += std::chrono::duration_cast<usec64_t>(p.first.time_since_epoch()) * p.second;
|
||||
}
|
||||
|
||||
closeTotal += usec64_t(closeCount / 2); // for round to nearest
|
||||
closeTotal /= closeCount;
|
||||
|
||||
// Use signed times since we are subtracting
|
||||
using duration = std::chrono::duration<std::int32_t>;
|
||||
using time_point = std::chrono::time_point<NetClock, duration>;
|
||||
auto offset = time_point{closeTotal} -
|
||||
std::chrono::time_point_cast<duration>(closeTime_);
|
||||
JLOG (j_.info())
|
||||
<< "Our close offset is estimated at "
|
||||
<< offset.count() << " (" << closeCount << ")";
|
||||
|
||||
app_.timeKeeper().adjustCloseTime(offset);
|
||||
}
|
||||
|
||||
return validating_;
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::endConsensus(bool correctLCL)
|
||||
{
|
||||
app_.getOPs ().endConsensus (correctLCL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RCLConsensus::notify(
|
||||
protocol::NodeEvent ne,
|
||||
RCLCxLedger const & ledger,
|
||||
bool haveCorrectLCL)
|
||||
{
|
||||
|
||||
protocol::TMStatusChange s;
|
||||
|
||||
if (!haveCorrectLCL)
|
||||
s.set_newevent (protocol::neLOST_SYNC);
|
||||
else
|
||||
s.set_newevent(ne);
|
||||
|
||||
s.set_ledgerseq (ledger.seq());
|
||||
s.set_networktime (app_.timeKeeper().now().time_since_epoch().count());
|
||||
s.set_ledgerhashprevious(ledger.parentID().begin (),
|
||||
std::decay_t<decltype(ledger.parentID())>::bytes);
|
||||
s.set_ledgerhash (ledger.id().begin (),
|
||||
std::decay_t<decltype(ledger.id())>::bytes);
|
||||
|
||||
std::uint32_t uMin, uMax;
|
||||
if (! ledgerMaster_.getFullValidatedRange (uMin, uMax))
|
||||
{
|
||||
uMin = 0;
|
||||
uMax = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't advertise ledgers we're not willing to serve
|
||||
uMin = std::max(uMin, ledgerMaster_.getEarliestFetch ());
|
||||
}
|
||||
s.set_firstseq (uMin);
|
||||
s.set_lastseq (uMax);
|
||||
app_.overlay ().foreach (send_always (
|
||||
std::make_shared <Message> (
|
||||
s, protocol::mtSTATUS_CHANGE)));
|
||||
JLOG (j_.trace()) << "send status change to peer";
|
||||
}
|
||||
|
||||
|
||||
/** Apply a set of transactions to a ledger.
|
||||
|
||||
Typically the txFilter is used to reject transactions
|
||||
that already accepted in the prior ledger.
|
||||
|
||||
@param set set of transactions to apply
|
||||
@param view ledger to apply to
|
||||
@param txFilter callback, return false to reject txn
|
||||
@return retriable transactions
|
||||
*/
|
||||
|
||||
CanonicalTXSet
|
||||
applyTransactions (
|
||||
Application& app,
|
||||
RCLTxSet const& cSet,
|
||||
OpenView& view,
|
||||
std::function<bool(uint256 const&)> txFilter)
|
||||
{
|
||||
auto j = app.journal ("LedgerConsensus");
|
||||
|
||||
auto& set = *(cSet.map_);
|
||||
CanonicalTXSet retriableTxs (set.getHash().as_uint256());
|
||||
|
||||
|
||||
for (auto const& item : set)
|
||||
{
|
||||
if (! txFilter (item.key()))
|
||||
continue;
|
||||
|
||||
// The transaction wan't filtered
|
||||
// Add it to the set to be tried in canonical order
|
||||
JLOG (j.debug()) <<
|
||||
"Processing candidate transaction: " << item.key();
|
||||
try
|
||||
{
|
||||
retriableTxs.insert (
|
||||
std::make_shared<STTx const>(SerialIter{item.slice()}));
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
JLOG (j.warn()) << "Txn " << item.key() << " throws";
|
||||
}
|
||||
}
|
||||
|
||||
bool certainRetry = true;
|
||||
// Attempt to apply all of the retriable transactions
|
||||
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
|
||||
{
|
||||
JLOG (j.debug()) << "Pass: " << pass << " Txns: "
|
||||
<< retriableTxs.size ()
|
||||
<< (certainRetry ? " retriable" : " final");
|
||||
int changes = 0;
|
||||
|
||||
auto it = retriableTxs.begin ();
|
||||
|
||||
while (it != retriableTxs.end ())
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (applyTransaction (app, view,
|
||||
*it->second, certainRetry, tapNO_CHECK_SIGN, j))
|
||||
{
|
||||
case ApplyResult::Success:
|
||||
it = retriableTxs.erase (it);
|
||||
++changes;
|
||||
break;
|
||||
|
||||
case ApplyResult::Fail:
|
||||
it = retriableTxs.erase (it);
|
||||
break;
|
||||
|
||||
case ApplyResult::Retry:
|
||||
++it;
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
JLOG (j.warn())
|
||||
<< "Transaction throws";
|
||||
it = retriableTxs.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
JLOG (j.debug()) << "Pass: "
|
||||
<< pass << " finished " << changes << " changes";
|
||||
|
||||
// A non-retry pass made no changes
|
||||
if (!changes && !certainRetry)
|
||||
return retriableTxs;
|
||||
|
||||
// Stop retriable passes
|
||||
if (!changes || (pass >= LEDGER_RETRY_PASSES))
|
||||
certainRetry = false;
|
||||
}
|
||||
|
||||
// If there are any transactions left, we must have
|
||||
// tried them in at least one final pass
|
||||
assert (retriableTxs.empty() || !certainRetry);
|
||||
return retriableTxs;
|
||||
}
|
||||
|
||||
RCLCxLedger
|
||||
RCLConsensus::buildLCL(
|
||||
RCLCxLedger const & previousLedger,
|
||||
RCLTxSet const & set,
|
||||
NetClock::time_point closeTime,
|
||||
bool closeTimeCorrect,
|
||||
NetClock::duration closeResolution,
|
||||
NetClock::time_point now,
|
||||
std::chrono::milliseconds roundTime,
|
||||
CanonicalTXSet & retriableTxs)
|
||||
{
|
||||
auto replay = ledgerMaster_.releaseReplay();
|
||||
if (replay)
|
||||
{
|
||||
// replaying, use the time the ledger we're replaying closed
|
||||
closeTime = replay->closeTime_;
|
||||
closeTimeCorrect = ((replay->closeFlags_ & sLCF_NoConsensusTime) == 0);
|
||||
}
|
||||
|
||||
JLOG (j_.debug())
|
||||
<< "Report: TxSt = " << set.id ()
|
||||
<< ", close " << closeTime.time_since_epoch().count()
|
||||
<< (closeTimeCorrect ? "" : "X");
|
||||
|
||||
|
||||
// Build the new last closed ledger
|
||||
auto buildLCL = std::make_shared<Ledger>(*previousLedger.ledger_, now);
|
||||
|
||||
auto const v2_enabled = buildLCL->rules().enabled(featureSHAMapV2);
|
||||
|
||||
auto v2_transition = false;
|
||||
if (v2_enabled && !buildLCL->stateMap().is_v2())
|
||||
{
|
||||
buildLCL->make_v2();
|
||||
v2_transition = true;
|
||||
}
|
||||
|
||||
// Set up to write SHAMap changes to our database,
|
||||
// perform updates, extract changes
|
||||
JLOG (j_.debug())
|
||||
<< "Applying consensus set transactions to the"
|
||||
<< " last closed ledger";
|
||||
|
||||
{
|
||||
OpenView accum(&*buildLCL);
|
||||
assert(!accum.open());
|
||||
if (replay)
|
||||
{
|
||||
// Special case, we are replaying a ledger close
|
||||
for (auto& tx : replay->txns_)
|
||||
applyTransaction (app_, accum, *tx.second,
|
||||
false, tapNO_CHECK_SIGN, j_);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal case, we are not replaying a ledger close
|
||||
retriableTxs = applyTransactions (app_, set, accum,
|
||||
[&buildLCL](uint256 const& txID)
|
||||
{
|
||||
return ! buildLCL->txExists(txID);
|
||||
});
|
||||
}
|
||||
// Update fee computations.
|
||||
app_.getTxQ().processClosedLedger(app_, accum,
|
||||
roundTime > 5s);
|
||||
accum.apply(*buildLCL);
|
||||
}
|
||||
|
||||
// retriableTxs will include any transactions that
|
||||
// made it into the consensus set but failed during application
|
||||
// to the ledger.
|
||||
|
||||
buildLCL->updateSkipList ();
|
||||
|
||||
{
|
||||
// Write the final version of all modified SHAMap
|
||||
// nodes to the node store to preserve the new LCL
|
||||
|
||||
int asf = buildLCL->stateMap().flushDirty (
|
||||
hotACCOUNT_NODE, buildLCL->info().seq);
|
||||
int tmf = buildLCL->txMap().flushDirty (
|
||||
hotTRANSACTION_NODE, buildLCL->info().seq);
|
||||
JLOG (j_.debug()) << "Flushed " <<
|
||||
asf << " accounts and " <<
|
||||
tmf << " transaction nodes";
|
||||
}
|
||||
buildLCL->unshare();
|
||||
|
||||
// Accept ledger
|
||||
buildLCL->setAccepted(closeTime, closeResolution,
|
||||
closeTimeCorrect, app_.config());
|
||||
|
||||
// And stash the ledger in the ledger master
|
||||
if (ledgerMaster_.storeLedger (buildLCL))
|
||||
JLOG (j_.debug())
|
||||
<< "Consensus built ledger we already had";
|
||||
else if (app_.getInboundLedgers().find (buildLCL->info().hash))
|
||||
JLOG (j_.debug())
|
||||
<< "Consensus built ledger we were acquiring";
|
||||
else
|
||||
JLOG (j_.debug())
|
||||
<< "Consensus built new ledger";
|
||||
return RCLCxLedger{std::move(buildLCL)};
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::validate(
|
||||
RCLCxLedger const & ledger,
|
||||
NetClock::time_point now,
|
||||
bool proposing)
|
||||
{
|
||||
auto validationTime = now;
|
||||
if (validationTime <= lastValidationTime_)
|
||||
validationTime = lastValidationTime_ + 1s;
|
||||
lastValidationTime_ = validationTime;
|
||||
|
||||
// Build validation
|
||||
auto v = std::make_shared<STValidation> (ledger.id(),
|
||||
validationTime, valPublic_, proposing);
|
||||
v->setFieldU32 (sfLedgerSequence, ledger.seq());
|
||||
|
||||
// Add our load fee to the validation
|
||||
auto const& feeTrack = app_.getFeeTrack();
|
||||
std::uint32_t fee = std::max(
|
||||
feeTrack.getLocalFee(),
|
||||
feeTrack.getClusterFee());
|
||||
|
||||
if (fee > feeTrack.getLoadBase())
|
||||
v->setFieldU32(sfLoadFee, fee);
|
||||
|
||||
if (((ledger.seq() + 1) % 256) == 0)
|
||||
// next ledger is flag ledger
|
||||
{
|
||||
// Suggest fee changes and new features
|
||||
feeVote_->doValidation (ledger.ledger_, *v);
|
||||
app_.getAmendmentTable ().doValidation (ledger.ledger_, *v);
|
||||
}
|
||||
|
||||
auto const signingHash = v->sign (valSecret_);
|
||||
v->setTrusted ();
|
||||
// suppress it if we receive it - FIXME: wrong suppression
|
||||
app_.getHashRouter ().addSuppression (signingHash);
|
||||
app_.getValidations ().addValidation (v, "local");
|
||||
Blob validation = v->getSerialized ();
|
||||
protocol::TMValidation val;
|
||||
val.set_validation (&validation[0], validation.size ());
|
||||
// Send signed validation to all of our directly connected peers
|
||||
app_.overlay().send(val);
|
||||
}
|
||||
|
||||
PublicKey const&
|
||||
RCLConsensus::getValidationPublicKey () const
|
||||
{
|
||||
return valPublic_;
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensus::setValidationKeys (SecretKey const& valSecret,
|
||||
PublicKey const& valPublic)
|
||||
{
|
||||
valSecret_ = valSecret;
|
||||
valPublic_ = valPublic;
|
||||
}
|
||||
|
||||
}
|
||||
383
src/ripple/app/consensus/RCLConsensus.h
Normal file
383
src/ripple/app/consensus/RCLConsensus.h
Normal file
@@ -0,0 +1,383 @@
|
||||
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_CONSENSUS_RCLCONSENSUS_H_INCLUDED
|
||||
#define RIPPLE_APP_CONSENSUS_RCLCONSENSUS_H_INCLUDED
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/protocol/STValidation.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/app/misc/FeeVote.h>
|
||||
#include <ripple/protocol/RippleLedgerHash.h>
|
||||
#include <ripple/app/consensus/RCLCxLedger.h>
|
||||
#include <ripple/app/consensus/RCLCxTx.h>
|
||||
#include <ripple/app/consensus/RCLCxPeerPos.h>
|
||||
#include <ripple/core/JobQueue.h>
|
||||
#include <ripple/consensus/Consensus.h>
|
||||
#include <ripple/basics/CountedObject.h>
|
||||
#include <ripple/overlay/Message.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class InboundTransactions;
|
||||
class LocalTxs;
|
||||
class LedgerMaster;
|
||||
|
||||
//! Types used to adapt consensus for RCL
|
||||
struct RCLCxTraits
|
||||
{
|
||||
//! Ledger type presented to Consensus
|
||||
using Ledger_t = RCLCxLedger;
|
||||
//! Peer identifier type used in Consensus
|
||||
using NodeID_t = NodeID;
|
||||
//! TxSet type presented to Consensus
|
||||
using TxSet_t = RCLTxSet;
|
||||
//! MissingTxException type neede by Consensus
|
||||
using MissingTxException_t = SHAMapMissingNode;
|
||||
};
|
||||
|
||||
|
||||
/** Adapts the generic Consensus algorithm for use by RCL.
|
||||
|
||||
@note The enabled_shared_from_this base allows the application to properly
|
||||
create a shared instance of RCLConsensus for use in the accept logic..
|
||||
*/
|
||||
class RCLConsensus : public Consensus<RCLConsensus, RCLCxTraits>
|
||||
, public std::enable_shared_from_this <RCLConsensus>
|
||||
, public CountedObject <RCLConsensus>
|
||||
{
|
||||
using Base = Consensus<RCLConsensus, RCLCxTraits>;
|
||||
using Base::accept;
|
||||
public:
|
||||
|
||||
//! Constructor
|
||||
RCLConsensus(
|
||||
Application& app,
|
||||
std::unique_ptr<FeeVote> && feeVote,
|
||||
LedgerMaster& ledgerMaster,
|
||||
LocalTxs& localTxs,
|
||||
InboundTransactions& inboundTransactions,
|
||||
typename Base::clock_type const & clock,
|
||||
beast::Journal journal);
|
||||
RCLConsensus(RCLConsensus const&) = delete;
|
||||
RCLConsensus& operator=(RCLConsensus const&) = delete;
|
||||
|
||||
static char const* getCountedObjectName() { return "Consensus"; }
|
||||
|
||||
/** Save the given consensus proposed by a peer with nodeID for later
|
||||
use in consensus.
|
||||
|
||||
@param peerPos Proposed peer position
|
||||
@param nodeID ID of peer
|
||||
*/
|
||||
void
|
||||
storeProposal( RCLCxPeerPos::ref peerPos, NodeID const& nodeID);
|
||||
|
||||
/** Returns validation public key */
|
||||
PublicKey const&
|
||||
getValidationPublicKey () const;
|
||||
|
||||
/** Set validation private and public key pair. */
|
||||
void
|
||||
setValidationKeys (SecretKey const& valSecret, PublicKey const& valPublic);
|
||||
|
||||
private:
|
||||
friend class Consensus<RCLConsensus, RCLCxTraits>;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Consensus type requirements.
|
||||
|
||||
/** Notification that a new consensus round has begun.
|
||||
|
||||
@param ledger The ledger we are building consensus on
|
||||
*/
|
||||
void
|
||||
onStartRound(RCLCxLedger const & ledger);
|
||||
|
||||
//! @return Whether consensus should be (proposing, validating)
|
||||
std::pair <bool, bool>
|
||||
getMode ();
|
||||
|
||||
/** Attempt to acquire a specific ledger.
|
||||
|
||||
If not available, asynchronously acquires from the network.
|
||||
|
||||
@param ledger The ID/hash of the ledger acquire
|
||||
@return Optional ledger, will be seated if we locally had the ledger
|
||||
*/
|
||||
boost::optional<RCLCxLedger>
|
||||
acquireLedger(LedgerHash const & ledger);
|
||||
|
||||
/** Get peers' proposed positions.
|
||||
@param prevLedger The base ledger which proposals are based on
|
||||
@return The set of proposals
|
||||
*/
|
||||
std::vector<RCLCxPeerPos>
|
||||
proposals (LedgerHash const& prevLedger);
|
||||
|
||||
/** Relay the given proposal to all peers
|
||||
|
||||
@param peerPos The peer position to relay.
|
||||
*/
|
||||
void
|
||||
relay(RCLCxPeerPos const & peerPos);
|
||||
|
||||
/** Relay disputed transacction to peers.
|
||||
|
||||
Only relay if the provided transaction hasn't been shared recently.
|
||||
|
||||
@param dispute The disputed transaction to relay.
|
||||
*/
|
||||
void
|
||||
relay(DisputedTx <RCLCxTx, NodeID> const & dispute);
|
||||
|
||||
/** Acquire the transaction set associated with a proposal.
|
||||
|
||||
If the transaction set is not available locally, will attempt acquire it
|
||||
from the network.
|
||||
|
||||
@param setId The transaction set ID associated with the proposal
|
||||
@return Optional set of transactions, seated if available.
|
||||
*/
|
||||
boost::optional<RCLTxSet>
|
||||
acquireTxSet(RCLTxSet::ID const & setId);
|
||||
|
||||
/** Whether the open ledger has any transactions
|
||||
*/
|
||||
bool
|
||||
hasOpenTransactions() const;
|
||||
|
||||
/** Number of proposers that have vallidated the given ledger
|
||||
|
||||
@param h The hash of the ledger of interest
|
||||
@return the number of proposers that validated a ledger
|
||||
*/
|
||||
std::size_t
|
||||
proposersValidated(LedgerHash const & h) const;
|
||||
|
||||
/** Number of proposers that have validated a ledger descended from requested ledger.
|
||||
|
||||
@param h The hash of the ledger of interest.
|
||||
@return The number of validating peers that have validated a ledger
|
||||
succeeding the one provided.
|
||||
*/
|
||||
std::size_t
|
||||
proposersFinished(LedgerHash const & h) const;
|
||||
|
||||
/** Propose the given position to my peers.
|
||||
|
||||
@param proposal Our proposed position
|
||||
*/
|
||||
void
|
||||
propose (RCLCxPeerPos::Proposal const& proposal);
|
||||
|
||||
/** Share the given tx set with peers.
|
||||
|
||||
@param set The TxSet to share.
|
||||
*/
|
||||
void
|
||||
share (RCLTxSet const& set);
|
||||
|
||||
/** Get the last closed ledger (LCL) seen on the network
|
||||
|
||||
@param currentLedger Current ledger used in consensus
|
||||
@param priorLedger Prior ledger used in consensus
|
||||
@param believedCorrect Whether consensus believes currentLedger is LCL
|
||||
|
||||
@return The hash of the last closed network
|
||||
*/
|
||||
uint256
|
||||
getLCL (
|
||||
uint256 const& currentLedger,
|
||||
uint256 const& priorLedger,
|
||||
bool believedCorrect);
|
||||
|
||||
|
||||
/** Notification that the ledger has closed.
|
||||
|
||||
@param ledger the ledger we are changing to
|
||||
@param haveCorrectLCL whether we believe this is the correct LCL
|
||||
*/
|
||||
void
|
||||
onClose(RCLCxLedger const & ledger, bool haveCorrectLCL);
|
||||
|
||||
/** Create our initial position of transactions to accept in this round
|
||||
of consensus.
|
||||
|
||||
@param prevLedger The ledger the transactions apply to
|
||||
@param isProposing Whether we are currently proposing
|
||||
@param isCorrectLCL Whether we have the correct LCL
|
||||
@param closeTime When we believe the ledger closed
|
||||
@param now The current network adjusted time
|
||||
|
||||
@return Pair of (i) transactions we believe are in the ledger
|
||||
(ii) the corresponding proposal of those transactions
|
||||
to send to peers
|
||||
*/
|
||||
std::pair <RCLTxSet, typename RCLCxPeerPos::Proposal>
|
||||
makeInitialPosition (
|
||||
RCLCxLedger const & prevLedger,
|
||||
bool isProposing,
|
||||
bool isCorrectLCL,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now);
|
||||
|
||||
|
||||
/** Dispatch a call to Consensus::accept
|
||||
|
||||
Accepting a ledger may be expensive, so this function can dispatch
|
||||
that call to another thread if desired and must call the accept
|
||||
method of the generic consensus algorithm.
|
||||
|
||||
@param txSet The transactions to accept.
|
||||
*/
|
||||
void
|
||||
dispatchAccept(RCLTxSet const & txSet);
|
||||
|
||||
|
||||
/** Accept a new ledger based on the given transactions.
|
||||
|
||||
TODO: Too many arguments, need to group related types.
|
||||
|
||||
@param set The set of accepted transactions
|
||||
@param consensusCloseTime Consensus agreed upon close time
|
||||
@param proposing_ Whether we are proposing
|
||||
@param validating_ Whether we are validating
|
||||
@param haveCorrectLCL_ Whether we had the correct last closed ledger
|
||||
@param consensusFail_ Whether consensus failed
|
||||
@param prevLedgerHash_ The hash/id of the previous ledger
|
||||
@param previousLedger_ The previous ledger
|
||||
@param closeResolution_ The close time resolution used this round
|
||||
@param now Current network adjsuted time
|
||||
@param roundTime_ Duration of this consensus round
|
||||
@param disputes_ Disputed trarnsactions from this round
|
||||
@param closeTimes_ Histogram of peers close times
|
||||
@param closeTime Our close time
|
||||
@return Whether we should continue validating
|
||||
*/
|
||||
bool
|
||||
accept(
|
||||
RCLTxSet const& set,
|
||||
NetClock::time_point consensusCloseTime,
|
||||
bool proposing_,
|
||||
bool validating_,
|
||||
bool haveCorrectLCL_,
|
||||
bool consensusFail_,
|
||||
LedgerHash const &prevLedgerHash_,
|
||||
RCLCxLedger const & previousLedger_,
|
||||
NetClock::duration closeResolution_,
|
||||
NetClock::time_point const & now,
|
||||
std::chrono::milliseconds const & roundTime_,
|
||||
hash_map<RCLCxTx::ID, DisputedTx <RCLCxTx, NodeID>> const & disputes_,
|
||||
std::map <NetClock::time_point, int> closeTimes_,
|
||||
NetClock::time_point const & closeTime
|
||||
);
|
||||
|
||||
/** Signal the end of consensus to the application, which will start the
|
||||
next round.
|
||||
|
||||
@param correctLCL Whether we believe we have the correct LCL
|
||||
*/
|
||||
void
|
||||
endConsensus(bool correctLCL);
|
||||
|
||||
//!-------------------------------------------------------------------------
|
||||
// Additional members (not directly required by Consensus interface)
|
||||
/** Notify peers of a consensus state change
|
||||
|
||||
@param ne Event type for notification
|
||||
@param ledger The ledger at the time of the state change
|
||||
@param haveCorrectLCL Whether we believ we have the correct LCL.
|
||||
*/
|
||||
void
|
||||
notify(protocol::NodeEvent ne, RCLCxLedger const & ledger, bool haveCorrectLCL);
|
||||
|
||||
/** Build the new last closed ledger.
|
||||
|
||||
Accept the given the provided set of consensus transactions and build
|
||||
the last closed ledger. Since consensus just agrees on which
|
||||
transactions to apply, but not whether they make it into the closed
|
||||
ledger, this function also populates retriableTxs with those that can
|
||||
be retried in the next round.
|
||||
|
||||
@param previousLedger Prior ledger building upon
|
||||
@param set The set of transactions to apply to the ledger
|
||||
@param closeTime The the ledger closed
|
||||
@param closeTimeCorrect Whether consensus agreed on close time
|
||||
@param closeResolution Resolution used to determine consensus close time
|
||||
@param now Current network adjusted time
|
||||
@param roundTime Duration of this consensus rorund
|
||||
@param retriableTxs Populate with transactions to retry in next round
|
||||
@return The newly built ledger
|
||||
*/
|
||||
RCLCxLedger
|
||||
buildLCL(
|
||||
RCLCxLedger const & previousLedger,
|
||||
RCLTxSet const & set,
|
||||
NetClock::time_point closeTime,
|
||||
bool closeTimeCorrect,
|
||||
NetClock::duration closeResolution,
|
||||
NetClock::time_point now,
|
||||
std::chrono::milliseconds roundTime,
|
||||
CanonicalTXSet & retriableTxs
|
||||
);
|
||||
|
||||
/** Validate the given ledger and share with peers as necessary
|
||||
|
||||
@param ledger The ledger to validate
|
||||
@param now Current network adjusted time
|
||||
@param proposing Whether we were proposing transactions while generating
|
||||
this ledger. If we are not proposing, a validation
|
||||
can still be sent to inform peers that we know we
|
||||
aren't fully participating in consensus but are still
|
||||
around and trying to catch up.
|
||||
*/
|
||||
void
|
||||
validate(
|
||||
RCLCxLedger const & ledger,
|
||||
NetClock::time_point now,
|
||||
bool proposing);
|
||||
|
||||
//!-------------------------------------------------------------------------
|
||||
Application& app_;
|
||||
std::unique_ptr <FeeVote> feeVote_;
|
||||
LedgerMaster & ledgerMaster_;
|
||||
LocalTxs & localTxs_;
|
||||
InboundTransactions& inboundTransactions_;
|
||||
beast::Journal j_;
|
||||
|
||||
NodeID nodeID_;
|
||||
PublicKey valPublic_;
|
||||
SecretKey valSecret_;
|
||||
LedgerHash acquiringLedger_;
|
||||
|
||||
// The timestamp of the last validation we used, in network time. This is
|
||||
// only used for our own validations.
|
||||
NetClock::time_point lastValidationTime_;
|
||||
|
||||
using PeerPositions = hash_map <NodeID, std::deque<RCLCxPeerPos::pointer>>;
|
||||
PeerPositions peerPositions_;
|
||||
std::mutex peerPositionsLock_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
121
src/ripple/app/consensus/RCLCxLedger.h
Normal file
121
src/ripple/app/consensus/RCLCxLedger.h
Normal file
@@ -0,0 +1,121 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_CONSENSUS_RCLCXLEDGER_H_INCLUDED
|
||||
#define RIPPLE_APP_CONSENSUS_RCLCXLEDGER_H_INCLUDED
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <ripple/app/ledger/LedgerToJson.h>
|
||||
#include <ripple/protocol/RippleLedgerHash.h>
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Represents a ledger in RCLConsensus.
|
||||
|
||||
RCLCxLedger is a thin wrapper over `std::shared_ptr<Ledger const>`.
|
||||
*/
|
||||
class RCLCxLedger
|
||||
{
|
||||
public:
|
||||
//! Unique identifier of a ledger
|
||||
using ID = LedgerHash;
|
||||
|
||||
/** Default constructor
|
||||
|
||||
TODO: This may not be needed if we ensure RCLConsensus is handed a valid
|
||||
ledger in its constructor. Its bad now because other members are not
|
||||
checking whether the ledger is valid.
|
||||
*/
|
||||
RCLCxLedger() = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param l The ledger to wrap.
|
||||
*/
|
||||
RCLCxLedger(std::shared_ptr<Ledger const> const & l) : ledger_{ l } {}
|
||||
|
||||
//! Sequence number of the ledger.
|
||||
auto const &
|
||||
seq() const
|
||||
{
|
||||
return ledger_->info().seq;
|
||||
}
|
||||
|
||||
//! Unique identifier (hash) of this ledger.
|
||||
auto const &
|
||||
id() const
|
||||
{
|
||||
return ledger_->info().hash;
|
||||
}
|
||||
|
||||
//! Unique identifier (hash) of this ledger's parent.
|
||||
auto const &
|
||||
parentID() const
|
||||
{
|
||||
return ledger_->info().parentHash;
|
||||
}
|
||||
|
||||
//! Resolution used when calculating this ledger's close time.
|
||||
auto
|
||||
closeTimeResolution() const
|
||||
{
|
||||
return ledger_->info().closeTimeResolution;
|
||||
}
|
||||
|
||||
//! Whether consensus process agreed on close time of the ledger.
|
||||
bool
|
||||
closeAgree() const
|
||||
{
|
||||
return ripple::getCloseAgree(ledger_->info());
|
||||
}
|
||||
|
||||
//! The close time of this ledger
|
||||
auto
|
||||
closeTime() const
|
||||
{
|
||||
return ledger_->info().closeTime;
|
||||
}
|
||||
|
||||
//! The close time of this ledger's parent.
|
||||
auto
|
||||
parentCloseTime() const
|
||||
{
|
||||
return ledger_->info().parentCloseTime;
|
||||
}
|
||||
|
||||
//! JSON representation of this ledger.
|
||||
Json::Value
|
||||
getJson() const
|
||||
{
|
||||
return ripple::getJson(*ledger_);
|
||||
}
|
||||
|
||||
/** The ledger instance.
|
||||
|
||||
TODO: Make this shared_ptr<ReadView const> .. requires ability to create
|
||||
a new ledger from a readView?
|
||||
*/
|
||||
std::shared_ptr<Ledger const> ledger_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -18,7 +18,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/ledger/LedgerProposal.h>
|
||||
#include <ripple/app/consensus/RCLCxPeerPos.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
@@ -28,97 +28,40 @@
|
||||
namespace ripple {
|
||||
|
||||
// Used to construct received proposals
|
||||
LedgerProposal::LedgerProposal (
|
||||
uint256 const& pLgr,
|
||||
std::uint32_t seq,
|
||||
uint256 const& tx,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now,
|
||||
RCLCxPeerPos::RCLCxPeerPos (
|
||||
PublicKey const& publicKey,
|
||||
NodeID const& nodeID,
|
||||
Slice const& signature,
|
||||
uint256 const& suppression)
|
||||
: mPreviousLedger (pLgr)
|
||||
, mCurrentHash (tx)
|
||||
, mSuppression (suppression)
|
||||
, mCloseTime (closeTime)
|
||||
, mProposeSeq (seq)
|
||||
, publicKey_ (publicKey)
|
||||
, mPeerID (nodeID)
|
||||
, mTime (now)
|
||||
uint256 const& suppression,
|
||||
Proposal && proposal)
|
||||
: proposal_{ std::move(proposal)}
|
||||
, mSuppression {suppression}
|
||||
, publicKey_{publicKey}
|
||||
, signature_{signature}
|
||||
{
|
||||
signature_.resize (signature.size());
|
||||
std::memcpy(signature_.data(),
|
||||
signature.data(), signature.size());
|
||||
}
|
||||
|
||||
// Used to construct local proposals
|
||||
// CAUTION: publicKey_ not set
|
||||
LedgerProposal::LedgerProposal (
|
||||
uint256 const& prevLgr,
|
||||
uint256 const& position,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now)
|
||||
: mPreviousLedger (prevLgr)
|
||||
, mCurrentHash (position)
|
||||
, mCloseTime (closeTime)
|
||||
, mProposeSeq (seqJoin)
|
||||
, mTime (now)
|
||||
{
|
||||
}
|
||||
|
||||
uint256 LedgerProposal::getSigningHash () const
|
||||
uint256 RCLCxPeerPos::getSigningHash () const
|
||||
{
|
||||
return sha512Half(
|
||||
HashPrefix::proposal,
|
||||
std::uint32_t(mProposeSeq),
|
||||
mCloseTime.time_since_epoch().count(),
|
||||
mPreviousLedger,
|
||||
mCurrentHash);
|
||||
std::uint32_t(proposal().proposeSeq()),
|
||||
proposal().closeTime().time_since_epoch().count(),
|
||||
proposal().prevLedger(),
|
||||
proposal().position());
|
||||
}
|
||||
|
||||
bool LedgerProposal::checkSign () const
|
||||
bool RCLCxPeerPos::checkSign () const
|
||||
{
|
||||
return verifyDigest (
|
||||
publicKey_,
|
||||
getSigningHash(),
|
||||
makeSlice (signature_),
|
||||
signature_,
|
||||
false);
|
||||
}
|
||||
|
||||
bool LedgerProposal::changePosition (
|
||||
uint256 const& newPosition,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now)
|
||||
Json::Value RCLCxPeerPos::getJson () const
|
||||
{
|
||||
if (mProposeSeq == seqLeave)
|
||||
return false;
|
||||
|
||||
mCurrentHash = newPosition;
|
||||
mCloseTime = closeTime;
|
||||
mTime = now;
|
||||
++mProposeSeq;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LedgerProposal::bowOut (NetClock::time_point now)
|
||||
{
|
||||
mTime = now;
|
||||
mProposeSeq = seqLeave;
|
||||
}
|
||||
|
||||
Json::Value LedgerProposal::getJson () const
|
||||
{
|
||||
Json::Value ret = Json::objectValue;
|
||||
ret[jss::previous_ledger] = to_string (mPreviousLedger);
|
||||
|
||||
if (mProposeSeq != seqLeave)
|
||||
{
|
||||
ret[jss::transaction_hash] = to_string (mCurrentHash);
|
||||
ret[jss::propose_seq] = mProposeSeq;
|
||||
}
|
||||
|
||||
ret[jss::close_time] = mCloseTime.time_since_epoch().count();
|
||||
auto ret = proposal().getJson();
|
||||
|
||||
if (publicKey_.size())
|
||||
ret[jss::peer_id] = toBase58 (
|
||||
@@ -17,8 +17,8 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_LEDGER_LEDGERPROPOSAL_H_INCLUDED
|
||||
#define RIPPLE_APP_LEDGER_LEDGERPROPOSAL_H_INCLUDED
|
||||
#ifndef RIPPLE_APP_CONSENSUS_RCLCXPEERPOS_H_INCLUDED
|
||||
#define RIPPLE_APP_CONSENSUS_RCLCXPEERPOS_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/CountedObject.h>
|
||||
#include <ripple/basics/base_uint.h>
|
||||
@@ -27,105 +27,84 @@
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/SecretKey.h>
|
||||
#include <ripple/beast/hash/hash_append.h>
|
||||
#include <ripple/consensus/ConsensusProposal.h>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class LedgerProposal
|
||||
: public CountedObject <LedgerProposal>
|
||||
/** A peer's signed, proposed position for use in RCLConsensus.
|
||||
|
||||
Carries a ConsensusProposal signed by a peer.
|
||||
*/
|
||||
class RCLCxPeerPos
|
||||
: public CountedObject <RCLCxPeerPos>
|
||||
{
|
||||
private:
|
||||
// A peer initial joins the consensus process
|
||||
static std::uint32_t const seqJoin = 0;
|
||||
|
||||
// A peer wants to bow out and leave the consensus process
|
||||
static std::uint32_t const seqLeave = 0xffffffff;
|
||||
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "LedgerProposal"; }
|
||||
|
||||
using pointer = std::shared_ptr<LedgerProposal>;
|
||||
static char const* getCountedObjectName () { return "RCLCxPeerPos"; }
|
||||
using pointer = std::shared_ptr<RCLCxPeerPos>;
|
||||
using ref = const pointer&;
|
||||
|
||||
// proposal from peer
|
||||
LedgerProposal (
|
||||
uint256 const& prevLgr,
|
||||
std::uint32_t proposeSeq,
|
||||
uint256 const& propose,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now,
|
||||
//< The type of the proposed position
|
||||
using Proposal = ConsensusProposal<NodeID, uint256, uint256>;
|
||||
|
||||
|
||||
/** Constructor
|
||||
|
||||
Constructs a signed peer position.
|
||||
|
||||
@param publicKey Public key of the peer
|
||||
@param signature Signature provided with the proposal
|
||||
@param suppress ????
|
||||
@param proposal The consensus proposal
|
||||
*/
|
||||
|
||||
RCLCxPeerPos (
|
||||
PublicKey const& publicKey,
|
||||
NodeID const& nodeID,
|
||||
Slice const& signature,
|
||||
uint256 const& suppress);
|
||||
|
||||
// Our own proposal:
|
||||
LedgerProposal (
|
||||
uint256 const& prevLedger,
|
||||
uint256 const& position,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now);
|
||||
uint256 const& suppress,
|
||||
Proposal && proposal);
|
||||
|
||||
//! Create the signing hash for the proposal
|
||||
uint256 getSigningHash () const;
|
||||
|
||||
//! Verify the signing hash of the proposal
|
||||
bool checkSign () const;
|
||||
|
||||
NodeID const& getPeerID () const
|
||||
//! Signature of the proposal (not necessarily verified)
|
||||
Slice getSignature () const
|
||||
{
|
||||
return mPeerID;
|
||||
}
|
||||
uint256 const& getCurrentHash () const
|
||||
{
|
||||
return mCurrentHash;
|
||||
}
|
||||
uint256 const& getPrevLedger () const
|
||||
{
|
||||
return mPreviousLedger;
|
||||
return signature_;
|
||||
}
|
||||
|
||||
//! Public key of peer that sent the proposal
|
||||
PublicKey const& getPublicKey () const
|
||||
{
|
||||
return publicKey_;
|
||||
}
|
||||
|
||||
//! ?????
|
||||
uint256 const& getSuppressionID () const
|
||||
{
|
||||
return mSuppression;
|
||||
}
|
||||
std::uint32_t getProposeSeq () const
|
||||
|
||||
//! The consensus proposal
|
||||
Proposal const & proposal() const
|
||||
{
|
||||
return mProposeSeq;
|
||||
}
|
||||
NetClock::time_point getCloseTime () const
|
||||
{
|
||||
return mCloseTime;
|
||||
}
|
||||
NetClock::time_point getSeenTime () const
|
||||
{
|
||||
return mTime;
|
||||
}
|
||||
Blob const& getSignature () const
|
||||
{
|
||||
return signature_;
|
||||
}
|
||||
bool isInitial () const
|
||||
{
|
||||
return mProposeSeq == seqJoin;
|
||||
}
|
||||
bool isBowOut () const
|
||||
{
|
||||
return mProposeSeq == seqLeave;
|
||||
return proposal_;
|
||||
}
|
||||
|
||||
bool isStale (NetClock::time_point cutoff) const
|
||||
/// @cond Ignore
|
||||
//! Add a conversion operator to conform to the Consensus interface
|
||||
operator Proposal const &() const
|
||||
{
|
||||
return mTime <= cutoff;
|
||||
return proposal_;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
bool changePosition (
|
||||
uint256 const& newPosition,
|
||||
NetClock::time_point newCloseTime,
|
||||
NetClock::time_point now);
|
||||
void bowOut (NetClock::time_point now);
|
||||
//! JSON representation of proposal
|
||||
Json::Value getJson () const;
|
||||
|
||||
private:
|
||||
@@ -135,21 +114,16 @@ private:
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, HashPrefix::proposal);
|
||||
hash_append(h, std::uint32_t(mProposeSeq));
|
||||
hash_append(h, mCloseTime);
|
||||
hash_append(h, mPreviousLedger);
|
||||
hash_append(h, mCurrentHash);
|
||||
hash_append(h, std::uint32_t(proposal().proposeSeq()));
|
||||
hash_append(h, proposal().closeTime());
|
||||
hash_append(h, proposal().prevLedger());
|
||||
hash_append(h, proposal().position());
|
||||
}
|
||||
|
||||
uint256 mPreviousLedger, mCurrentHash, mSuppression;
|
||||
NetClock::time_point mCloseTime;
|
||||
std::uint32_t mProposeSeq;
|
||||
|
||||
Proposal proposal_;
|
||||
uint256 mSuppression;
|
||||
PublicKey publicKey_;
|
||||
NodeID mPeerID;
|
||||
Blob signature_;
|
||||
|
||||
NetClock::time_point mTime;
|
||||
Buffer signature_;
|
||||
};
|
||||
|
||||
/** Calculate a unique identifier for a signed proposal.
|
||||
@@ -160,6 +134,13 @@ private:
|
||||
present. Recipients of the proposal will inject the last closed ledger in
|
||||
order to validate the signature. If the last closed ledger is left out, then
|
||||
it is considered as all zeroes for the purposes of signing.
|
||||
|
||||
@param proposeHash The hash of the proposed position
|
||||
@param previousLedger The hash of the ledger the proposal is based upon
|
||||
@param proposeSeq Sequence number of the proposal
|
||||
@param closeTime Close time of the proposal
|
||||
@param publicKey Signer's public key
|
||||
@param signature Proposal signature
|
||||
*/
|
||||
uint256 proposalUniqueId (
|
||||
uint256 const& proposeHash,
|
||||
@@ -1,143 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_CONSENSUS_RCLCXPOSITION_H_INCLUDED
|
||||
#define RIPPLE_APP_CONSENSUS_RCLCXPOSITION_H_INCLUDED
|
||||
|
||||
#include <ripple/app/ledger/LedgerProposal.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// A position taken during a consensus round
|
||||
// As seen by the RCL consensus process
|
||||
class RCLCxPos
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
static std::uint32_t constexpr seqInitial = 0;
|
||||
static std::uint32_t constexpr seqLeave = 0xffffffff;
|
||||
|
||||
RCLCxPos (LedgerProposal const& prop) :
|
||||
proposal_ (prop)
|
||||
{ }
|
||||
|
||||
std::uint32_t getSequence() const
|
||||
{
|
||||
return proposal_.getProposeSeq();
|
||||
}
|
||||
|
||||
NetClock::time_point getCloseTime () const
|
||||
{
|
||||
return proposal_.getCloseTime();
|
||||
}
|
||||
|
||||
NetClock::time_point getSeenTime() const
|
||||
{
|
||||
return proposal_.getSeenTime();
|
||||
}
|
||||
|
||||
bool isStale (NetClock::time_point lastValid) const
|
||||
{
|
||||
return getSeenTime() < lastValid;
|
||||
}
|
||||
|
||||
NodeID const& getNodeID() const
|
||||
{
|
||||
return proposal_.getPeerID();
|
||||
}
|
||||
|
||||
LedgerHash const& getPosition() const
|
||||
{
|
||||
return proposal_.getCurrentHash();
|
||||
}
|
||||
|
||||
LedgerHash const& getPrevLedger() const
|
||||
{
|
||||
return proposal_.getPrevLedger();
|
||||
}
|
||||
|
||||
bool changePosition (
|
||||
LedgerHash const& position,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now)
|
||||
{
|
||||
return proposal_.changePosition (position, closeTime, now);
|
||||
}
|
||||
|
||||
bool bowOut (NetClock::time_point now)
|
||||
{
|
||||
if (isBowOut ())
|
||||
return false;
|
||||
|
||||
proposal_.bowOut (now);
|
||||
return true;
|
||||
}
|
||||
|
||||
Json::Value getJson() const
|
||||
{
|
||||
return proposal_.getJson();
|
||||
}
|
||||
|
||||
bool isInitial () const
|
||||
{
|
||||
return getSequence() == seqInitial;
|
||||
}
|
||||
|
||||
bool isBowOut() const
|
||||
{
|
||||
return getSequence() == seqLeave;
|
||||
}
|
||||
|
||||
// These three functions will be removed. New code
|
||||
// should use getPosition, getSequence and getNodeID
|
||||
LedgerHash const& getCurrentHash() const
|
||||
{
|
||||
return getPosition();
|
||||
}
|
||||
NodeID const& getPeerID() const
|
||||
{
|
||||
return getNodeID();
|
||||
}
|
||||
std::uint32_t getProposeSeq() const
|
||||
{
|
||||
return getSequence();
|
||||
}
|
||||
|
||||
LedgerProposal const& peek() const
|
||||
{
|
||||
return proposal_;
|
||||
}
|
||||
|
||||
LedgerProposal& peek()
|
||||
{
|
||||
return proposal_;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
LedgerProposal proposal_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -23,108 +23,160 @@
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Transactions, as seen by the consensus code in the rippled app
|
||||
/** Represents a transaction in RCLConsensus.
|
||||
|
||||
RCLCxTx is a thin wrapper over the SHAMapItem that corresponds to the
|
||||
transaction.
|
||||
*/
|
||||
class RCLCxTx
|
||||
{
|
||||
public:
|
||||
//! Unique identifier/hash of transaction
|
||||
using ID = uint256;
|
||||
|
||||
RCLCxTx (SHAMapItem const& txn) : txn_ (txn)
|
||||
/** Constructor
|
||||
|
||||
@param txn The transaction to wrap
|
||||
*/
|
||||
RCLCxTx(SHAMapItem const& txn) : tx_{ txn }
|
||||
{ }
|
||||
|
||||
uint256 const& getID() const
|
||||
//! The unique identifier/hash of the transaction
|
||||
ID const&
|
||||
id() const
|
||||
{
|
||||
return txn_.key ();
|
||||
return tx_.key ();
|
||||
}
|
||||
|
||||
SHAMapItem const& txn() const
|
||||
{
|
||||
return txn_;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
SHAMapItem const txn_;
|
||||
//! The SHAMapItem that represents the transaction.
|
||||
SHAMapItem const tx_;
|
||||
};
|
||||
|
||||
class RCLTxSet;
|
||||
/** Represents a set of transactions in RCLConsensus.
|
||||
|
||||
class MutableRCLTxSet
|
||||
{
|
||||
public:
|
||||
|
||||
MutableRCLTxSet (RCLTxSet const&);
|
||||
|
||||
bool
|
||||
addEntry (RCLCxTx const& p)
|
||||
{
|
||||
return map_->addItem (
|
||||
SHAMapItem {p.getID(), p.txn().peekData()},
|
||||
true, false);
|
||||
}
|
||||
|
||||
bool
|
||||
removeEntry (uint256 const& entry)
|
||||
{
|
||||
return map_->delItem (entry);
|
||||
}
|
||||
|
||||
std::shared_ptr <SHAMap> const& map() const
|
||||
{
|
||||
return map_;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr <SHAMap> map_;
|
||||
};
|
||||
|
||||
// Sets of transactions
|
||||
// as seen by the consensus code in the rippled app
|
||||
RCLTxSet is a thin wrapper over a SHAMap that stores the set of
|
||||
transactions.
|
||||
*/
|
||||
class RCLTxSet
|
||||
{
|
||||
public:
|
||||
//! Unique identifier/hash of the set of transactions
|
||||
using ID = uint256;
|
||||
//! The type that corresponds to a single transaction
|
||||
using Tx = RCLCxTx;
|
||||
|
||||
using mutable_t = MutableRCLTxSet;
|
||||
|
||||
RCLTxSet (std::shared_ptr<SHAMap> map) :
|
||||
map_ (std::move(map))
|
||||
//< Provide a mutable view of a TxSet
|
||||
class MutableTxSet
|
||||
{
|
||||
assert (map_);
|
||||
friend class RCLTxSet;
|
||||
//! The SHAMap representing the transactions.
|
||||
std::shared_ptr <SHAMap> map_;
|
||||
|
||||
public:
|
||||
MutableTxSet(RCLTxSet const & src)
|
||||
: map_{ src.map_->snapShot(true) }
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/** Insert a new transaction into the set.
|
||||
|
||||
@param t The transaction to insert.
|
||||
@return Whether the transaction took place.
|
||||
*/
|
||||
bool
|
||||
insert(Tx const& t)
|
||||
{
|
||||
return map_->addItem(
|
||||
SHAMapItem{ t.id(), t.tx_.peekData() },
|
||||
true, false);
|
||||
}
|
||||
|
||||
/** Remove a transaction from the set.
|
||||
|
||||
@param entry The ID of the transaction to remove.
|
||||
@return Whether the transaction was removed.
|
||||
*/
|
||||
bool
|
||||
erase(Tx::ID const& entry)
|
||||
{
|
||||
return map_->delItem(entry);
|
||||
}
|
||||
};
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param m SHAMap to wrap
|
||||
*/
|
||||
RCLTxSet (std::shared_ptr<SHAMap> m)
|
||||
: map_{ std::move(m) }
|
||||
{
|
||||
assert(map_);
|
||||
}
|
||||
|
||||
RCLTxSet (MutableRCLTxSet const& set) :
|
||||
map_ (set.map()->snapShot (false))
|
||||
{ }
|
||||
/** Constructor from a previosly created MutableTxSet
|
||||
|
||||
bool hasEntry (uint256 const& entry) const
|
||||
@param m MutableTxSet that will become fixed
|
||||
*/
|
||||
RCLTxSet(MutableTxSet const & m)
|
||||
: map_{m.map_->snapShot(false)}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/** Test if a transaction is in the set.
|
||||
|
||||
@param entry The ID of transaction to test.
|
||||
@return Whether the transaction is in the set.
|
||||
*/
|
||||
bool
|
||||
exists(Tx::ID const& entry) const
|
||||
{
|
||||
return map_->hasItem (entry);
|
||||
}
|
||||
|
||||
boost::optional <RCLCxTx const>
|
||||
getEntry (uint256 const& entry) const
|
||||
/** Lookup a transaction.
|
||||
|
||||
@param entry The ID of the transaction to find.
|
||||
@return A shared pointer to the SHAMapItem.
|
||||
|
||||
@note Since find may not succeed, this returns a
|
||||
`std::shared_ptr<const SHAMapItem>` rather than a Tx, which
|
||||
cannot refer to a missing transaction. The generic consensus
|
||||
code use the shared_ptr semantics to know whether the find
|
||||
was succesfully and properly creates a Tx as needed.
|
||||
*/
|
||||
std::shared_ptr<const SHAMapItem> const &
|
||||
find(Tx::ID const& entry) const
|
||||
{
|
||||
auto item = map_->peekItem (entry);
|
||||
if (item)
|
||||
return RCLCxTx(*item);
|
||||
return boost::none;
|
||||
return map_->peekItem (entry);
|
||||
}
|
||||
|
||||
uint256 getID() const
|
||||
//! The unique ID/hash of the transaction set
|
||||
ID
|
||||
id() const
|
||||
{
|
||||
return map_->getHash().as_uint256();
|
||||
}
|
||||
|
||||
std::map <uint256, bool>
|
||||
getDifferences (RCLTxSet const& j) const
|
||||
/** Find transactions not in common between this and another transaction set.
|
||||
|
||||
@param j The set to compare with
|
||||
@return Map of transactions in this set and `j` but not both. The key
|
||||
is the transaction ID and the value is a bool of the transaction
|
||||
exists in this set.
|
||||
*/
|
||||
std::map<Tx::ID, bool>
|
||||
compare (RCLTxSet const& j) const
|
||||
{
|
||||
SHAMap::Delta delta;
|
||||
|
||||
// Bound the work we do in case of a malicious
|
||||
// map from a trusted validator
|
||||
// map_ from a trusted validator
|
||||
map_->compare (*(j.map_), delta, 65536);
|
||||
|
||||
std::map <uint256, bool> ret;
|
||||
@@ -138,19 +190,9 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<SHAMap> const& map() const
|
||||
{
|
||||
return map_;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//! The SHAMap representing the transactions.
|
||||
std::shared_ptr <SHAMap> map_;
|
||||
};
|
||||
|
||||
inline MutableRCLTxSet::MutableRCLTxSet (RCLTxSet const& set)
|
||||
: map_ (set.map()->snapShot (true))
|
||||
{ }
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,115 +1,13 @@
|
||||
# Consensus Algorithm
|
||||
# RCL Consensus
|
||||
|
||||
This directory holds the types and classes needed
|
||||
to connect consensus to rippled.
|
||||
to connect the generic consensus algorithm to the
|
||||
rippled-specific instance of consensus.
|
||||
|
||||
## Types
|
||||
* `RCLCxTx` adapts a `SHAMapItem` transaction.
|
||||
* `RCLCxTxSet` adapts a `SHAMap` to represent a set of transactions.
|
||||
* `RCLCxLedger` adapts a `Ledger`.
|
||||
* `RCLConsensus` is implements the requirements of the generic
|
||||
`Consensus` class by connecting to the rest of the `rippled`
|
||||
application.
|
||||
|
||||
All types must be copy constructible and assignable.
|
||||
|
||||
* `LgrID_t`
|
||||
Represents a ledger identifier.
|
||||
Typically a 256-bit hash of the ledger header.
|
||||
|
||||
* `TxID_t`
|
||||
Represents a transaction identifier.
|
||||
Typically a 256-bit hash of the transaction data.
|
||||
|
||||
* `TxSetID_t`
|
||||
Represents an identifier of a set of transactions.
|
||||
Typically a 256-bit hash of the set's root tree node.
|
||||
|
||||
* `NodeID_t`
|
||||
Represents an identifier for a node that can take positions during
|
||||
the consenus process.
|
||||
|
||||
* `Time_t`
|
||||
Encodes absolute times. Used for the close times of ledgers and the
|
||||
expiration times of positions.
|
||||
|
||||
* `Pos_t`
|
||||
Represents a position on a consensus taken by a participant.
|
||||
Typically it encodes the previous ledger identifier, the transaction
|
||||
set identifier, the participant, and a sequence number. It also includes
|
||||
either the time it was signed or the time it was first seen. It may also
|
||||
include additional information such as the participant's public key or
|
||||
signature
|
||||
|
||||
* `Tx_t`
|
||||
Represent a transaction. Has an identifier and also whatever information
|
||||
is needed to add it to a set.
|
||||
|
||||
* `TxSet_t`
|
||||
Represents a set of transactions. It has an identifier and can report
|
||||
which transactions it has and provide the actual transaction data.
|
||||
If non-const, it can be modified.
|
||||
|
||||
## `Pos_t`
|
||||
|
||||
Represents a position taken by a validator during a consensus round.
|
||||
Must provide:
|
||||
|
||||
static std::uint32_t seqInitial;
|
||||
|
||||
static std::uint32_t seqLeave;
|
||||
|
||||
std::uint32_t getSequence() const;
|
||||
|
||||
Time_t getCloseTime() const;
|
||||
|
||||
Time_t getSeenTime() const;
|
||||
|
||||
bool isStale (Time_t) const;
|
||||
|
||||
NodeID_t getNodeID() const;
|
||||
|
||||
TxSetID_t getPosition() const;
|
||||
|
||||
LgrID_t getPrevLedger() const;
|
||||
|
||||
bool isInitial() const;
|
||||
|
||||
bool isBowOut() const;
|
||||
|
||||
Json::Value getJson() const;
|
||||
|
||||
bool changePosition (TxSetID_t const& position, Time_t closeTime, Time_t now);
|
||||
|
||||
bool bowOut (Time_t now);
|
||||
|
||||
|
||||
### `Tx_t`
|
||||
|
||||
Represents a transaction.
|
||||
Must provide:
|
||||
|
||||
TxID_t getID() const;
|
||||
|
||||
|
||||
### TxSet_t
|
||||
|
||||
Represents a set of transactions.
|
||||
Must provide:
|
||||
|
||||
TxSet_t (TxSet_t::mutable_t const&);
|
||||
|
||||
TxSetID_t getID() const;
|
||||
|
||||
bool hasEntry (TxID_t const&) const;
|
||||
|
||||
bool hasEntry (Tx_t const&) const;
|
||||
|
||||
boost::optional <Tx_t const> const getEntry (TxID_t const&) const;
|
||||
|
||||
std::map <TxID_t, bool> getDifferences(TxSet_t const&) const;
|
||||
|
||||
## TxSet_t::mutable_t
|
||||
|
||||
Represents a set of transactions that can be modified.
|
||||
Must provide:
|
||||
|
||||
TxSet_t::mutable_t (TxSet_t const &);
|
||||
|
||||
bool addEntry (Tx_t const&);
|
||||
|
||||
bool removeEntry (TxID_t const&);
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_LEDGER_CONSENSUS_H_INCLUDED
|
||||
#define RIPPLE_APP_LEDGER_CONSENSUS_H_INCLUDED
|
||||
|
||||
#include <ripple/app/ledger/LedgerConsensus.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/InboundTransactions.h>
|
||||
#include <ripple/app/consensus/RCLCxTraits.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/Config.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class LocalTxs;
|
||||
|
||||
/** Implements the consensus process and provides inter-round state. */
|
||||
class Consensus
|
||||
{
|
||||
public:
|
||||
using Proposals = hash_map <NodeID, std::deque<LedgerProposal::pointer>>;
|
||||
|
||||
virtual
|
||||
~Consensus () = default;
|
||||
|
||||
/** Returns whether we are issuing proposals currently. */
|
||||
virtual
|
||||
bool
|
||||
isProposing () const = 0;
|
||||
|
||||
/** Returns whether we are issuing validations currently. */
|
||||
virtual
|
||||
bool
|
||||
isValidating () const = 0;
|
||||
|
||||
/** Returns the number of unique proposers we observed for the LCL. */
|
||||
virtual
|
||||
int
|
||||
getLastCloseProposers () const = 0;
|
||||
|
||||
/** Returns the time (in milliseconds) that the last close took. */
|
||||
virtual
|
||||
std::chrono::milliseconds
|
||||
getLastCloseDuration () const = 0;
|
||||
|
||||
/** Called to create a LedgerConsensus instance */
|
||||
virtual
|
||||
std::shared_ptr<LedgerConsensus<RCLCxTraits>>
|
||||
makeLedgerConsensus (
|
||||
Application& app,
|
||||
InboundTransactions& inboundTransactions,
|
||||
LedgerMaster& ledgerMaster,
|
||||
LocalTxs& localTxs) = 0;
|
||||
|
||||
/** Called when a new round of consensus is about to begin */
|
||||
virtual
|
||||
void
|
||||
startRound (
|
||||
LedgerConsensus<RCLCxTraits>& consensus,
|
||||
LedgerHash const &prevLCLHash,
|
||||
std::shared_ptr<Ledger const> const& previousLedger,
|
||||
NetClock::time_point closeTime) = 0;
|
||||
|
||||
/** Specified the network time when the last ledger closed */
|
||||
virtual
|
||||
void
|
||||
setLastCloseTime (NetClock::time_point t) = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
storeProposal (
|
||||
LedgerProposal::ref proposal,
|
||||
NodeID const& nodeID) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<Consensus>
|
||||
make_Consensus (Config const& config, Logs& logs);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <ripple/app/ledger/AcceptedLedger.h>
|
||||
#include <ripple/app/ledger/InboundLedgers.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/LedgerTiming.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/app/ledger/LedgerToJson.h>
|
||||
#include <ripple/app/ledger/OrderBookDB.h>
|
||||
#include <ripple/app/ledger/PendingSaves.h>
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_LEDGER_LEDGERCONSENSUS_H_INCLUDED
|
||||
#define RIPPLE_APP_LEDGER_LEDGERCONSENSUS_H_INCLUDED
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/app/ledger/LedgerProposal.h>
|
||||
#include <ripple/app/ledger/InboundTransactions.h>
|
||||
#include <ripple/app/consensus/RCLCxTraits.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||
#include <ripple/app/misc/FeeVote.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/overlay/Peer.h>
|
||||
#include <ripple/protocol/RippleLedgerHash.h>
|
||||
#include <chrono>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Manager for achieving consensus on the next ledger.
|
||||
*/
|
||||
template <class Traits>
|
||||
class LedgerConsensus : public Traits
|
||||
{
|
||||
public:
|
||||
|
||||
using typename Traits::Time_t;
|
||||
using typename Traits::Pos_t;
|
||||
using typename Traits::TxSet_t;
|
||||
using typename Traits::Tx_t;
|
||||
using typename Traits::LgrID_t;
|
||||
using typename Traits::TxID_t;
|
||||
using typename Traits::TxSetID_t;
|
||||
using typename Traits::NodeID_t;
|
||||
|
||||
virtual ~LedgerConsensus() = default;
|
||||
|
||||
virtual Json::Value getJson (bool full) = 0;
|
||||
|
||||
virtual LgrID_t getLCL () = 0;
|
||||
|
||||
virtual void gotMap (TxSet_t const& map) = 0;
|
||||
|
||||
virtual void timerEntry () = 0;
|
||||
|
||||
virtual bool peerPosition (Pos_t const& position) = 0;
|
||||
|
||||
virtual PublicKey const& getValidationPublicKey () const = 0;
|
||||
|
||||
virtual void setValidationKeys (
|
||||
SecretKey const& valSecret, PublicKey const& valPublic) = 0;
|
||||
|
||||
virtual void startRound (
|
||||
LgrID_t const& prevLCLHash,
|
||||
std::shared_ptr<Ledger const> const& prevLedger,
|
||||
Time_t closeTime,
|
||||
int previousProposers,
|
||||
std::chrono::milliseconds previousConvergeTime) = 0;
|
||||
|
||||
/** Simulate the consensus process without any network traffic.
|
||||
|
||||
The end result, is that consensus begins and completes as if everyone
|
||||
had agreed with whatever we propose.
|
||||
|
||||
This function is only called from the rpc "ledger_accept" path with the
|
||||
server in standalone mode and SHOULD NOT be used during the normal
|
||||
consensus process.
|
||||
*/
|
||||
virtual void simulate (
|
||||
boost::optional<std::chrono::milliseconds> consensusDelay) = 0;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
@@ -1,183 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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 <ripple/app/ledger/LedgerTiming.h>
|
||||
#include <ripple/app/ledger/impl/ConsensusImp.h>
|
||||
#include <ripple/app/ledger/impl/LedgerConsensusImp.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
ConsensusImp::ConsensusImp (
|
||||
FeeVote::Setup const& voteSetup,
|
||||
Logs& logs)
|
||||
: journal_ (logs.journal("Consensus"))
|
||||
, feeVote_ (make_FeeVote (voteSetup,
|
||||
logs.journal("FeeVote")))
|
||||
, proposing_ (false)
|
||||
, validating_ (false)
|
||||
, lastCloseProposers_ (0)
|
||||
, lastCloseConvergeTook_ (LEDGER_IDLE_INTERVAL)
|
||||
, lastValidationTimestamp_ (0s)
|
||||
, lastCloseTime_ (0s)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ConsensusImp::isProposing () const
|
||||
{
|
||||
return proposing_;
|
||||
}
|
||||
|
||||
bool
|
||||
ConsensusImp::isValidating () const
|
||||
{
|
||||
return validating_;
|
||||
}
|
||||
|
||||
int
|
||||
ConsensusImp::getLastCloseProposers () const
|
||||
{
|
||||
return lastCloseProposers_;
|
||||
}
|
||||
|
||||
std::chrono::milliseconds
|
||||
ConsensusImp::getLastCloseDuration () const
|
||||
{
|
||||
return lastCloseConvergeTook_;
|
||||
}
|
||||
|
||||
std::shared_ptr<LedgerConsensus<RCLCxTraits>>
|
||||
ConsensusImp::makeLedgerConsensus (
|
||||
Application& app,
|
||||
InboundTransactions& inboundTransactions,
|
||||
LedgerMaster& ledgerMaster,
|
||||
LocalTxs& localTxs)
|
||||
{
|
||||
return make_LedgerConsensus (app, *this,
|
||||
inboundTransactions, localTxs, ledgerMaster, *feeVote_);
|
||||
}
|
||||
|
||||
void
|
||||
ConsensusImp::startRound (
|
||||
LedgerConsensus<RCLCxTraits>& consensus,
|
||||
LedgerHash const &prevLCLHash,
|
||||
std::shared_ptr<Ledger const> const& previousLedger,
|
||||
NetClock::time_point closeTime)
|
||||
{
|
||||
consensus.startRound (
|
||||
prevLCLHash,
|
||||
previousLedger,
|
||||
closeTime,
|
||||
lastCloseProposers_,
|
||||
lastCloseConvergeTook_);
|
||||
}
|
||||
|
||||
void
|
||||
ConsensusImp::setProposing (bool p, bool v)
|
||||
{
|
||||
proposing_ = p;
|
||||
validating_ = v;
|
||||
}
|
||||
|
||||
STValidation::ref
|
||||
ConsensusImp::getLastValidation () const
|
||||
{
|
||||
return lastValidation_;
|
||||
}
|
||||
|
||||
void
|
||||
ConsensusImp::setLastValidation (STValidation::ref v)
|
||||
{
|
||||
lastValidation_ = v;
|
||||
}
|
||||
|
||||
void
|
||||
ConsensusImp::newLCL (
|
||||
int proposers,
|
||||
std::chrono::milliseconds convergeTime)
|
||||
{
|
||||
lastCloseProposers_ = proposers;
|
||||
lastCloseConvergeTook_ = convergeTime;
|
||||
}
|
||||
|
||||
NetClock::time_point
|
||||
ConsensusImp::validationTimestamp (NetClock::time_point vt)
|
||||
{
|
||||
if (vt <= lastValidationTimestamp_)
|
||||
vt = lastValidationTimestamp_ + 1s;
|
||||
|
||||
lastValidationTimestamp_ = vt;
|
||||
return vt;
|
||||
}
|
||||
|
||||
NetClock::time_point
|
||||
ConsensusImp::getLastCloseTime () const
|
||||
{
|
||||
return lastCloseTime_;
|
||||
}
|
||||
|
||||
void
|
||||
ConsensusImp::setLastCloseTime (NetClock::time_point t)
|
||||
{
|
||||
lastCloseTime_ = t;
|
||||
}
|
||||
|
||||
void
|
||||
ConsensusImp::storeProposal (
|
||||
LedgerProposal::ref proposal,
|
||||
NodeID const& nodeID)
|
||||
{
|
||||
std::lock_guard <std::mutex> _(lock_);
|
||||
|
||||
auto& props = storedProposals_[nodeID];
|
||||
|
||||
if (props.size () >= 10)
|
||||
props.pop_front ();
|
||||
|
||||
props.push_back (proposal);
|
||||
}
|
||||
|
||||
std::vector <RCLCxPos>
|
||||
ConsensusImp::getStoredProposals (uint256 const& prevLedger)
|
||||
{
|
||||
|
||||
std::vector <RCLCxPos> ret;
|
||||
|
||||
{
|
||||
std::lock_guard <std::mutex> _(lock_);
|
||||
|
||||
for (auto const& it : storedProposals_)
|
||||
for (auto const& prop : it.second)
|
||||
if (prop->getPrevLedger() == prevLedger)
|
||||
ret.emplace_back (*prop);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::unique_ptr <Consensus>
|
||||
make_Consensus (Config const& config, Logs& logs)
|
||||
{
|
||||
return std::make_unique<ConsensusImp> (
|
||||
setup_FeeVote (config.section ("voting")),
|
||||
logs);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_LEDGER_IMPL_CONSENSUSIMP_H_INCLUDED
|
||||
#define RIPPLE_APP_LEDGER_IMPL_CONSENSUSIMP_H_INCLUDED
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/ledger/Consensus.h>
|
||||
#include <ripple/app/ledger/LedgerConsensus.h>
|
||||
#include <ripple/app/misc/FeeVote.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/protocol/STValidation.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Implements the consensus process and provides inter-round state. */
|
||||
class ConsensusImp
|
||||
: public Consensus
|
||||
{
|
||||
public:
|
||||
ConsensusImp (FeeVote::Setup const& voteSetup, Logs& logs);
|
||||
|
||||
~ConsensusImp () = default;
|
||||
|
||||
bool
|
||||
isProposing () const override;
|
||||
|
||||
bool
|
||||
isValidating () const override;
|
||||
|
||||
int
|
||||
getLastCloseProposers () const override;
|
||||
|
||||
std::chrono::milliseconds
|
||||
getLastCloseDuration () const override;
|
||||
|
||||
std::shared_ptr<LedgerConsensus<RCLCxTraits>>
|
||||
makeLedgerConsensus (
|
||||
Application& app,
|
||||
InboundTransactions& inboundTransactions,
|
||||
LedgerMaster& ledgerMaster,
|
||||
LocalTxs& localTxs) override;
|
||||
|
||||
void
|
||||
startRound (
|
||||
LedgerConsensus<RCLCxTraits>& ledgerConsensus,
|
||||
LedgerHash const& prevLCLHash,
|
||||
std::shared_ptr<Ledger const> const& previousLedger,
|
||||
NetClock::time_point closeTime) override;
|
||||
|
||||
void
|
||||
setLastCloseTime (NetClock::time_point t) override;
|
||||
|
||||
void
|
||||
storeProposal (
|
||||
LedgerProposal::ref proposal,
|
||||
NodeID const& nodeID) override;
|
||||
|
||||
void
|
||||
setProposing (bool p, bool v);
|
||||
|
||||
STValidation::ref
|
||||
getLastValidation () const;
|
||||
|
||||
void
|
||||
setLastValidation (STValidation::ref v);
|
||||
|
||||
void
|
||||
newLCL (
|
||||
int proposers,
|
||||
std::chrono::milliseconds convergeTime);
|
||||
|
||||
NetClock::time_point
|
||||
validationTimestamp (NetClock::time_point vt);
|
||||
|
||||
NetClock::time_point
|
||||
getLastCloseTime () const;
|
||||
|
||||
std::vector <RCLCxPos>
|
||||
getStoredProposals (uint256 const& previousLedger);
|
||||
|
||||
private:
|
||||
beast::Journal journal_;
|
||||
std::unique_ptr <FeeVote> feeVote_;
|
||||
|
||||
bool proposing_;
|
||||
bool validating_;
|
||||
|
||||
// A pointer to the last validation that we issued
|
||||
STValidation::pointer lastValidation_;
|
||||
|
||||
// The number of proposers who participated in the last ledger close
|
||||
int lastCloseProposers_;
|
||||
|
||||
// How long the last ledger close took, in milliseconds
|
||||
std::chrono::milliseconds lastCloseConvergeTook_;
|
||||
|
||||
// The timestamp of the last validation we used, in network time. This is
|
||||
// only used for our own validations.
|
||||
NetClock::time_point lastValidationTimestamp_;
|
||||
|
||||
// The last close time
|
||||
NetClock::time_point lastCloseTime_;
|
||||
|
||||
Consensus::Proposals storedProposals_;
|
||||
|
||||
// lock to protect storedProposals_
|
||||
std::mutex lock_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,402 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_LEDGER_IMPL_LEDGERCONSENSUSIMP_H_INCLUDED
|
||||
#define RIPPLE_APP_LEDGER_IMPL_LEDGERCONSENSUSIMP_H_INCLUDED
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/impl/ConsensusImp.h>
|
||||
#include <ripple/app/ledger/impl/DisputedTx.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||
#include <ripple/app/misc/FeeVote.h>
|
||||
#include <ripple/basics/CountedObject.h>
|
||||
#include <ripple/protocol/STValidation.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/**
|
||||
Provides the implementation for LedgerConsensus.
|
||||
|
||||
Achieves consensus on the next ledger.
|
||||
|
||||
Two things need consensus:
|
||||
1. The set of transactions.
|
||||
2. The close time for the ledger.
|
||||
*/
|
||||
template <class Traits>
|
||||
class LedgerConsensusImp
|
||||
: public LedgerConsensus<Traits>
|
||||
, public std::enable_shared_from_this <LedgerConsensusImp<Traits>>
|
||||
, public CountedObject <LedgerConsensusImp<Traits>>
|
||||
{
|
||||
private:
|
||||
enum class State
|
||||
{
|
||||
// We haven't closed our ledger yet, but others might have
|
||||
open,
|
||||
|
||||
// Establishing consensus
|
||||
establish,
|
||||
|
||||
// We have closed on a transaction set and are
|
||||
// processing the new ledger
|
||||
processing,
|
||||
|
||||
// We have accepted / validated a new last closed ledger
|
||||
// and need to start a new round
|
||||
accepted,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
using typename Traits::Time_t;
|
||||
using typename Traits::Pos_t;
|
||||
using typename Traits::TxSet_t;
|
||||
using typename Traits::Tx_t;
|
||||
using typename Traits::LgrID_t;
|
||||
using typename Traits::TxID_t;
|
||||
using typename Traits::TxSetID_t;
|
||||
using typename Traits::NodeID_t;
|
||||
using Dispute_t = DisputedTx <Traits>;
|
||||
|
||||
/**
|
||||
* The result of applying a transaction to a ledger.
|
||||
*/
|
||||
enum {resultSuccess, resultFail, resultRetry};
|
||||
|
||||
static char const* getCountedObjectName () { return "LedgerConsensus"; }
|
||||
|
||||
LedgerConsensusImp(LedgerConsensusImp const&) = delete;
|
||||
LedgerConsensusImp& operator=(LedgerConsensusImp const&) = delete;
|
||||
|
||||
~LedgerConsensusImp () = default;
|
||||
|
||||
|
||||
/**
|
||||
@param localtx transactions issued by local clients
|
||||
@param inboundTransactions set of inbound transaction sets
|
||||
@param localtx A set of local transactions to apply
|
||||
@param feeVote Our desired fee levels and voting logic.
|
||||
*/
|
||||
LedgerConsensusImp (
|
||||
Application& app,
|
||||
ConsensusImp& consensus,
|
||||
InboundTransactions& inboundTransactions,
|
||||
LocalTxs& localtx,
|
||||
LedgerMaster& ledgerMaster,
|
||||
FeeVote& feeVote);
|
||||
|
||||
/**
|
||||
@param prevLCLHash The hash of the Last Closed Ledger (LCL).
|
||||
@param previousLedger Best guess of what the LCL was.
|
||||
@param closeTime Closing time point of the LCL.
|
||||
@param previousProposers the number of participants in the last round
|
||||
@param previousConvergeTime how long the last round took (ms)
|
||||
*/
|
||||
void startRound (
|
||||
LgrID_t const& prevLCLHash,
|
||||
std::shared_ptr<Ledger const> const& prevLedger,
|
||||
Time_t closeTime,
|
||||
int previousProposers,
|
||||
std::chrono::milliseconds previousConvergeTime) override;
|
||||
|
||||
/**
|
||||
Get the Json state of the consensus process.
|
||||
Called by the consensus_info RPC.
|
||||
|
||||
@param full True if verbose response desired.
|
||||
@return The Json state.
|
||||
*/
|
||||
Json::Value getJson (bool full) override;
|
||||
|
||||
/* The hash of the last closed ledger */
|
||||
LgrID_t getLCL () override;
|
||||
|
||||
/**
|
||||
We have a complete transaction set, typically acquired from the network
|
||||
|
||||
@param map the transaction set.
|
||||
*/
|
||||
void gotMap (TxSet_t const& map) override;
|
||||
|
||||
/**
|
||||
On timer call the correct handler for each state.
|
||||
*/
|
||||
void timerEntry () override;
|
||||
|
||||
/**
|
||||
A server has taken a new position, adjust our tracking
|
||||
Called when a peer takes a new postion.
|
||||
|
||||
@param newPosition the new position
|
||||
@return true if we should do delayed relay of this position.
|
||||
*/
|
||||
bool peerPosition (Pos_t const& newPosition) override;
|
||||
|
||||
void simulate(
|
||||
boost::optional<std::chrono::milliseconds> consensusDelay) override;
|
||||
|
||||
/**
|
||||
Put a transaction set where peers can find it
|
||||
*/
|
||||
void shareSet (TxSet_t const&);
|
||||
|
||||
private:
|
||||
/**
|
||||
Handle pre-close state.
|
||||
*/
|
||||
void statePreClose ();
|
||||
|
||||
/** We are establishing a consensus
|
||||
Update our position only on the timer, and in this state.
|
||||
If we have consensus, move to the finish state
|
||||
*/
|
||||
void stateEstablish ();
|
||||
|
||||
/** Check if we've reached consensus */
|
||||
bool haveConsensus ();
|
||||
|
||||
/**
|
||||
Check if our last closed ledger matches the network's.
|
||||
This tells us if we are still in sync with the network.
|
||||
This also helps us if we enter the consensus round with
|
||||
the wrong ledger, to leave it with the correct ledger so
|
||||
that we can participate in the next round.
|
||||
*/
|
||||
void checkLCL ();
|
||||
|
||||
/**
|
||||
Change our view of the last closed ledger
|
||||
|
||||
@param lclHash Hash of the last closed ledger.
|
||||
*/
|
||||
void handleLCL (LgrID_t const& lclHash);
|
||||
|
||||
/**
|
||||
We have a complete transaction set, typically acquired from the network
|
||||
|
||||
@param map the transaction set.
|
||||
@param acquired true if we have acquired the transaction set.
|
||||
*/
|
||||
void mapCompleteInternal (
|
||||
TxSet_t const& map,
|
||||
bool acquired);
|
||||
|
||||
/** We have a new last closed ledger, process it. Final accept logic
|
||||
|
||||
@param set Our consensus set
|
||||
*/
|
||||
void accept (TxSet_t const& set);
|
||||
|
||||
/**
|
||||
Compare two proposed transaction sets and create disputed
|
||||
transctions structures for any mismatches
|
||||
|
||||
@param m1 One transaction set
|
||||
@param m2 The other transaction set
|
||||
*/
|
||||
void createDisputes (TxSet_t const& m1,
|
||||
TxSet_t const& m2);
|
||||
|
||||
/**
|
||||
Add a disputed transaction (one that at least one node wants
|
||||
in the consensus set and at least one node does not) to our tracking
|
||||
|
||||
@param tx The disputed transaction
|
||||
*/
|
||||
void addDisputedTransaction (Tx_t const& tx);
|
||||
|
||||
/**
|
||||
Adjust the votes on all disputed transactions based
|
||||
on the set of peers taking this position
|
||||
|
||||
@param map A disputed position
|
||||
@param peers peers which are taking the position map
|
||||
*/
|
||||
void adjustCount (TxSet_t const& map,
|
||||
std::vector<NodeID_t> const& peers);
|
||||
|
||||
/**
|
||||
Revoke our outstanding proposal, if any, and
|
||||
cease proposing at least until this round ends
|
||||
*/
|
||||
void leaveConsensus ();
|
||||
|
||||
/** Make and send a proposal
|
||||
*/
|
||||
void propose ();
|
||||
|
||||
/** Send a node status change message to our directly connected peers
|
||||
|
||||
@param event The event which caused the status change. This is
|
||||
typically neACCEPTED_LEDGER or neCLOSING_LEDGER.
|
||||
@param ledger The ledger associated with the event.
|
||||
*/
|
||||
void statusChange (protocol::NodeEvent event, ReadView const& ledger);
|
||||
|
||||
/** Determine our initial proposed transaction set based on
|
||||
our open ledger
|
||||
*/
|
||||
std::pair <TxSet_t, Pos_t> makeInitialPosition();
|
||||
|
||||
/** Take an initial position on what we think the consensus set should be
|
||||
*/
|
||||
void takeInitialPosition ();
|
||||
|
||||
/**
|
||||
Called while trying to avalanche towards consensus.
|
||||
Adjusts our positions to try to agree with other validators.
|
||||
*/
|
||||
void updateOurPositions ();
|
||||
|
||||
/** If we radically changed our consensus context for some reason,
|
||||
we need to replay recent proposals so that they're not lost.
|
||||
*/
|
||||
void playbackProposals ();
|
||||
|
||||
/** We have just decided to close the ledger. Start the consensus timer,
|
||||
stash the close time, inform peers, and take a position
|
||||
*/
|
||||
void closeLedger ();
|
||||
|
||||
/**
|
||||
If we missed a consensus round, we may be missing a validation.
|
||||
This will send an older owed validation if we previously missed it.
|
||||
*/
|
||||
void checkOurValidation ();
|
||||
|
||||
/** We have a new LCL and must accept it */
|
||||
void beginAccept (bool synchronous);
|
||||
|
||||
void endConsensus (bool correctLCL);
|
||||
|
||||
|
||||
/** Add our load fee to our validation */
|
||||
void addLoad(STValidation::ref val);
|
||||
|
||||
/** Convert an advertised close time to an effective close time */
|
||||
NetClock::time_point effectiveCloseTime(NetClock::time_point closeTime);
|
||||
|
||||
private:
|
||||
Application& app_;
|
||||
ConsensusImp& consensus_;
|
||||
InboundTransactions& inboundTransactions_;
|
||||
LocalTxs& localTX_;
|
||||
LedgerMaster& ledgerMaster_;
|
||||
FeeVote& feeVote_;
|
||||
std::recursive_mutex lock_;
|
||||
|
||||
NodeID_t ourID_;
|
||||
State state_;
|
||||
|
||||
// The wall time this ledger closed
|
||||
Time_t closeTime_;
|
||||
|
||||
LgrID_t prevLedgerHash_;
|
||||
LgrID_t acquiringLedger_;
|
||||
|
||||
std::shared_ptr<Ledger const> previousLedger_;
|
||||
boost::optional<Pos_t> ourPosition_;
|
||||
boost::optional<TxSet_t> ourSet_;
|
||||
PublicKey valPublic_;
|
||||
SecretKey valSecret_;
|
||||
bool proposing_, validating_, haveCorrectLCL_, consensusFail_;
|
||||
|
||||
// How much time has elapsed since the round started
|
||||
std::chrono::milliseconds roundTime_;
|
||||
|
||||
// How long the close has taken, expressed as a percentage of the time that
|
||||
// we expected it to take.
|
||||
int closePercent_;
|
||||
|
||||
NetClock::duration closeResolution_;
|
||||
|
||||
bool haveCloseTimeConsensus_;
|
||||
|
||||
std::chrono::steady_clock::time_point consensusStartTime_;
|
||||
int previousProposers_;
|
||||
|
||||
// Time it took for the last consensus round to converge
|
||||
std::chrono::milliseconds previousRoundTime_;
|
||||
|
||||
// Convergence tracking, trusted peers indexed by hash of public key
|
||||
hash_map<NodeID_t, Pos_t> peerPositions_;
|
||||
|
||||
// Transaction Sets, indexed by hash of transaction tree
|
||||
hash_map<TxSetID_t, const TxSet_t> acquired_;
|
||||
|
||||
// Disputed transactions
|
||||
hash_map<TxID_t, Dispute_t> disputes_;
|
||||
hash_set<TxSetID_t> compares_;
|
||||
|
||||
// Close time estimates, keep ordered for predictable traverse
|
||||
std::map <Time_t, int> closeTimes_;
|
||||
|
||||
// nodes that have bowed out of this consensus process
|
||||
hash_set<NodeID_t> deadNodes_;
|
||||
beast::Journal j_;
|
||||
|
||||
public:
|
||||
/** Returns validation public key */
|
||||
PublicKey const&
|
||||
getValidationPublicKey () const override;
|
||||
|
||||
/** Set validation private and public key pair. */
|
||||
void
|
||||
setValidationKeys (
|
||||
SecretKey const& valSecret, PublicKey const& valPublic) override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::shared_ptr <LedgerConsensus <RCLCxTraits>>
|
||||
make_LedgerConsensus (
|
||||
Application& app,
|
||||
ConsensusImp& consensus,
|
||||
InboundTransactions& inboundTransactions,
|
||||
LocalTxs& localtx,
|
||||
LedgerMaster& ledgerMaster,
|
||||
FeeVote& feeVote);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/** Apply a set of transactions to a ledger
|
||||
|
||||
Typically the txFilter is used to reject transactions
|
||||
that already got in the prior ledger
|
||||
|
||||
@param set set of transactions to apply
|
||||
@param view ledger to apply to
|
||||
@param txFilter callback, return false to reject txn
|
||||
@return retriable transactions
|
||||
*/
|
||||
CanonicalTXSet
|
||||
applyTransactions (
|
||||
Application& app,
|
||||
RCLTxSet const& set,
|
||||
OpenView& view,
|
||||
std::function<bool(uint256 const&)> txFilter);
|
||||
|
||||
extern template class LedgerConsensusImp <RCLCxTraits>;
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
@@ -828,6 +828,8 @@ LedgerMaster::consensusBuilt(
|
||||
if (standalone_)
|
||||
return;
|
||||
|
||||
mLedgerHistory.builtLedger (ledger, std::move (consensus));
|
||||
|
||||
if (ledger->info().seq <= mValidLedgerSeq)
|
||||
{
|
||||
auto stream = app_.journal ("LedgerConsensus").info();
|
||||
@@ -912,8 +914,6 @@ LedgerMaster::consensusBuilt(
|
||||
<< "Consensus triggered check of ledger";
|
||||
checkAccept (maxLedger, maxSeq);
|
||||
}
|
||||
|
||||
mLedgerHistory.builtLedger (ledger, std::move (consensus));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/beast/asio/io_latency_probe.h>
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -1338,7 +1339,6 @@ ApplicationImp::startGenesisLedger()
|
||||
*genesis, timeKeeper().closeTime());
|
||||
next->updateSkipList ();
|
||||
next->setImmutable (*config_);
|
||||
m_networkOPs->setLastCloseTime (next->info().closeTime);
|
||||
openLedger_.emplace(next, cachedSLEs_,
|
||||
logs_->journal("OpenLedger"));
|
||||
m_ledgerMaster->storeLedger(next);
|
||||
@@ -1648,7 +1648,6 @@ bool ApplicationImp::loadOldLedger (
|
||||
m_ledgerMaster->switchLCL (loadLedger);
|
||||
loadLedger->setValidated();
|
||||
m_ledgerMaster->setFullLedger(loadLedger, true, false);
|
||||
m_networkOPs->setLastCloseTime (loadLedger->info().closeTime);
|
||||
openLedger_.emplace(loadLedger, cachedSLEs_,
|
||||
logs_->journal("OpenLedger"));
|
||||
|
||||
|
||||
@@ -21,15 +21,14 @@
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/protocol/Quality.h>
|
||||
#include <ripple/core/DatabaseCon.h>
|
||||
#include <ripple/consensus/Consensus.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/consensus/RCLCxTraits.h>
|
||||
#include <ripple/app/ledger/Consensus.h>
|
||||
#include <ripple/app/ledger/LedgerConsensus.h>
|
||||
#include <ripple/app/consensus/RCLConsensus.h>
|
||||
#include <ripple/app/ledger/AcceptedLedger.h>
|
||||
#include <ripple/app/ledger/InboundLedger.h>
|
||||
#include <ripple/app/ledger/InboundLedgers.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/LedgerTiming.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/app/ledger/LedgerToJson.h>
|
||||
#include <ripple/app/ledger/LocalTxs.h>
|
||||
#include <ripple/app/ledger/OpenLedger.h>
|
||||
@@ -222,9 +221,14 @@ public:
|
||||
, m_amendmentBlocked (false)
|
||||
, m_heartbeatTimer (this)
|
||||
, m_clusterTimer (this)
|
||||
, mConsensus (make_Consensus (app_.config(), app_.logs()))
|
||||
, mLedgerConsensus (mConsensus->makeLedgerConsensus (
|
||||
app, app.getInboundTransactions(), ledgerMaster, *m_localTX))
|
||||
, mConsensus (std::make_shared<RCLConsensus>(app,
|
||||
make_FeeVote(setup_FeeVote (app_.config().section ("voting")),
|
||||
app_.logs().journal("FeeVote")),
|
||||
ledgerMaster,
|
||||
*m_localTX,
|
||||
app.getInboundTransactions(),
|
||||
stopwatch(),
|
||||
app_.logs().journal("LedgerConsensus")))
|
||||
, m_ledgerMaster (ledgerMaster)
|
||||
, m_job_queue (job_queue)
|
||||
, m_standalone (standalone)
|
||||
@@ -308,7 +312,7 @@ public:
|
||||
|
||||
// Ledger proposal/close functions.
|
||||
void processTrustedProposal (
|
||||
LedgerProposal::pointer proposal,
|
||||
RCLCxPeerPos::pointer proposal,
|
||||
std::shared_ptr<protocol::TMProposeSet> set,
|
||||
NodeID const &node) override;
|
||||
|
||||
@@ -372,18 +376,14 @@ public:
|
||||
}
|
||||
void setAmendmentBlocked () override;
|
||||
void consensusViewChange () override;
|
||||
void setLastCloseTime (NetClock::time_point t) override
|
||||
{
|
||||
mConsensus->setLastCloseTime(t);
|
||||
}
|
||||
PublicKey const& getValidationPublicKey () const override
|
||||
{
|
||||
return mLedgerConsensus->getValidationPublicKey ();
|
||||
return mConsensus->getValidationPublicKey ();
|
||||
}
|
||||
void setValidationKeys (
|
||||
SecretKey const& valSecret, PublicKey const& valPublic) override
|
||||
{
|
||||
mLedgerConsensus->setValidationKeys (valSecret, valPublic);
|
||||
mConsensus->setValidationKeys (valSecret, valPublic);
|
||||
}
|
||||
Json::Value getConsensusInfo () override;
|
||||
Json::Value getServerInfo (bool human, bool admin) override;
|
||||
@@ -560,8 +560,7 @@ private:
|
||||
DeadlineTimer m_heartbeatTimer;
|
||||
DeadlineTimer m_clusterTimer;
|
||||
|
||||
std::unique_ptr<Consensus> mConsensus;
|
||||
std::shared_ptr<LedgerConsensus<RCLCxTraits>> mLedgerConsensus;
|
||||
std::shared_ptr<RCLConsensus> mConsensus;
|
||||
|
||||
LedgerMaster& m_ledgerMaster;
|
||||
std::shared_ptr<InboundLedger> mAcquiringLedger;
|
||||
@@ -700,7 +699,8 @@ void NetworkOPsImp::processHeartbeatTimer ()
|
||||
<< "Node count (" << numPeers << ") "
|
||||
<< "has fallen below quorum (" << m_network_quorum << ").";
|
||||
}
|
||||
|
||||
// We do not call mConsensus->timerEntry until there
|
||||
// are enough peers providing meaningful inputs to consensus
|
||||
setHeartbeatTimer ();
|
||||
|
||||
return;
|
||||
@@ -722,7 +722,7 @@ void NetworkOPsImp::processHeartbeatTimer ()
|
||||
|
||||
}
|
||||
|
||||
mLedgerConsensus->timerEntry ();
|
||||
mConsensus->timerEntry (app_.timeKeeper().closeTime());
|
||||
|
||||
setHeartbeatTimer ();
|
||||
}
|
||||
@@ -776,12 +776,12 @@ void NetworkOPsImp::processClusterTimer ()
|
||||
|
||||
std::string NetworkOPsImp::strOperatingMode () const
|
||||
{
|
||||
if (mMode == omFULL)
|
||||
if (mMode == omFULL && mConsensus->haveCorrectLCL())
|
||||
{
|
||||
if (mConsensus->isProposing ())
|
||||
if (mConsensus->proposing ())
|
||||
return "proposing";
|
||||
|
||||
if (mConsensus->isValidating ())
|
||||
if (mConsensus->validating ())
|
||||
return "validating";
|
||||
}
|
||||
|
||||
@@ -1282,8 +1282,7 @@ void NetworkOPsImp::tryStartConsensus ()
|
||||
}
|
||||
}
|
||||
|
||||
if (mMode != omDISCONNECTED)
|
||||
beginConsensus (networkClosed);
|
||||
beginConsensus (networkClosed);
|
||||
}
|
||||
|
||||
bool NetworkOPsImp::checkLastClosedLedger (
|
||||
@@ -1522,10 +1521,9 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
|
||||
app_.getValidations().getCurrentPublicKeys ());
|
||||
|
||||
mConsensus->startRound (
|
||||
*mLedgerConsensus,
|
||||
app_.timeKeeper().closeTime(),
|
||||
networkClosed,
|
||||
prevLedger,
|
||||
closingInfo.closeTime);
|
||||
prevLedger);
|
||||
|
||||
JLOG(m_journal.debug()) << "Initiating consensus engine";
|
||||
return true;
|
||||
@@ -1533,18 +1531,19 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
|
||||
|
||||
uint256 NetworkOPsImp::getConsensusLCL ()
|
||||
{
|
||||
return mLedgerConsensus->getLCL ();
|
||||
return mConsensus->LCL ();
|
||||
}
|
||||
|
||||
void NetworkOPsImp::processTrustedProposal (
|
||||
LedgerProposal::pointer proposal,
|
||||
RCLCxPeerPos::pointer peerPos,
|
||||
std::shared_ptr<protocol::TMProposeSet> set,
|
||||
NodeID const& node)
|
||||
{
|
||||
mConsensus->storeProposal (proposal, node);
|
||||
mConsensus->storeProposal (peerPos, node);
|
||||
|
||||
if (mLedgerConsensus->peerPosition (*proposal))
|
||||
app_.overlay().relay(*set, proposal->getSuppressionID());
|
||||
if (mConsensus->peerProposal (
|
||||
app_.timeKeeper().closeTime(), peerPos->proposal()))
|
||||
app_.overlay().relay(*set, peerPos->getSuppressionID());
|
||||
else
|
||||
JLOG(m_journal.info()) << "Not relaying trusted proposal";
|
||||
}
|
||||
@@ -1567,7 +1566,9 @@ NetworkOPsImp::mapComplete (
|
||||
|
||||
// We acquired it because consensus asked us to
|
||||
if (fromAcquire)
|
||||
mLedgerConsensus->gotMap (RCLTxSet{map});
|
||||
mConsensus->gotTxSet (
|
||||
app_.timeKeeper().closeTime(),
|
||||
RCLTxSet{map});
|
||||
}
|
||||
|
||||
void NetworkOPsImp::endConsensus (bool correctLCL)
|
||||
@@ -1647,7 +1648,7 @@ NetworkOPsImp::ServerFeeSummary::operator !=(NetworkOPsImp::ServerFeeSummary con
|
||||
em.is_initialized() != b.em.is_initialized())
|
||||
return true;
|
||||
|
||||
if(em)
|
||||
if(em && b.em)
|
||||
{
|
||||
return (em->minFeeLevel != b.em->minFeeLevel ||
|
||||
em->expFeeLevel != b.em->expFeeLevel ||
|
||||
@@ -2113,7 +2114,7 @@ bool NetworkOPsImp::recvValidation (
|
||||
|
||||
Json::Value NetworkOPsImp::getConsensusInfo ()
|
||||
{
|
||||
return mLedgerConsensus->getJson (true);
|
||||
return mConsensus->getJson (true);
|
||||
}
|
||||
|
||||
Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
|
||||
@@ -2175,17 +2176,17 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
|
||||
{
|
||||
lastClose[jss::converge_time_s] =
|
||||
std::chrono::duration<double>{
|
||||
mConsensus->getLastCloseDuration()}.count();
|
||||
mConsensus->getLastConvergeDuration()}.count();
|
||||
}
|
||||
else
|
||||
{
|
||||
lastClose[jss::converge_time] =
|
||||
Json::Int (mConsensus->getLastCloseDuration().count());
|
||||
Json::Int (mConsensus->getLastConvergeDuration().count());
|
||||
}
|
||||
|
||||
info[jss::last_close] = lastClose;
|
||||
|
||||
// info[jss::consensus] = mLedgerConsensus->getJson();
|
||||
// info[jss::consensus] = mConsensus->getJson();
|
||||
|
||||
if (admin)
|
||||
info[jss::load] = m_job_queue.getJson ();
|
||||
@@ -2757,9 +2758,11 @@ std::uint32_t NetworkOPsImp::acceptLedger (
|
||||
Throw<std::runtime_error> ("Operation only possible in STANDALONE mode.");
|
||||
|
||||
// FIXME Could we improve on this and remove the need for a specialized
|
||||
// API in LedgerConsensus?
|
||||
// API in Consensus?
|
||||
beginConsensus (m_ledgerMaster.getClosedLedger()->info().hash);
|
||||
mLedgerConsensus->simulate (consensusDelay);
|
||||
mConsensus->simulate (
|
||||
app_.timeKeeper().closeTime(),
|
||||
consensusDelay);
|
||||
return m_ledgerMaster.getCurrentLedger ()->info().seq;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <ripple/core/JobQueue.h>
|
||||
#include <ripple/protocol/STValidation.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/app/ledger/LedgerProposal.h>
|
||||
#include <ripple/app/consensus/RCLCxPeerPos.h>
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <ripple/net/InfoSub.h>
|
||||
#include <memory>
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// ledger proposal/close functions
|
||||
virtual void processTrustedProposal (LedgerProposal::pointer proposal,
|
||||
virtual void processTrustedProposal (RCLCxPeerPos::pointer peerPos,
|
||||
std::shared_ptr<protocol::TMProposeSet> set,
|
||||
NodeID const& node) = 0;
|
||||
|
||||
@@ -174,9 +174,6 @@ public:
|
||||
virtual bool isAmendmentBlocked () = 0;
|
||||
virtual void setAmendmentBlocked () = 0;
|
||||
virtual void consensusViewChange () = 0;
|
||||
|
||||
// FIXME(NIKB): Remove the need for this function
|
||||
virtual void setLastCloseTime (NetClock::time_point t) = 0;
|
||||
virtual PublicKey const& getValidationPublicKey () const = 0;
|
||||
virtual void setValidationKeys (
|
||||
SecretKey const& valSecret, PublicKey const& valPublic) = 0;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <ripple/app/misc/Validations.h>
|
||||
#include <ripple/core/DatabaseCon.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/LedgerTiming.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/app/misc/ValidatorList.h>
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
@@ -324,6 +325,18 @@ template <class Hasher, class Key, class Hash, class Pred, class Alloc>
|
||||
void
|
||||
hash_append(Hasher& h, std::unordered_set<Key, Hash, Pred, Alloc> const& s);
|
||||
|
||||
template <class Hasher, class Key, class Compare, class Alloc>
|
||||
std::enable_if_t
|
||||
<
|
||||
!is_contiguously_hashable<Key, Hasher>::value
|
||||
>
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept;
|
||||
template <class Hasher, class Key, class Compare, class Alloc>
|
||||
std::enable_if_t
|
||||
<
|
||||
is_contiguously_hashable<Key, Hasher>::value
|
||||
>
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept;
|
||||
template <class Hasher, class T0, class T1, class ...T>
|
||||
void
|
||||
hash_append (Hasher& h, T0 const& t0, T1 const& t1, T const& ...t) noexcept;
|
||||
@@ -421,6 +434,25 @@ hash_append(Hasher& h, std::array<T, N> const& a) noexcept
|
||||
hash_append(h, t);
|
||||
}
|
||||
|
||||
template <class Hasher, class Key, class Compare, class Alloc>
|
||||
std::enable_if_t
|
||||
<
|
||||
!is_contiguously_hashable<Key, Hasher>::value
|
||||
>
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
|
||||
{
|
||||
for (auto const& t : v)
|
||||
hash_append(h, t);
|
||||
}
|
||||
template <class Hasher, class Key, class Compare, class Alloc>
|
||||
std::enable_if_t
|
||||
<
|
||||
is_contiguously_hashable<Key, Hasher>::value
|
||||
>
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
|
||||
{
|
||||
h(&(v.begin()), v.size()*sizeof(Key));
|
||||
}
|
||||
// tuple
|
||||
|
||||
namespace detail
|
||||
|
||||
1791
src/ripple/consensus/Consensus.h
Normal file
1791
src/ripple/consensus/Consensus.h
Normal file
File diff suppressed because it is too large
Load Diff
262
src/ripple/consensus/ConsensusProposal.h
Normal file
262
src/ripple/consensus/ConsensusProposal.h
Normal file
@@ -0,0 +1,262 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 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 PROVID_tED "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.
|
||||
*/
|
||||
//==============================================================================
|
||||
#ifndef RIPPLE_CONSENSUS_ConsensusProposal_H_INCLUDED
|
||||
#define RIPPLE_CONSENSUS_ConsensusProposal_H_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
|
||||
namespace ripple
|
||||
{
|
||||
/** Represents a proposed position taken during a round of consensus.
|
||||
|
||||
During consensus, peers seek agreement on a set of transactions to
|
||||
apply to the prior ledger to generate the next ledger. Each peer takes a
|
||||
position on whether to include or exclude potential transactions.
|
||||
The position on the set of transactions is proposed to its peers as an
|
||||
instance of the ConsensusProposal class.
|
||||
|
||||
An instance of ConsensusProposal can be either our own proposal or one of
|
||||
our peer's.
|
||||
|
||||
As consensus proceeds, peers may change their position on the transaction,
|
||||
or choose to abstain. Each successive proposal includes a strictly
|
||||
monotonically increasing number (or, if a peer is choosing to abstain,
|
||||
the special value `seqLeave`).
|
||||
|
||||
Refer to @ref Consensus for requirements of the template arguments.
|
||||
|
||||
@tparam NodeID_t Type used to uniquely identify nodes/peers
|
||||
@tparam LedgerID_t Type used to uniquely identify ledgers
|
||||
@tparam Position_t Type used to represent the position taken on transactions
|
||||
under consideration during this round of consensus
|
||||
*/
|
||||
template <
|
||||
class NodeID_t,
|
||||
class LedgerID_t,
|
||||
class Position_t>
|
||||
class ConsensusProposal
|
||||
{
|
||||
public:
|
||||
using NodeID = NodeID_t;
|
||||
|
||||
//< Sequence value when a peer initially joins consensus
|
||||
static std::uint32_t const seqJoin = 0;
|
||||
|
||||
//< Sequence number when a peer wants to bow out and leave consensus
|
||||
static std::uint32_t const seqLeave = 0xffffffff;
|
||||
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param prevLedger The previous ledger this proposal is building on.
|
||||
@param seq The sequence number of this proposal.
|
||||
@param position The position taken on transactions in this round.
|
||||
@param closeTime Position of when this ledger closed.
|
||||
@param now Time when the proposal was taken.
|
||||
@param nodeID ID of node/peer taking this position.
|
||||
*/
|
||||
ConsensusProposal(
|
||||
LedgerID_t const& prevLedger,
|
||||
std::uint32_t seq,
|
||||
Position_t const& position,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now,
|
||||
NodeID_t const& nodeID)
|
||||
: previousLedger_(prevLedger)
|
||||
, position_(position)
|
||||
, closeTime_(closeTime)
|
||||
, time_(now)
|
||||
, proposeSeq_(seq)
|
||||
, nodeID_(nodeID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//! Identifying which peer took this position.
|
||||
NodeID_t const&
|
||||
nodeID () const
|
||||
{
|
||||
return nodeID_;
|
||||
}
|
||||
|
||||
//! Get the proposed position.
|
||||
Position_t const&
|
||||
position () const
|
||||
{
|
||||
return position_;
|
||||
}
|
||||
|
||||
//! Get the prior accepted ledger this position is based on.
|
||||
LedgerID_t const&
|
||||
prevLedger () const
|
||||
{
|
||||
return previousLedger_;
|
||||
}
|
||||
|
||||
/** Get the sequence number of this proposal
|
||||
|
||||
Starting with an initial sequence number of `seqJoin`, successive
|
||||
proposals from a peer will increase the sequence number.
|
||||
|
||||
@return the sequence number
|
||||
*/
|
||||
std::uint32_t
|
||||
proposeSeq () const
|
||||
{
|
||||
return proposeSeq_;
|
||||
}
|
||||
|
||||
//! The current position on the consensus close time.
|
||||
NetClock::time_point const &
|
||||
closeTime () const
|
||||
{
|
||||
return closeTime_;
|
||||
}
|
||||
|
||||
//! Get when this position was taken.
|
||||
NetClock::time_point const &
|
||||
seenTime () const
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
|
||||
/** Whether this is the first position taken during the current
|
||||
consensus round.
|
||||
*/
|
||||
bool
|
||||
isInitial () const
|
||||
{
|
||||
return proposeSeq_ == seqJoin;
|
||||
}
|
||||
|
||||
//! Get whether this node left the consensus process
|
||||
bool
|
||||
isBowOut () const
|
||||
{
|
||||
return proposeSeq_ == seqLeave;
|
||||
}
|
||||
|
||||
//! Get whether this position is stale relative to the provided cutoff
|
||||
bool
|
||||
isStale (NetClock::time_point cutoff) const
|
||||
{
|
||||
return time_ <= cutoff;
|
||||
}
|
||||
|
||||
/** Update the position during the consensus process. This will increment
|
||||
the proposal's sequence number.
|
||||
|
||||
@param newPosition The new position taken.
|
||||
@param newCloseTime The new close time.
|
||||
@param now the time The new position was taken.
|
||||
|
||||
@return `true` if the position was updated or `false` if this node has
|
||||
already left this consensus round.
|
||||
*/
|
||||
bool
|
||||
changePosition(
|
||||
Position_t const& newPosition,
|
||||
NetClock::time_point newCloseTime,
|
||||
NetClock::time_point now)
|
||||
{
|
||||
if (proposeSeq_ == seqLeave)
|
||||
return false;
|
||||
|
||||
position_ = newPosition;
|
||||
closeTime_ = newCloseTime;
|
||||
time_ = now;
|
||||
++proposeSeq_;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Leave consensus
|
||||
|
||||
Update position to indicate the node left consensus.
|
||||
|
||||
@param now Time when this node left consensus.
|
||||
*/
|
||||
void
|
||||
bowOut(NetClock::time_point now)
|
||||
{
|
||||
time_ = now;
|
||||
proposeSeq_ = seqLeave;
|
||||
}
|
||||
|
||||
//! Get JSON representation for debugging
|
||||
Json::Value
|
||||
getJson () const
|
||||
{
|
||||
using std::to_string;
|
||||
|
||||
Json::Value ret = Json::objectValue;
|
||||
ret[jss::previous_ledger] = to_string (prevLedger());
|
||||
|
||||
if (!isBowOut())
|
||||
{
|
||||
ret[jss::transaction_hash] = to_string (position());
|
||||
ret[jss::propose_seq] = proposeSeq();
|
||||
}
|
||||
|
||||
ret[jss::close_time] = to_string(closeTime().time_since_epoch().count());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//! Unique identifier of prior ledger this proposal is based on
|
||||
LedgerID_t previousLedger_;
|
||||
|
||||
//! Unique identifier of the position this proposal is taking
|
||||
Position_t position_;
|
||||
|
||||
//! The ledger close time this position is taking
|
||||
NetClock::time_point closeTime_;
|
||||
|
||||
// !The time this position was last updated
|
||||
NetClock::time_point time_;
|
||||
|
||||
//! The sequence number of these positions taken by this node
|
||||
std::uint32_t proposeSeq_;
|
||||
|
||||
//! The identifier of the node taking this position
|
||||
NodeID_t nodeID_;
|
||||
|
||||
};
|
||||
|
||||
template <class NodeID_t,
|
||||
class LedgerID_t,
|
||||
class Position_t>
|
||||
bool
|
||||
operator==(ConsensusProposal<NodeID_t, LedgerID_t, Position_t> const & a,
|
||||
ConsensusProposal<NodeID_t, LedgerID_t, Position_t> const & b)
|
||||
{
|
||||
return a.nodeID() == b.nodeID() &&
|
||||
a.proposeSeq() == b.proposeSeq() &&
|
||||
a.prevLedger() == b.prevLedger() &&
|
||||
a.position() == b.position() &&
|
||||
a.closeTime() == b.closeTime() &&
|
||||
a.seenTime() == b.seenTime();
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
@@ -35,70 +37,110 @@ namespace ripple {
|
||||
the dispute.
|
||||
|
||||
Undisputed transactions have no corresponding @ref DisputedTx object.
|
||||
|
||||
Refer to @ref Consensus for details on the template type requirements.
|
||||
|
||||
@tparam Tx_t The type for a transaction
|
||||
@tparam NodeID_t The type for a node identifier
|
||||
*/
|
||||
|
||||
template <class Traits>
|
||||
template <class Tx_t, class NodeID_t>
|
||||
class DisputedTx
|
||||
{
|
||||
using TxID_t = typename Tx_t::ID;
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
using Tx_t = typename Traits::Tx_t;
|
||||
using TxID_t = typename Traits::TxID_t;
|
||||
using NodeID_t = typename Traits::NodeID_t;
|
||||
|
||||
@param tx The transaction under dispute
|
||||
@param ourVote Our vote on whether tx should be included
|
||||
@param j Journal for debugging
|
||||
*/
|
||||
DisputedTx (Tx_t const& tx,
|
||||
bool ourVote, beast::Journal j)
|
||||
: mTransactionID (tx.getID())
|
||||
, mYays (0)
|
||||
, mNays (0)
|
||||
, mOurVote (ourVote)
|
||||
, transaction (tx)
|
||||
bool ourVote,
|
||||
beast::Journal j)
|
||||
: yays_ (0)
|
||||
, nays_ (0)
|
||||
, ourVote_ (ourVote)
|
||||
, tx_ (tx)
|
||||
, j_ (j)
|
||||
{
|
||||
}
|
||||
|
||||
TxID_t const& getID () const
|
||||
//! The unique id/hash of the disputed transaction.
|
||||
TxID_t
|
||||
const& ID () const
|
||||
{
|
||||
return mTransactionID;
|
||||
return tx_.id();
|
||||
}
|
||||
|
||||
bool getOurVote () const
|
||||
//! Our vote on whether the transaction should be included.
|
||||
bool
|
||||
getOurVote () const
|
||||
{
|
||||
return mOurVote;
|
||||
return ourVote_;
|
||||
}
|
||||
|
||||
Tx_t const& tx () const
|
||||
//! The disputed transaction.
|
||||
Tx_t
|
||||
const& tx () const
|
||||
{
|
||||
return transaction;
|
||||
return tx_;
|
||||
}
|
||||
|
||||
void setOurVote (bool o)
|
||||
//! Change our vote
|
||||
void
|
||||
setOurVote (bool o)
|
||||
{
|
||||
mOurVote = o;
|
||||
ourVote_ = o;
|
||||
}
|
||||
|
||||
void setVote (NodeID_t const& peer, bool votesYes);
|
||||
void unVote (NodeID_t const& peer);
|
||||
/** Change a peer's vote
|
||||
|
||||
bool updateVote (int percentTime, bool proposing);
|
||||
Json::Value getJson ();
|
||||
@param peer Identifier of peer.
|
||||
@param votesYes Whether peer votes to include the disputed transaction.
|
||||
*/
|
||||
void
|
||||
setVote (NodeID_t const& peer, bool votesYes);
|
||||
|
||||
/** Remove a peer's vote
|
||||
|
||||
@param peer Identifier of peer.
|
||||
*/
|
||||
void
|
||||
unVote (NodeID_t const& peer);
|
||||
|
||||
/** Update our vote given progression of consensus.
|
||||
|
||||
Updates our vote on this disputed transaction based on our peers' votes
|
||||
and how far along consensus has proceeded.
|
||||
|
||||
@param percentTime Percentage progress through consensus, e.g. 50%
|
||||
through or 90%.
|
||||
@param proposing Whether we are proposing to our peers in this round.
|
||||
@return Whether our vote changed
|
||||
*/
|
||||
bool
|
||||
updateVote (int percentTime, bool proposing);
|
||||
|
||||
//! JSON representation of dispute, used for debugging
|
||||
Json::Value
|
||||
getJson () const;
|
||||
|
||||
private:
|
||||
TxID_t mTransactionID;
|
||||
int mYays;
|
||||
int mNays;
|
||||
bool mOurVote;
|
||||
Tx_t transaction;
|
||||
int yays_; //< Number of yes votes
|
||||
int nays_; //< Number of no votes
|
||||
bool ourVote_; //< Our vote (true is yes)
|
||||
Tx_t tx_; //< Transaction under dispute
|
||||
|
||||
hash_map <NodeID_t, bool> mVotes;
|
||||
beast::Journal j_;
|
||||
hash_map <NodeID_t, bool> votes_; //< Votes of our peers
|
||||
beast::Journal j_; //< Debug journal
|
||||
};
|
||||
|
||||
// Track a peer's yes/no vote on a particular disputed transaction
|
||||
template <class Traits>
|
||||
void DisputedTx<Traits>::setVote (NodeID_t const& peer, bool votesYes)
|
||||
// Track a peer's yes/no vote on a particular disputed tx_
|
||||
template <class Tx_t, class NodeID_t>
|
||||
void DisputedTx<Tx_t, NodeID_t>::setVote (NodeID_t const& peer, bool votesYes)
|
||||
{
|
||||
auto res = mVotes.insert (std::make_pair (peer, votesYes));
|
||||
auto res = votes_.insert (std::make_pair (peer, votesYes));
|
||||
|
||||
// new vote
|
||||
if (res.second)
|
||||
@@ -106,60 +148,60 @@ void DisputedTx<Traits>::setVote (NodeID_t const& peer, bool votesYes)
|
||||
if (votesYes)
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Peer " << peer << " votes YES on " << mTransactionID;
|
||||
++mYays;
|
||||
<< "Peer " << peer << " votes YES on " << tx_.id();
|
||||
++yays_;
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Peer " << peer << " votes NO on " << mTransactionID;
|
||||
++mNays;
|
||||
<< "Peer " << peer << " votes NO on " << tx_.id();
|
||||
++nays_;
|
||||
}
|
||||
}
|
||||
// changes vote to yes
|
||||
else if (votesYes && !res.first->second)
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Peer " << peer << " now votes YES on " << mTransactionID;
|
||||
--mNays;
|
||||
++mYays;
|
||||
<< "Peer " << peer << " now votes YES on " << tx_.id();
|
||||
--nays_;
|
||||
++yays_;
|
||||
res.first->second = true;
|
||||
}
|
||||
// changes vote to no
|
||||
else if (!votesYes && res.first->second)
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Peer " << peer << " now votes NO on " << mTransactionID;
|
||||
++mNays;
|
||||
--mYays;
|
||||
<< "Peer " << peer << " now votes NO on " << tx_.id();
|
||||
++nays_;
|
||||
--yays_;
|
||||
res.first->second = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a peer's vote on this disputed transasction
|
||||
template <class Traits>
|
||||
void DisputedTx<Traits>::unVote (NodeID_t const& peer)
|
||||
template <class Tx_t, class NodeID_t>
|
||||
void DisputedTx<Tx_t, NodeID_t>::unVote (NodeID_t const& peer)
|
||||
{
|
||||
auto it = mVotes.find (peer);
|
||||
auto it = votes_.find (peer);
|
||||
|
||||
if (it != mVotes.end ())
|
||||
if (it != votes_.end ())
|
||||
{
|
||||
if (it->second)
|
||||
--mYays;
|
||||
--yays_;
|
||||
else
|
||||
--mNays;
|
||||
--nays_;
|
||||
|
||||
mVotes.erase (it);
|
||||
votes_.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
bool DisputedTx<Traits>::updateVote (int percentTime, bool proposing)
|
||||
template <class Tx_t, class NodeID_t>
|
||||
bool DisputedTx<Tx_t, NodeID_t>::updateVote (int percentTime, bool proposing)
|
||||
{
|
||||
if (mOurVote && (mNays == 0))
|
||||
if (ourVote_ && (nays_ == 0))
|
||||
return false;
|
||||
|
||||
if (!mOurVote && (mYays == 0))
|
||||
if (!ourVote_ && (yays_ == 0))
|
||||
return false;
|
||||
|
||||
bool newPosition;
|
||||
@@ -168,7 +210,7 @@ bool DisputedTx<Traits>::updateVote (int percentTime, bool proposing)
|
||||
if (proposing) // give ourselves full weight
|
||||
{
|
||||
// This is basically the percentage of nodes voting 'yes' (including us)
|
||||
weight = (mYays * 100 + (mOurVote ? 100 : 0)) / (mNays + mYays + 1);
|
||||
weight = (yays_ * 100 + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1);
|
||||
|
||||
// VFALCO TODO Rename these macros and turn them into language
|
||||
// constructs. consolidate them into a class that collects
|
||||
@@ -189,39 +231,41 @@ bool DisputedTx<Traits>::updateVote (int percentTime, bool proposing)
|
||||
{
|
||||
// don't let us outweigh a proposing node, just recognize consensus
|
||||
weight = -1;
|
||||
newPosition = mYays > mNays;
|
||||
newPosition = yays_ > nays_;
|
||||
}
|
||||
|
||||
if (newPosition == mOurVote)
|
||||
if (newPosition == ourVote_)
|
||||
{
|
||||
JLOG (j_.info())
|
||||
<< "No change (" << (mOurVote ? "YES" : "NO") << ") : weight "
|
||||
<< "No change (" << (ourVote_ ? "YES" : "NO") << ") : weight "
|
||||
<< weight << ", percent " << percentTime;
|
||||
JLOG (j_.debug()) << getJson ();
|
||||
return false;
|
||||
}
|
||||
|
||||
mOurVote = newPosition;
|
||||
ourVote_ = newPosition;
|
||||
JLOG (j_.debug())
|
||||
<< "We now vote " << (mOurVote ? "YES" : "NO")
|
||||
<< " on " << mTransactionID;
|
||||
<< "We now vote " << (ourVote_ ? "YES" : "NO")
|
||||
<< " on " << tx_.id();
|
||||
JLOG (j_.debug()) << getJson ();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
Json::Value DisputedTx<Traits>::getJson ()
|
||||
template <class Tx_t, class NodeID_t>
|
||||
Json::Value DisputedTx<Tx_t, NodeID_t>::getJson () const
|
||||
{
|
||||
using std::to_string;
|
||||
|
||||
Json::Value ret (Json::objectValue);
|
||||
|
||||
ret["yays"] = mYays;
|
||||
ret["nays"] = mNays;
|
||||
ret["our_vote"] = mOurVote;
|
||||
ret["yays"] = yays_;
|
||||
ret["nays"] = nays_;
|
||||
ret["our_vote"] = ourVote_;
|
||||
|
||||
if (!mVotes.empty ())
|
||||
if (!votes_.empty ())
|
||||
{
|
||||
Json::Value votesj (Json::objectValue);
|
||||
for (auto& vote : mVotes)
|
||||
for (auto& vote : votes_)
|
||||
votesj[to_string (vote.first)] = vote.second;
|
||||
ret["votes"] = std::move (votesj);
|
||||
}
|
||||
@@ -18,68 +18,19 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/ledger/LedgerTiming.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
NetClock::duration
|
||||
getNextLedgerTimeResolution (
|
||||
NetClock::duration previousResolution,
|
||||
bool previousAgree,
|
||||
std::uint32_t ledgerSeq)
|
||||
{
|
||||
assert (ledgerSeq);
|
||||
|
||||
using namespace std::chrono;
|
||||
// Find the current resolution:
|
||||
auto iter = std::find (std::begin (ledgerPossibleTimeResolutions),
|
||||
std::end (ledgerPossibleTimeResolutions), previousResolution);
|
||||
assert (iter != std::end (ledgerPossibleTimeResolutions));
|
||||
|
||||
// This should never happen, but just as a precaution
|
||||
if (iter == std::end (ledgerPossibleTimeResolutions))
|
||||
return previousResolution;
|
||||
|
||||
// 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 (++iter != std::end (ledgerPossibleTimeResolutions))
|
||||
return *iter;
|
||||
}
|
||||
|
||||
// If we previously agreed, we try to increase the resolution to determine
|
||||
// if we can continue to agree.
|
||||
if (previousAgree && ledgerSeq % increaseLedgerTimeResolutionEvery == 0)
|
||||
{
|
||||
if (iter-- != std::begin (ledgerPossibleTimeResolutions))
|
||||
return *iter;
|
||||
}
|
||||
|
||||
return previousResolution;
|
||||
}
|
||||
|
||||
NetClock::time_point
|
||||
roundCloseTime (
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::duration closeResolution)
|
||||
{
|
||||
if (closeTime == NetClock::time_point{})
|
||||
return closeTime;
|
||||
|
||||
closeTime += (closeResolution / 2);
|
||||
return closeTime - (closeTime.time_since_epoch() % closeResolution);
|
||||
}
|
||||
|
||||
bool
|
||||
shouldCloseLedger (
|
||||
bool anyTransactions,
|
||||
int previousProposers,
|
||||
int proposersClosed,
|
||||
int proposersValidated,
|
||||
std::size_t previousProposers,
|
||||
std::size_t proposersClosed,
|
||||
std::size_t proposersValidated,
|
||||
std::chrono::milliseconds previousTime,
|
||||
std::chrono::milliseconds currentTime, // Time since last ledger's close time
|
||||
std::chrono::milliseconds openTime, // Time waiting to close this ledger
|
||||
@@ -135,7 +86,10 @@ shouldCloseLedger (
|
||||
}
|
||||
|
||||
bool
|
||||
checkConsensusReached (int agreeing, int total, bool count_self)
|
||||
checkConsensusReached (
|
||||
std::size_t agreeing,
|
||||
std::size_t total,
|
||||
bool count_self)
|
||||
{
|
||||
// If we are alone, we have a consensus
|
||||
if (total == 0)
|
||||
@@ -154,10 +108,10 @@ checkConsensusReached (int agreeing, int total, bool count_self)
|
||||
|
||||
ConsensusState
|
||||
checkConsensus (
|
||||
int previousProposers,
|
||||
int currentProposers,
|
||||
int currentAgree,
|
||||
int currentFinished,
|
||||
std::size_t previousProposers,
|
||||
std::size_t currentProposers,
|
||||
std::size_t currentAgree,
|
||||
std::size_t currentFinished,
|
||||
std::chrono::milliseconds previousAgreeTime,
|
||||
std::chrono::milliseconds currentAgreeTime,
|
||||
bool proposing,
|
||||
@@ -27,6 +27,117 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// These are protocol parameters used to control the behavior of the system and
|
||||
// they should not be changed arbitrarily.
|
||||
|
||||
//! The percentage threshold above which we can declare consensus.
|
||||
auto constexpr minimumConsensusPercentage = 80;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
/** Possible close time resolutions.
|
||||
|
||||
Values should not be duplicated.
|
||||
@see getNextLedgerTimeResolution
|
||||
*/
|
||||
std::chrono::seconds constexpr ledgerPossibleTimeResolutions[] =
|
||||
{ 10s, 20s, 30s, 60s, 90s, 120s };
|
||||
|
||||
#ifndef _MSC_VER
|
||||
//! Initial resolution of ledger close time.
|
||||
auto constexpr ledgerDefaultTimeResolution = ledgerPossibleTimeResolutions[2];
|
||||
#else
|
||||
// HH Remove this workaround of a VS bug when possible
|
||||
//! Initial resolution of ledger close time.
|
||||
auto constexpr ledgerDefaultTimeResolution = 30s;
|
||||
#endif
|
||||
|
||||
//! How often we increase the close time resolution (in numbers of ledgers)
|
||||
auto constexpr increaseLedgerTimeResolutionEvery = 8;
|
||||
|
||||
//! How often we decrease the close time resolution (in numbers of ledgers)
|
||||
auto constexpr decreaseLedgerTimeResolutionEvery = 1;
|
||||
|
||||
//! The number of seconds a ledger may remain idle before closing
|
||||
auto constexpr LEDGER_IDLE_INTERVAL = 15s;
|
||||
|
||||
/** The number of seconds a validation remains current after its ledger's close
|
||||
time.
|
||||
|
||||
This is a safety to protect against very old validations and the time
|
||||
it takes to adjust the close time accuracy window.
|
||||
*/
|
||||
auto constexpr VALIDATION_VALID_WALL = 5min;
|
||||
|
||||
/** Duration a validation remains current after first observed.
|
||||
|
||||
The number of seconds a validation remains current after the time we first
|
||||
saw it. This provides faster recovery in very rare cases where the number
|
||||
of validations produced by the network is lower than normal
|
||||
*/
|
||||
auto constexpr VALIDATION_VALID_LOCAL = 3min;
|
||||
|
||||
/** Duration pre-close in which validations are acceptable.
|
||||
|
||||
The number of seconds before a close time that we consider a validation
|
||||
acceptable. This protects against extreme clock errors
|
||||
*/
|
||||
auto constexpr VALIDATION_VALID_EARLY = 3min;
|
||||
|
||||
//! The number of seconds we wait minimum to ensure participation
|
||||
auto constexpr LEDGER_MIN_CONSENSUS = 1950ms;
|
||||
|
||||
//! Minimum number of seconds to wait to ensure others have computed the LCL
|
||||
auto constexpr LEDGER_MIN_CLOSE = 2s;
|
||||
|
||||
//! How often we check state or change positions
|
||||
auto constexpr LEDGER_GRANULARITY = 1s;
|
||||
|
||||
//! How long we consider a proposal fresh
|
||||
auto constexpr PROPOSE_FRESHNESS = 20s;
|
||||
|
||||
//! How often we force generating a new proposal to keep ours fresh
|
||||
auto constexpr PROPOSE_INTERVAL = 12s;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Avalanche tuning
|
||||
//! Percentage of nodes on our UNL that must vote yes
|
||||
auto constexpr AV_INIT_CONSENSUS_PCT = 50;
|
||||
|
||||
//! Percentage of previous close time before we advance
|
||||
auto constexpr AV_MID_CONSENSUS_TIME = 50;
|
||||
|
||||
//! Percentage of nodes that most vote yes after advancing
|
||||
auto constexpr AV_MID_CONSENSUS_PCT = 65;
|
||||
|
||||
//! Percentage of previous close time before we advance
|
||||
auto constexpr AV_LATE_CONSENSUS_TIME = 85;
|
||||
|
||||
//! Percentage of nodes that most vote yes after advancing
|
||||
auto constexpr AV_LATE_CONSENSUS_PCT = 70;
|
||||
|
||||
//! Percentage of previous close time before we are stuck
|
||||
auto constexpr AV_STUCK_CONSENSUS_TIME = 200;
|
||||
|
||||
//! Percentage of nodes that must vote yes after we are stuck
|
||||
auto constexpr AV_STUCK_CONSENSUS_PCT = 95;
|
||||
|
||||
//! Percentage of nodes required to reach agreement on ledger close time
|
||||
auto constexpr AV_CT_CONSENSUS_PCT = 75;
|
||||
|
||||
/** The minimum amount of time to consider the previous round
|
||||
to have taken.
|
||||
|
||||
The minimum amount of time to consider the previous round
|
||||
to have taken. This ensures that there is an opportunity
|
||||
for a round at each avalanche threshold even if the
|
||||
previous consensus was very fast. This should be at least
|
||||
twice the interval between proposals (0.7s) divided by
|
||||
the interval between mid and late consensus ([85-50]/100).
|
||||
*/
|
||||
auto constexpr AV_MIN_CONSENSUS_TIME = 5s;
|
||||
|
||||
/** Calculates the close time resolution for the specified ledger.
|
||||
|
||||
The Ripple protocol uses binning to represent time intervals using only one
|
||||
@@ -34,22 +145,95 @@ namespace ripple {
|
||||
without the need for perfectly synchronized clocks.
|
||||
The time resolution (i.e. the size of the intervals) is adjusted dynamically
|
||||
based on what happened in the last ledger, to try to avoid disagreements.
|
||||
|
||||
@param previousResolution the resolution used for the prior ledger
|
||||
@param previousAgree whether consensus agreed on the close time of the prior
|
||||
ledger
|
||||
@param ledgerSeq the sequence number of the new ledger
|
||||
|
||||
@pre previousResolution must be a valid bin
|
||||
from @ref ledgerPossibleTimeResolutions
|
||||
*/
|
||||
NetClock::duration
|
||||
getNextLedgerTimeResolution (
|
||||
NetClock::duration previousResolution,
|
||||
template <class duration>
|
||||
duration
|
||||
getNextLedgerTimeResolution(
|
||||
duration previousResolution,
|
||||
bool previousAgree,
|
||||
std::uint32_t ledgerSeq);
|
||||
std::uint32_t ledgerSeq)
|
||||
{
|
||||
assert (ledgerSeq);
|
||||
|
||||
using namespace std::chrono;
|
||||
// Find the current resolution:
|
||||
auto iter = std::find (std::begin (ledgerPossibleTimeResolutions),
|
||||
std::end (ledgerPossibleTimeResolutions), previousResolution);
|
||||
assert (iter != std::end (ledgerPossibleTimeResolutions));
|
||||
|
||||
// This should never happen, but just as a precaution
|
||||
if (iter == std::end (ledgerPossibleTimeResolutions))
|
||||
return previousResolution;
|
||||
|
||||
// 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 (++iter != std::end (ledgerPossibleTimeResolutions))
|
||||
return *iter;
|
||||
}
|
||||
|
||||
// If we previously agreed, we try to increase the resolution to determine
|
||||
// if we can continue to agree.
|
||||
if (previousAgree && ledgerSeq % increaseLedgerTimeResolutionEvery == 0)
|
||||
{
|
||||
if (iter-- != std::begin (ledgerPossibleTimeResolutions))
|
||||
return *iter;
|
||||
}
|
||||
|
||||
return previousResolution;
|
||||
}
|
||||
|
||||
/** Calculates the close time for a ledger, given a close time resolution.
|
||||
|
||||
@param closeTime The time to be rouned.
|
||||
@param closeResolution The resolution
|
||||
@return @b closeTime rounded to the nearest multiple of @b closeResolution.
|
||||
Rounds up if @b closeTime is midway between multiples of @b closeResolution.
|
||||
*/
|
||||
NetClock::time_point
|
||||
roundCloseTime (
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::duration closeResolution);
|
||||
template <class time_point>
|
||||
time_point
|
||||
roundCloseTime(
|
||||
time_point closeTime,
|
||||
typename time_point::duration closeResolution)
|
||||
{
|
||||
if (closeTime == time_point{})
|
||||
return closeTime;
|
||||
|
||||
closeTime += (closeResolution / 2);
|
||||
return closeTime - (closeTime.time_since_epoch() % closeResolution);
|
||||
}
|
||||
|
||||
|
||||
/** Calculate the effective ledger close time
|
||||
|
||||
After adjusting the ledger close time based on the current resolution, also
|
||||
ensure it is sufficiently separated from the prior close time.
|
||||
|
||||
@param closeTime The raw ledger close time
|
||||
@param resolution The current close time resolution
|
||||
@param priorCloseTime The close time of the prior ledger
|
||||
*/
|
||||
template <class time_point>
|
||||
time_point effectiveCloseTime(time_point closeTime,
|
||||
typename time_point::duration const resolution,
|
||||
time_point priorCloseTime)
|
||||
{
|
||||
if (closeTime == time_point{})
|
||||
return closeTime;
|
||||
|
||||
return std::max<time_point>(
|
||||
roundCloseTime (closeTime, resolution),
|
||||
(priorCloseTime + 1s));
|
||||
}
|
||||
|
||||
/** Determines whether the current ledger should close at this time.
|
||||
|
||||
@@ -71,9 +255,9 @@ roundCloseTime (
|
||||
bool
|
||||
shouldCloseLedger (
|
||||
bool anyTransactions,
|
||||
int previousProposers,
|
||||
int proposersClosed,
|
||||
int proposersValidated,
|
||||
std::size_t previousProposers,
|
||||
std::size_t proposersClosed,
|
||||
std::size_t proposersValidated,
|
||||
std::chrono::milliseconds previousTime,
|
||||
std::chrono::milliseconds currentTime, // Time since last ledger's close time
|
||||
std::chrono::milliseconds openTime, // Time waiting to close this ledger
|
||||
@@ -91,14 +275,17 @@ shouldCloseLedger (
|
||||
@return True if a consensus has been reached
|
||||
*/
|
||||
bool
|
||||
checkConsensusReached (int agreeing, int total, bool count_self);
|
||||
checkConsensusReached (
|
||||
std::size_t agreeing,
|
||||
std::size_t total,
|
||||
bool count_self);
|
||||
|
||||
/** Whether we have or don't have a consensus */
|
||||
enum class ConsensusState
|
||||
{
|
||||
No, // We do not have consensus
|
||||
MovedOn, // The network has consensus without us
|
||||
Yes // We have consensus along with the network
|
||||
No, //!< We do not have consensus
|
||||
MovedOn, //!< The network has consensus without us
|
||||
Yes //!< We have consensus along with the network
|
||||
};
|
||||
|
||||
/** Determine whether the network reached consensus and whether we joined.
|
||||
@@ -116,103 +303,15 @@ enum class ConsensusState
|
||||
*/
|
||||
ConsensusState
|
||||
checkConsensus (
|
||||
int previousProposers,
|
||||
int currentProposers,
|
||||
int currentAgree,
|
||||
int currentFinished,
|
||||
std::size_t previousProposers,
|
||||
std::size_t currentProposers,
|
||||
std::size_t currentAgree,
|
||||
std::size_t currentFinished,
|
||||
std::chrono::milliseconds previousAgreeTime,
|
||||
std::chrono::milliseconds currentAgreeTime,
|
||||
bool proposing,
|
||||
beast::Journal j);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// These are protocol parameters used to control the behavior of the system and
|
||||
// they should not be changed arbitrarily.
|
||||
|
||||
// The percentage threshold above which we can declare consensus.
|
||||
auto constexpr minimumConsensusPercentage = 80;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
// All possible close time resolutions. Values should not be duplicated.
|
||||
std::chrono::seconds constexpr ledgerPossibleTimeResolutions[] =
|
||||
{ 10s, 20s, 30s, 60s, 90s, 120s };
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// Initial resolution of ledger close time.
|
||||
auto constexpr ledgerDefaultTimeResolution = ledgerPossibleTimeResolutions[2];
|
||||
#else
|
||||
// HH Remove this workaround of a VS bug when possible
|
||||
auto constexpr ledgerDefaultTimeResolution = 30s;
|
||||
#endif
|
||||
|
||||
// How often we increase the close time resolution
|
||||
auto constexpr increaseLedgerTimeResolutionEvery = 8;
|
||||
|
||||
// How often we decrease the close time resolution
|
||||
auto constexpr decreaseLedgerTimeResolutionEvery = 1;
|
||||
|
||||
// The number of seconds a ledger may remain idle before closing
|
||||
auto constexpr LEDGER_IDLE_INTERVAL = 15s;
|
||||
|
||||
// The number of seconds a validation remains current after its ledger's close
|
||||
// time. This is a safety to protect against very old validations and the time
|
||||
// it takes to adjust the close time accuracy window
|
||||
auto constexpr VALIDATION_VALID_WALL = 5min;
|
||||
|
||||
// The number of seconds a validation remains current after the time we first
|
||||
// saw it. This provides faster recovery in very rare cases where the number
|
||||
// of validations produced by the network is lower than normal
|
||||
auto constexpr VALIDATION_VALID_LOCAL = 3min;
|
||||
|
||||
// The number of seconds before a close time that we consider a validation
|
||||
// acceptable. This protects against extreme clock errors
|
||||
auto constexpr VALIDATION_VALID_EARLY = 3min;
|
||||
|
||||
// The number of seconds we wait minimum to ensure participation
|
||||
auto constexpr LEDGER_MIN_CONSENSUS = 1950ms;
|
||||
|
||||
// Minimum number of seconds to wait to ensure others have computed the LCL
|
||||
auto constexpr LEDGER_MIN_CLOSE = 2s;
|
||||
|
||||
// How often we check state or change positions (in milliseconds)
|
||||
auto constexpr LEDGER_GRANULARITY = 1s;
|
||||
|
||||
// How long we consider a proposal fresh
|
||||
auto constexpr PROPOSE_FRESHNESS = 20s;
|
||||
|
||||
// How often we force generating a new proposal to keep ours fresh
|
||||
auto constexpr PROPOSE_INTERVAL = 12s;
|
||||
|
||||
// Avalanche tuning
|
||||
// percentage of nodes on our UNL that must vote yes
|
||||
auto constexpr AV_INIT_CONSENSUS_PCT = 50;
|
||||
|
||||
// percentage of previous close time before we advance
|
||||
auto constexpr AV_MID_CONSENSUS_TIME = 50;
|
||||
|
||||
// percentage of nodes that most vote yes after advancing
|
||||
auto constexpr AV_MID_CONSENSUS_PCT = 65;
|
||||
|
||||
// percentage of previous close time before we advance
|
||||
auto constexpr AV_LATE_CONSENSUS_TIME = 85;
|
||||
|
||||
// percentage of nodes that most vote yes after advancing
|
||||
auto constexpr AV_LATE_CONSENSUS_PCT = 70;
|
||||
|
||||
auto constexpr AV_STUCK_CONSENSUS_TIME = 200;
|
||||
auto constexpr AV_STUCK_CONSENSUS_PCT = 95;
|
||||
|
||||
auto constexpr AV_CT_CONSENSUS_PCT = 75;
|
||||
|
||||
// The minimum amount of time to consider the previous round
|
||||
// to have taken. This ensures that there is an opportunity
|
||||
// for a round at each avalanche threshold even if the
|
||||
// previous consensus was very fast. This should be at least
|
||||
// twice the interval between proposals (0.7s) divided by
|
||||
// the interval between mid and late consensus ([85-50]/100).
|
||||
auto constexpr AV_MIN_CONSENSUS_TIME = 5s;
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
9
src/ripple/consensus/README.md
Normal file
9
src/ripple/consensus/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Consensus
|
||||
|
||||
This directory contains the implementation of a
|
||||
generic consensus algorithm. The implementation
|
||||
follows a CRTP design, requiring client code to implement
|
||||
specific functions and types to use consensus in their
|
||||
application. The interface is undergoing refactoring and
|
||||
is not yet finalized.
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <ripple/overlay/impl/Tuning.h>
|
||||
#include <ripple/app/ledger/InboundLedgers.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/LedgerTiming.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/app/ledger/InboundTransactions.h>
|
||||
#include <ripple/app/misc/HashRouter.h>
|
||||
#include <ripple/app/misc/LoadFeeTrack.h>
|
||||
@@ -1275,10 +1275,10 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMProposeSet> const& m)
|
||||
JLOG(p_journal_.trace()) <<
|
||||
"Proposal: " << (isTrusted ? "trusted" : "UNTRUSTED");
|
||||
|
||||
auto proposal = std::make_shared<LedgerProposal> (
|
||||
prevLedger, set.proposeseq (), proposeHash, closeTime,
|
||||
app_.timeKeeper().closeTime(), publicKey, calcNodeID(publicKey),
|
||||
signature, suppression);
|
||||
auto proposal = std::make_shared<RCLCxPeerPos> (
|
||||
publicKey, signature, suppression,
|
||||
RCLCxPeerPos::Proposal{prevLedger, set.proposeseq (), proposeHash, closeTime,
|
||||
app_.timeKeeper().closeTime(),calcNodeID(publicKey)});
|
||||
|
||||
std::weak_ptr<PeerImp> weak = shared_from_this();
|
||||
app_.getJobQueue ().addJob (
|
||||
@@ -1878,7 +1878,7 @@ PeerImp::checkTransaction (int flags,
|
||||
void
|
||||
PeerImp::checkPropose (Job& job,
|
||||
std::shared_ptr <protocol::TMProposeSet> const& packet,
|
||||
LedgerProposal::pointer proposal)
|
||||
RCLCxPeerPos::pointer peerPos)
|
||||
{
|
||||
bool isTrusted = (job.getType () == jtPROPOSAL_t);
|
||||
|
||||
@@ -1888,7 +1888,7 @@ PeerImp::checkPropose (Job& job,
|
||||
assert (packet);
|
||||
protocol::TMProposeSet& set = *packet;
|
||||
|
||||
if (! cluster() && ! proposal->checkSign ())
|
||||
if (! cluster() && !peerPos->checkSign ())
|
||||
{
|
||||
JLOG(p_journal_.warn()) <<
|
||||
"Proposal fails sig check";
|
||||
@@ -1899,16 +1899,16 @@ PeerImp::checkPropose (Job& job,
|
||||
if (isTrusted)
|
||||
{
|
||||
app_.getOPs ().processTrustedProposal (
|
||||
proposal, packet, calcNodeID (publicKey_));
|
||||
peerPos, packet, calcNodeID (publicKey_));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (app_.getOPs().getConsensusLCL() == proposal->getPrevLedger())
|
||||
if (app_.getOPs().getConsensusLCL() == peerPos->proposal().prevLedger())
|
||||
{
|
||||
// relay untrusted proposal
|
||||
JLOG(p_journal_.trace()) <<
|
||||
"relaying UNTRUSTED proposal";
|
||||
overlay_.relay(set, proposal->getSuppressionID());
|
||||
overlay_.relay(set, peerPos->getSuppressionID());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef RIPPLE_OVERLAY_PEERIMP_H_INCLUDED
|
||||
#define RIPPLE_OVERLAY_PEERIMP_H_INCLUDED
|
||||
|
||||
#include <ripple/app/ledger/LedgerProposal.h>
|
||||
#include <ripple/app/consensus/RCLCxPeerPos.h>
|
||||
#include <ripple/basics/Log.h> // deprecated
|
||||
#include <ripple/nodestore/Database.h>
|
||||
#include <ripple/overlay/predicates.h>
|
||||
@@ -452,7 +452,7 @@ private:
|
||||
void
|
||||
checkPropose (Job& job,
|
||||
std::shared_ptr<protocol::TMProposeSet> const& packet,
|
||||
LedgerProposal::pointer proposal);
|
||||
RCLCxPeerPos::pointer peerPos);
|
||||
|
||||
void
|
||||
checkValidation (STValidation::pointer val,
|
||||
|
||||
@@ -108,7 +108,7 @@ JSS ( check_nodes ); // in: LedgerCleaner
|
||||
JSS ( clear ); // in/out: FetchInfo
|
||||
JSS ( close_flags ); // out: LedgerToJson
|
||||
JSS ( close_time ); // in: Application, out: NetworkOPs,
|
||||
// LedgerProposal, LedgerToJson
|
||||
// RCLCxPeerPos, LedgerToJson
|
||||
JSS ( close_time_estimated ); // in: Application, out: LedgerToJson
|
||||
JSS ( close_time_human ); // out: LedgerToJson
|
||||
JSS ( close_time_offset ); // out: NetworkOPs
|
||||
@@ -316,7 +316,7 @@ JSS ( paths_canonical ); // out: RipplePathFind
|
||||
JSS ( paths_computed ); // out: PathRequest, RipplePathFind
|
||||
JSS ( peer ); // in: AccountLines
|
||||
JSS ( peer_authorized ); // out: AccountLines
|
||||
JSS ( peer_id ); // out: LedgerProposal
|
||||
JSS ( peer_id ); // out: RCLCxPeerPos
|
||||
JSS ( peers ); // out: InboundLedger, handlers/Peers, Overlay
|
||||
JSS ( port ); // in: Connect
|
||||
JSS ( previous_ledger ); // out: LedgerPropose
|
||||
@@ -408,7 +408,7 @@ JSS ( total_coins ); // out: LedgerToJson
|
||||
JSS ( transTreeHash ); // out: ledger/Ledger.cpp
|
||||
JSS ( transaction ); // in: Tx
|
||||
// out: NetworkOPs, AcceptedLedgerTx,
|
||||
JSS ( transaction_hash ); // out: LedgerProposal, LedgerToJson
|
||||
JSS ( transaction_hash ); // out: RCLCxPeerPos, LedgerToJson
|
||||
JSS ( transactions ); // out: LedgerToJson,
|
||||
// in: AccountTx*, Unsubscribe
|
||||
JSS ( transitions ); // out: NetworkOPs
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
{
|
||||
mSeen = s;
|
||||
}
|
||||
Blob getSigned () const;
|
||||
Blob getSerialized () const;
|
||||
Blob getSignature () const;
|
||||
|
||||
// Signs the validation and returns the signing hash
|
||||
|
||||
@@ -134,7 +134,7 @@ Blob STValidation::getSignature () const
|
||||
return getFieldVL (sfSignature);
|
||||
}
|
||||
|
||||
Blob STValidation::getSigned () const
|
||||
Blob STValidation::getSerialized () const
|
||||
{
|
||||
Serializer s;
|
||||
add (s);
|
||||
|
||||
@@ -16,40 +16,7 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <BeastConfig.h>
|
||||
|
||||
#ifndef RIPPLE_APP_CONSENSUS_RCLCXTRAITS_H_INCLUDED
|
||||
#define RIPPLE_APP_CONSENSUS_RCLCXTRAITS_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/basics/base_uint.h>
|
||||
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/protocol/RippleLedgerHash.h>
|
||||
|
||||
#include <ripple/app/consensus/RCLCxPos.h>
|
||||
#include <ripple/app/consensus/RCLCxTx.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Consensus traits class
|
||||
// For adapting consensus to RCL
|
||||
|
||||
class RCLCxTraits
|
||||
{
|
||||
public:
|
||||
|
||||
using Time_t = NetClock::time_point;
|
||||
|
||||
using Pos_t = RCLCxPos;
|
||||
using TxSet_t = RCLTxSet;
|
||||
using Tx_t = RCLCxTx;
|
||||
|
||||
using LgrID_t = LedgerHash;
|
||||
using TxID_t = uint256;
|
||||
using TxSetID_t = uint256;
|
||||
using NodeID_t = NodeID;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#include <ripple/app/consensus/RCLConsensus.cpp>
|
||||
#include <ripple/app/consensus/RCLCxPeerPos.cpp>
|
||||
@@ -26,18 +26,14 @@
|
||||
#include <ripple/app/ledger/ConsensusTransSetSF.cpp>
|
||||
#include <ripple/app/ledger/Ledger.cpp>
|
||||
#include <ripple/app/ledger/LedgerHistory.cpp>
|
||||
#include <ripple/app/ledger/LedgerProposal.cpp>
|
||||
#include <ripple/app/ledger/OrderBookDB.cpp>
|
||||
#include <ripple/app/ledger/TransactionStateSF.cpp>
|
||||
|
||||
#include <ripple/app/ledger/impl/ConsensusImp.cpp>
|
||||
#include <ripple/app/ledger/impl/InboundLedger.cpp>
|
||||
#include <ripple/app/ledger/impl/InboundLedgers.cpp>
|
||||
#include <ripple/app/ledger/impl/InboundTransactions.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerCleaner.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerConsensusImp.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerMaster.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerTiming.cpp>
|
||||
#include <ripple/app/ledger/impl/LocalTxs.cpp>
|
||||
#include <ripple/app/ledger/impl/OpenLedger.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerToJson.cpp>
|
||||
|
||||
21
src/ripple/unity/consensus.cpp
Normal file
21
src/ripple/unity/consensus.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 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 <ripple/consensus/LedgerTiming.cpp>
|
||||
Reference in New Issue
Block a user