Migrate thread safety to RCLConsensus (RIPD-1389):

Moves thread safety from generic Consensus to RCLConsensus and switch generic
Consensus to adaptor design.
This commit is contained in:
Brad Chase
2017-04-26 11:13:30 -04:00
committed by seelabs
parent 8c155dd875
commit 01b4d5cdd4
23 changed files with 1729 additions and 1135 deletions

View File

@@ -1099,6 +1099,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorKeys.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorList.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -1131,6 +1135,8 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorKeys.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorSite.h">
@@ -1865,6 +1871,8 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\ConsensusTypes.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
@@ -4323,6 +4331,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\app\ValidatorKeys_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\app\ValidatorList_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -1635,6 +1635,9 @@
<ClCompile Include="..\..\src\ripple\app\misc\impl\TxQ.cpp">
<Filter>ripple\app\misc\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorKeys.cpp">
<Filter>ripple\app\misc\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorList.cpp">
<Filter>ripple\app\misc\impl</Filter>
</ClCompile>
@@ -1671,6 +1674,9 @@
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
<Filter>ripple\app\misc</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorKeys.h">
<Filter>ripple\app\misc</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
<Filter>ripple\app\misc</Filter>
</ClInclude>
@@ -2511,6 +2517,9 @@
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
<Filter>ripple\consensus</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\ConsensusTypes.h">
<Filter>ripple\consensus</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
<Filter>ripple\consensus</Filter>
</ClInclude>
@@ -5121,6 +5130,9 @@
<ClCompile Include="..\..\src\test\app\TxQ_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\ValidatorKeys_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\ValidatorList_test.cpp">
<Filter>test\app</Filter>
</ClCompile>

View File

@@ -480,69 +480,87 @@ struct Ledger
//... implementation specific
};
```
[heading Generic Consensus Interface]
Following the
[@https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern CRTP]
idiom, generic =Consensus= relies on a deriving class implementing a set of
helpers and callbacks that encapsulate implementation specific details of the
algorithm. Below are excerpts of the generic consensus implementation and of
helper types that will interact with the concrete implementing class.
[heading PeerProposal] The =PeerProposal= type represents the signed position taken
by a peer during consensus. The only type requirement is owning an instance of a
generic =ConsensusProposal=.
```
// Represents our proposed position or a peer's proposed position
// and is provided with the generic code
template <class NodeID_t, class LedgerID_t, class Position_t> class ConsensusProposal;
struct PeerPosition
{
ConsensusProposal<
NodeID_t,
typename Ledger::ID,
typename TxSet::ID> const &
proposal() const;
// ... implementation specific
};
```
[heading Generic Consensus Interface]
The generic =Consensus= relies on =Adaptor= template class to implement a set
of helper functions that plug the consensus algorithm into a specific application.
The =Adaptor= class also defines the types above needed by the algorithm. Below
are excerpts of the generic consensus implementation and of helper types that will
interact with the concrete implementing class.
```
// Represents a transction under dispute this round
template <class Tx_t, class NodeID_t> class DisputedTx;
template <class Derived, class Traits> class Consensus
// Represents how the node participates in Consensus this round
enum class ConsensusMode { proposing, observing, wrongLedger, switchedLedger};
// Measure duration of phases of consensus
class ConsensusTimer
{
protected:
enum class Mode { proposing, observing, wrongLedger, switchedLedger};
// Measure duration of phases of consensus
class Stopwatch
{
public:
std::chrono::milliseconds read() const;
// details omitted ...
};
// Initial ledger close times, not rounded by closeTimeResolution
// Used to gauge degree of synchronization between a node and its peers
struct CloseTimes
{
std::map<NetClock::time_point, int> peers;
NetClock::time_point self;
};
// Encapsulates the result of consensus.
struct Result
{
//! The set of transactions consensus agrees go in the ledger
TxSet_t set;
//! Our proposed position on transactions/close time
Proposal_t position;
//! Transactions which are under dispute with our peers
using Dispute_t = DisputedTx<Tx_t, NodeID_t>;
hash_map<typename Tx_t::ID, Dispute_t> disputes;
// Set of TxSet ids we have already compared/created disputes
hash_set<typename TxSet_t::ID> compares;
// Measures the duration of the establish phase for this consensus round
Stopwatch roundTime;
// Indicates state in which consensus ended. Once in the accept phase
// will be either Yes or MovedOn
ConsensusState state = ConsensusState::No;
};
public:
std::chrono::milliseconds read() const;
// details omitted ...
};
// Initial ledger close times, not rounded by closeTimeResolution
// Used to gauge degree of synchronization between a node and its peers
struct ConsensusCloseTimes
{
std::map<NetClock::time_point, int> peers;
NetClock::time_point self;
};
// Encapsulates the result of consensus.
template <class Adaptor>
struct ConsensusResult
{
//! The set of transactions consensus agrees go in the ledger
Adaptor::TxSet_t set;
//! Our proposed position on transactions/close time
ConsensusProposal<...> position;
//! Transactions which are under dispute with our peers
hash_map<Adaptor::Tx_t::ID, DisputedTx<...>> disputes;
// Set of TxSet ids we have already compared/created disputes
hash_set<typename Adaptor::TxSet_t::ID> compares;
// Measures the duration of the establish phase for this consensus round
ConsensusTimer roundTime;
// Indicates state in which consensus ended. Once in the accept phase
// will be either Yes or MovedOn
ConsensusState state = ConsensusState::No;
};
template <class Adaptor>
class Consensus
{
public:
Consensus(clock_type, Adaptor &, beast::journal);
// Kick-off the next round of consensus.
void startRound(
NetClock::time_point const& now,
@@ -568,26 +586,20 @@ public:
The stub below shows the set of callback/helper functions required in the implementing class.
```
struct Traits
struct Adaptor
{
using Ledger_t = Ledger;
using TxSet_t = TxSet;
using NodeID_t = ...; // Integer-like std::uint32_t to uniquely identify a node
using Ledger_t = Ledger;
using TxSet_t = TxSet;
using PeerProposal_t = PeerProposal;
using NodeID_t = ...; // Integer-like std::uint32_t to uniquely identify a node
};
class ConsensusImp : public Consensus<ConsensusImp, Traits>
{
// Attempt to acquire a specific ledger from the network.
boost::optional<Ledger> acquireLedger(Ledger::ID const & ledgerID);
// Acquire the transaction set associated with a proposed position.
boost::optional<TxSet> acquireTxSet(TxSet::ID const & setID);
// Get peers' proposed positions. Returns an iterable
// with value_type convertable to ConsensusPosition<...>
auto const & proposals(Ledger::ID const & ledgerID);
// Whether any transactions are in the open ledger
bool hasOpenTransactions() const;
@@ -602,24 +614,27 @@ class ConsensusImp : public Consensus<ConsensusImp, Traits>
// application thinks consensus should use as the prior ledger.
Ledger::ID getPrevLedger(Ledger::ID const & prevLedgerID,
Ledger const & prevLedger,
Mode mode);
ConsensusMode mode);
// Called when consensus operating mode changes
void onModeChange(ConsensuMode before, ConsensusMode after);
// Called when ledger closes. Implementation should generate an initial Result
// with position based on the current open ledger's transactions.
Result onClose(Ledger const &, Ledger const & prev, Mode mode);
ConsensusResult onClose(Ledger const &, Ledger const & prev, ConsensusMode mode);
// Called when ledger is accepted by consensus
void onAccept(Result const & result,
void onAccept(ConsensusResult const & result,
RCLCxLedger const & prevLedger,
NetClock::duration closeResolution,
CloseTimes const & rawCloseTimes,
Mode const & mode);
ConsensusCloseTimes const & rawCloseTimes,
ConsensusMode const & mode);
// Propose the position to peers.
void propose(ConsensusProposal<...> const & pos);
// Relay a received peer proposal on to other peer's.
void relay(ConsensusProposal<...> const & pos);
void relay(PeerPosition_t const & pos);
// Relay a disputed transaction to peers
void relay(TxSet::Tx const & tx);

View File

@@ -111,6 +111,7 @@ INPUT = \
../src/test/jtx/WSClient.h \
../src/ripple/consensus/Consensus.h \
../src/ripple/consensus/ConsensusProposal.h \
../src/ripple/consensus/ConsensusTypes.h \
../src/ripple/consensus/DisputedTx.h \
../src/ripple/consensus/LedgerTiming.h \
../src/ripple/consensus/Validations.h \

View File

@@ -30,6 +30,7 @@
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/misc/ValidatorKeys.h>
#include <ripple/app/misc/ValidatorList.h>
#include <ripple/app/tx/apply.h>
#include <ripple/basics/make_lock.h>
@@ -48,21 +49,45 @@ RCLConsensus::RCLConsensus(
LedgerMaster& ledgerMaster,
LocalTxs& localTxs,
InboundTransactions& inboundTransactions,
typename Base::clock_type const& clock,
Consensus<Adaptor>::clock_type const& clock,
ValidatorKeys const& validatorKeys,
beast::Journal journal)
: Base(clock, ConsensusParms{}, journal)
, app_(app)
, feeVote_(std::move(feeVote))
, ledgerMaster_(ledgerMaster)
, localTxs_(localTxs)
, inboundTransactions_{inboundTransactions}
: adaptor_(
app,
std::move(feeVote),
ledgerMaster,
localTxs,
inboundTransactions,
validatorKeys,
journal)
, consensus_(clock, adaptor_, journal)
, j_(journal)
, nodeID_{calcNodeID(app.nodeIdentity().first)}
{
}
RCLConsensus::Adaptor::Adaptor(
Application& app,
std::unique_ptr<FeeVote>&& feeVote,
LedgerMaster& ledgerMaster,
LocalTxs& localTxs,
InboundTransactions& inboundTransactions,
ValidatorKeys const& validatorKeys,
beast::Journal journal)
: app_(app)
, feeVote_(std::move(feeVote))
, ledgerMaster_(ledgerMaster)
, localTxs_(localTxs)
, inboundTransactions_{inboundTransactions}
, j_(journal)
, nodeID_{calcNodeID(app.nodeIdentity().first)}
, valPublic_{validatorKeys.publicKey}
, valSecret_{validatorKeys.secretKey}
{
}
boost::optional<RCLCxLedger>
RCLConsensus::acquireLedger(LedgerHash const& ledger)
RCLConsensus::Adaptor::acquireLedger(LedgerHash const& ledger)
{
// we need to switch the ledger we're working from
auto buildLCL = ledgerMaster_.getLedgerByHash(ledger);
@@ -96,37 +121,9 @@ RCLConsensus::acquireLedger(LedgerHash const& 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)
RCLConsensus::Adaptor::relay(RCLCxPeerPos const& peerPos)
{
protocol::TMProposeSet prop;
@@ -140,17 +137,17 @@ RCLConsensus::relay(RCLCxPeerPos const& peerPos)
prop.set_previousledger(
proposal.prevLedger().begin(), proposal.position().size());
auto const pk = peerPos.getPublicKey().slice();
auto const pk = peerPos.publicKey().slice();
prop.set_nodepubkey(pk.data(), pk.size());
auto const sig = peerPos.getSignature();
auto const sig = peerPos.signature();
prop.set_signature(sig.data(), sig.size());
app_.overlay().relay(prop, peerPos.getSuppressionID());
app_.overlay().relay(prop, peerPos.suppressionID());
}
void
RCLConsensus::relay(RCLCxTx const& tx)
RCLConsensus::Adaptor::relay(RCLCxTx const& tx)
{
// If we didn't relay this transaction recently, relay it to all peers
if (app_.getHashRouter().shouldRelay(tx.id()))
@@ -166,7 +163,7 @@ RCLConsensus::relay(RCLCxTx const& tx)
}
}
void
RCLConsensus::propose(RCLCxPeerPos::Proposal const& proposal)
RCLConsensus::Adaptor::propose(RCLCxPeerPos::Proposal const& proposal)
{
JLOG(j_.trace()) << "We propose: "
<< (proposal.isBowOut()
@@ -199,13 +196,13 @@ RCLConsensus::propose(RCLCxPeerPos::Proposal const& proposal)
}
void
RCLConsensus::relay(RCLTxSet const& set)
RCLConsensus::Adaptor::relay(RCLTxSet const& set)
{
inboundTransactions_.giveSet(set.id(), set.map_, false);
}
boost::optional<RCLTxSet>
RCLConsensus::acquireTxSet(RCLTxSet::ID const& setId)
RCLConsensus::Adaptor::acquireTxSet(RCLTxSet::ID const& setId)
{
if (auto set = inboundTransactions_.getSet(setId, true))
{
@@ -215,32 +212,32 @@ RCLConsensus::acquireTxSet(RCLTxSet::ID const& setId)
}
bool
RCLConsensus::hasOpenTransactions() const
RCLConsensus::Adaptor::hasOpenTransactions() const
{
return !app_.openLedger().empty();
}
std::size_t
RCLConsensus::proposersValidated(LedgerHash const& h) const
RCLConsensus::Adaptor::proposersValidated(LedgerHash const& h) const
{
return app_.getValidations().numTrustedForLedger(h);
}
std::size_t
RCLConsensus::proposersFinished(LedgerHash const& h) const
RCLConsensus::Adaptor::proposersFinished(LedgerHash const& h) const
{
return app_.getValidations().getNodesAfter(h);
}
uint256
RCLConsensus::getPrevLedger(
RCLConsensus::Adaptor::getPrevLedger(
uint256 ledgerID,
RCLCxLedger const& ledger,
Mode mode)
ConsensusMode mode)
{
uint256 parentID;
// Only set the parent ID if we believe ledger is the right ledger
if (mode != Mode::wrongLedger)
if (mode != ConsensusMode::wrongLedger)
parentID = ledger.parentID();
// Get validators that are on our ledger, or "close" to being on
@@ -265,28 +262,27 @@ RCLConsensus::getPrevLedger(
if (netLgr != ledgerID)
{
if (mode != Mode::wrongLedger)
if (mode != ConsensusMode::wrongLedger)
app_.getOPs().consensusViewChange();
if (auto stream = j_.debug())
{
for (auto& it : vals)
stream << "V: " << it.first << ", " << it.second.count;
stream << getJson(true);
}
}
return netLgr;
}
RCLConsensus::Result
RCLConsensus::onClose(
auto
RCLConsensus::Adaptor::onClose(
RCLCxLedger const& ledger,
NetClock::time_point const& closeTime,
Mode mode)
ConsensusMode mode) -> Result
{
const bool wrongLCL = mode == Mode::wrongLedger;
const bool proposing = mode == Mode::proposing;
const bool wrongLCL = mode == ConsensusMode::wrongLedger;
const bool proposing = mode == ConsensusMode::proposing;
notify(protocol::neCLOSING_LEDGER, ledger, !wrongLCL);
@@ -346,47 +342,68 @@ RCLConsensus::onClose(
}
void
RCLConsensus::onForceAccept(
RCLConsensus::Adaptor::onForceAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration const& closeResolution,
CloseTimes const& rawCloseTimes,
Mode const& mode)
ConsensusCloseTimes const& rawCloseTimes,
ConsensusMode const& mode,
Json::Value && consensusJson)
{
doAccept(result, prevLedger, closeResolution, rawCloseTimes, mode);
doAccept(
result,
prevLedger,
closeResolution,
rawCloseTimes,
mode,
std::move(consensusJson));
}
void
RCLConsensus::onAccept(
RCLConsensus::Adaptor::onAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration const& closeResolution,
CloseTimes const& rawCloseTimes,
Mode const& mode)
ConsensusCloseTimes const& rawCloseTimes,
ConsensusMode const& mode,
Json::Value && consensusJson)
{
app_.getJobQueue().addJob(
jtACCEPT, "acceptLedger", [&, that = this->shared_from_this() ](auto&) {
// note that no lock is held inside this thread, which
// is fine since once a ledger is accepted, consensus
// will not touch any internal state until startRound is called
that->doAccept(
result, prevLedger, closeResolution, rawCloseTimes, mode);
that->app_.getOPs().endConsensus();
jtACCEPT,
"acceptLedger",
[&, cj = std::move(consensusJson) ](auto&) mutable {
// Note that no lock is held or acquired during this job.
// This is because generic Consensus guarantees that once a ledger
// is accepted, the consensus results and capture by reference state
// will not change until startRound is called (which happens via
// endConsensus).
this->doAccept(
result,
prevLedger,
closeResolution,
rawCloseTimes,
mode,
std::move(cj));
this->app_.getOPs().endConsensus();
});
}
void
RCLConsensus::doAccept(
RCLConsensus::Adaptor::doAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration closeResolution,
CloseTimes const& rawCloseTimes,
Mode const& mode)
ConsensusCloseTimes const& rawCloseTimes,
ConsensusMode const& mode,
Json::Value && consensusJson)
{
prevProposers_ = result.proposers;
prevRoundTime_ = result.roundTime.read();
bool closeTimeCorrect;
const bool proposing = mode == Mode::proposing;
const bool haveCorrectLCL = mode != Mode::wrongLedger;
const bool proposing = mode == ConsensusMode::proposing;
const bool haveCorrectLCL = mode != ConsensusMode::wrongLedger;
const bool consensusFail = result.state == ConsensusState::MovedOn;
auto consensusCloseTime = result.position.closeTime();
@@ -447,7 +464,7 @@ RCLConsensus::doAccept(
JLOG(j_.info()) << "CNF buildLCL " << newLCLHash;
// See if we can accept a ledger as fully-validated
ledgerMaster_.consensusBuilt(sharedLCL.ledger_, getJson(true));
ledgerMaster_.consensusBuilt(sharedLCL.ledger_, std::move(consensusJson));
//-------------------------------------------------------------------------
{
@@ -538,7 +555,7 @@ RCLConsensus::doAccept(
// 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.
if ((mode == Mode::proposing || mode == Mode::observing) && !consensusFail)
if ((mode == ConsensusMode::proposing || mode == ConsensusMode::observing) && !consensusFail)
{
auto closeTime = rawCloseTimes.self;
@@ -577,7 +594,7 @@ RCLConsensus::doAccept(
}
void
RCLConsensus::notify(
RCLConsensus::Adaptor::notify(
protocol::NodeEvent ne,
RCLCxLedger const& ledger,
bool haveCorrectLCL)
@@ -713,7 +730,7 @@ applyTransactions(
}
RCLCxLedger
RCLConsensus::buildLCL(
RCLConsensus::Adaptor::buildLCL(
RCLCxLedger const& previousLedger,
RCLTxSet const& set,
NetClock::time_point closeTime,
@@ -809,7 +826,7 @@ RCLConsensus::buildLCL(
}
void
RCLConsensus::validate(RCLCxLedger const& ledger, bool proposing)
RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, bool proposing)
{
auto validationTime = app_.timeKeeper().closeTime();
if (validationTime <= lastValidationTime_)
@@ -852,37 +869,26 @@ RCLConsensus::validate(RCLCxLedger const& ledger, bool proposing)
Json::Value
RCLConsensus::getJson(bool full) const
{
auto ret = Base::getJson(full);
ret["validating"] = validating_;
Json::Value ret;
{
ScopedLockType _{mutex_};
ret = consensus_.getJson(full);
}
ret["validating"] = adaptor_.validating();
return ret;
}
PublicKey const&
RCLConsensus::getValidationPublicKey() const
{
return valPublic_;
}
void
RCLConsensus::setValidationKeys(
SecretKey const& valSecret,
PublicKey const& valPublic)
{
valSecret_ = valSecret;
valPublic_ = valPublic;
}
void
RCLConsensus::timerEntry(NetClock::time_point const& now)
{
try
{
Base::timerEntry(now);
ScopedLockType _{mutex_};
consensus_.timerEntry(now);
}
catch (SHAMapMissingNode const& mn)
{
// This should never happen
leaveConsensus();
JLOG(j_.error()) << "Missing node during consensus process " << mn;
Rethrow();
}
@@ -893,31 +899,45 @@ RCLConsensus::gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet)
{
try
{
Base::gotTxSet(now, txSet);
ScopedLockType _{mutex_};
consensus_.gotTxSet(now, txSet);
}
catch (SHAMapMissingNode const& mn)
{
// This should never happen
leaveConsensus();
JLOG(j_.error()) << "Missing node during consensus process " << mn;
Rethrow();
}
}
//! @see Consensus::simulate
void
RCLConsensus::startRound(
RCLConsensus::simulate(
NetClock::time_point const& now,
RCLCxLedger::ID const& prevLgrId,
RCLCxLedger const& prevLgr)
boost::optional<std::chrono::milliseconds> consensusDelay)
{
ScopedLockType _{mutex_};
consensus_.simulate(now, consensusDelay);
}
bool
RCLConsensus::peerProposal(
NetClock::time_point const& now,
RCLCxPeerPos const& newProposal)
{
ScopedLockType _{mutex_};
return consensus_.peerProposal(now, newProposal);
}
bool
RCLConsensus::Adaptor::preStartRound(RCLCxLedger const & prevLgr)
{
// We have a key, and we have some idea what the ledger is
validating_ =
!app_.getOPs().isNeedNetworkLedger() && (valPublic_.size() != 0);
// propose only if we're in sync with the network (and validating)
bool proposing =
validating_ && (app_.getOPs().getOperatingMode() == NetworkOPs::omFULL);
if (validating_)
{
JLOG(j_.info()) << "Entering consensus process, validating";
@@ -931,6 +951,19 @@ RCLConsensus::startRound(
// Notify inbOund ledgers that we are starting a new round
inboundTransactions_.newRound(prevLgr.seq());
Base::startRound(now, prevLgrId, prevLgr, proposing);
// propose only if we're in sync with the network (and validating)
return validating_ &&
(app_.getOPs().getOperatingMode() == NetworkOPs::omFULL);
}
void
RCLConsensus::startRound(
NetClock::time_point const& now,
RCLCxLedger::ID const& prevLgrId,
RCLCxLedger const& prevLgr)
{
ScopedLockType _{mutex_};
consensus_.startRound(
now, prevLgrId, prevLgr, adaptor_.preStartRound(prevLgr));
}
}

View File

@@ -34,34 +34,332 @@
#include <ripple/protocol/RippleLedgerHash.h>
#include <ripple/protocol/STValidation.h>
#include <ripple/shamap/SHAMap.h>
#include <atomic>
#include <mutex>
namespace ripple {
class InboundTransactions;
class LocalTxs;
class LedgerMaster;
class ValidatorKeys;
//! 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;
};
/** 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..
/** Manages the generic consensus algorithm for use by the RCL.
*/
class RCLConsensus final : public Consensus<RCLConsensus, RCLCxTraits>,
public std::enable_shared_from_this<RCLConsensus>,
public CountedObject<RCLConsensus>
class RCLConsensus
{
using Base = Consensus<RCLConsensus, RCLCxTraits>;
// Implements the Adaptor template interface required by Consensus.
class Adaptor
{
Application& app_;
std::unique_ptr<FeeVote> feeVote_;
LedgerMaster& ledgerMaster_;
LocalTxs& localTxs_;
InboundTransactions& inboundTransactions_;
beast::Journal j_;
NodeID const nodeID_;
PublicKey const valPublic_;
SecretKey const valSecret_;
// Ledger we most recently needed to acquire
LedgerHash acquiringLedger_;
ConsensusParms parms_;
// The timestamp of the last validation we used
NetClock::time_point lastValidationTime_;
// These members are queried via public accesors and are atomic for
// thread safety.
std::atomic<bool> validating_{false};
std::atomic<std::size_t> prevProposers_{0};
std::atomic<std::chrono::milliseconds> prevRoundTime_{
std::chrono::milliseconds{0}};
std::atomic<ConsensusMode> mode_{ConsensusMode::observing};
public:
using Ledger_t = RCLCxLedger;
using NodeID_t = NodeID;
using TxSet_t = RCLTxSet;
using PeerPosition_t = RCLCxPeerPos;
using Result = ConsensusResult<Adaptor>;
Adaptor(
Application& app,
std::unique_ptr<FeeVote>&& feeVote,
LedgerMaster& ledgerMaster,
LocalTxs& localTxs,
InboundTransactions& inboundTransactions,
ValidatorKeys const & validatorKeys,
beast::Journal journal);
bool
validating() const
{
return validating_;
}
std::size_t
prevProposers() const
{
return prevProposers_;
}
std::chrono::milliseconds
prevRoundTime() const
{
return prevRoundTime_;
}
ConsensusMode
mode() const
{
return mode_;
}
/** Called before kicking off a new consensus round.
@param prevLedger Ledger that will be prior ledger for next round
@return Whether we enter the round proposing
*/
bool
preStartRound(RCLCxLedger const & prevLedger);
/** Consensus simulation parameters
*/
ConsensusParms const&
parms() const
{
return parms_;
}
private:
//---------------------------------------------------------------------
// The following members implement the generic Consensus requirements
// and are marked private to indicate ONLY Consensus<Adaptor> will call
// them (via friendship). Since they are callled only from Consenus<Adaptor>
// methods and since RCLConsensus::consensus_ should only be accessed
// under lock, these will only be called under lock.
//
// In general, the idea is that there is only ONE thread that is running
// consensus code at anytime. The only special case is the dispatched
// onAccept call, which does not take a lock and relies on Consensus not
// changing state until a future call to startRound.
friend class Consensus<Adaptor>;
/** 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);
/** 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 tx The disputed transaction to relay.
*/
void
relay(RCLCxTx const& tx);
/** 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);
/** Relay the given tx set to peers.
@param set The TxSet to share.
*/
void
relay(RCLTxSet const& set);
/** Get the ID of the previous ledger/last closed ledger(LCL) on the
network
@param ledgerID ID of previous ledger used by consensus
@param ledger Previous ledger consensus has available
@param mode Current consensus mode
@return The id of the last closed network
@note ledgerID may not match ledger.id() if we haven't acquired
the ledger matching ledgerID from the network
*/
uint256
getPrevLedger(
uint256 ledgerID,
RCLCxLedger const& ledger,
ConsensusMode mode);
void
onModeChange(ConsensusMode before, ConsensusMode after)
{
mode_ = after;
}
/** Close the open ledger and return initial consensus position.
@param ledger the ledger we are changing to
@param closeTime When consensus closed the ledger
@param mode Current consensus mode
@return Tentative consensus result
*/
Result
onClose(
RCLCxLedger const& ledger,
NetClock::time_point const& closeTime,
ConsensusMode mode);
/** Process the accepted ledger.
@param result The result of consensus
@param prevLedger The closed ledger consensus worked from
@param closeResolution The resolution used in agreeing on an
effective closeTime
@param rawCloseTimes The unrounded closetimes of ourself and our
peers
@param mode Our participating mode at the time consensus was
declared
@param consensusJson Json representation of consensus state
*/
void
onAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration const& closeResolution,
ConsensusCloseTimes const& rawCloseTimes,
ConsensusMode const& mode,
Json::Value&& consensusJson);
/** Process the accepted ledger that was a result of simulation/force
accept.
@ref onAccept
*/
void
onForceAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration const& closeResolution,
ConsensusCloseTimes const& rawCloseTimes,
ConsensusMode const& mode,
Json::Value&& consensusJson);
/** 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);
/** Accept a new ledger based on the given transactions.
@ref onAccept
*/
void
doAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration closeResolution,
ConsensusCloseTimes const& rawCloseTimes,
ConsensusMode const& mode,
Json::Value&& consensusJson);
/** 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 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,
std::chrono::milliseconds roundTime,
CanonicalTXSet& retriableTxs);
/** Validate the given ledger and share with peers as necessary
@param ledger The ledger to validate
@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, bool proposing);
};
public:
//! Constructor
@@ -71,7 +369,8 @@ public:
LedgerMaster& ledgerMaster,
LocalTxs& localTxs,
InboundTransactions& inboundTransactions,
typename Base::clock_type const& clock,
Consensus<Adaptor>::clock_type const& clock,
ValidatorKeys const & validatorKeys,
beast::Journal journal);
RCLConsensus(RCLConsensus const&) = delete;
@@ -79,310 +378,96 @@ public:
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);
//! Whether we are validating consensus ledgers.
bool
validating() const
{
return validating_;
return adaptor_.validating();
}
bool
haveCorrectLCL() const
//! Get the number of proposing peers that participated in the previous
//! round.
std::size_t
prevProposers() const
{
return mode() != Mode::wrongLedger;
return adaptor_.prevProposers();
}
bool
proposing() const
{
return mode() == Mode::proposing;
}
/** Get duration of the previous round.
/** Get the Json state of the consensus process.
The duration of the round is the establish phase, measured from closing
the open ledger to accepting the consensus result.
Called by the consensus_info RPC.
@param full True if verbose response desired.
@return The Json state.
@return Last round duration in milliseconds
*/
std::chrono::milliseconds
prevRoundTime() const
{
return adaptor_.prevRoundTime();
}
//! @see Consensus::mode
ConsensusMode
mode() const
{
return adaptor_.mode();
}
//! @see Consensus::getJson
Json::Value
getJson(bool full) const;
//! See Consensus::startRound
//! @see Consensus::startRound
void
startRound(
NetClock::time_point const& now,
RCLCxLedger::ID const& prevLgrId,
RCLCxLedger const& prevLgr);
//! See Consensus::timerEntry
//! @see Consensus::timerEntry
void
timerEntry(NetClock::time_point const& now);
//! See Consensus::gotTxSet
//! @see Consensus::gotTxSet
void
gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet);
/** Returns validation public key */
PublicKey const&
getValidationPublicKey() const;
// @see Consensus::prevLedgerID
RCLCxLedger::ID
prevLedgerID() const
{
ScopedLockType _{mutex_};
return consensus_.prevLedgerID();
}
/** Set validation private and public key pair. */
//! @see Consensus::simulate
void
setValidationKeys(SecretKey const& valSecret, PublicKey const& valPublic);
simulate(
NetClock::time_point const& now,
boost::optional<std::chrono::milliseconds> consensusDelay);
//! @see Consensus::proposal
bool
peerProposal(
NetClock::time_point const& now,
RCLCxPeerPos const& newProposal);
ConsensusParms const &
parms() const
{
return adaptor_.parms();
}
private:
friend class Consensus<RCLConsensus, RCLCxTraits>;
// Since Consensus does not provide intrinsic thread-safety, this mutex
// guards all calls to consensus_. adaptor_ uses atomics internally
// to allow concurrent access of its data members that have getters.
mutable std::recursive_mutex mutex_;
using ScopedLockType = std::lock_guard <std::recursive_mutex>;
//-------------------------------------------------------------------------
// Consensus type requirements.
/** 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 tx The disputed transaction to relay.
*/
void
relay(RCLCxTx const& tx);
/** 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);
/** Relay the given tx set to peers.
@param set The TxSet to share.
*/
void
relay(RCLTxSet const& set);
/** Get the ID of the previous ledger/last closed ledger(LCL) on the network
@param ledgerID ID of previous ledger used by consensus
@param ledger Previous ledger consensus has available
@param mode Current consensus mode
@return The id of the last closed network
@note ledgerID may not match ledger.id() if we haven't acquired
the ledger matching ledgerID from the network
*/
uint256
getPrevLedger(
uint256 ledgerID,
RCLCxLedger const& ledger,
Mode mode);
/** Close the open ledger and return initial consensus position.
@param ledger the ledger we are changing to
@param closeTime When consensus closed the ledger
@param mode Current consensus mode
@return Tentative consensus result
*/
Result
onClose(
RCLCxLedger const& ledger,
NetClock::time_point const& closeTime,
Mode mode);
/** Process the accepted ledger.
Accepting a ledger may be expensive, so this function can dispatch
that call to another thread if desired.
@param result The result of consensus
@param prevLedger The closed ledger consensus worked from
@param closeResolution The resolution used in agreeing on an effective
closeTiem
@param rawCloseTimes The unrounded closetimes of ourself and our peers
@param mode Our participating mode at the time consensus was declared
*/
void
onAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration const & closeResolution,
CloseTimes const& rawCloseTimes,
Mode const& mode);
/** Process the accepted ledger that was a result of simulation/force
accept.
@ref onAccept
*/
void
onForceAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration const &closeResolution,
CloseTimes const& rawCloseTimes,
Mode const& mode);
//!-------------------------------------------------------------------------
// 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);
/** Accept a new ledger based on the given transactions.
@ref onAccept
*/
void
doAccept(
Result const& result,
RCLCxLedger const& prevLedger,
NetClock::duration closeResolution,
CloseTimes const& rawCloseTimes,
Mode const& mode);
/** 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 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,
std::chrono::milliseconds roundTime,
CanonicalTXSet& retriableTxs);
/** Validate the given ledger and share with peers as necessary
@param ledger The ledger to validate
@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, bool proposing);
//!-------------------------------------------------------------------------
Application& app_;
std::unique_ptr<FeeVote> feeVote_;
LedgerMaster& ledgerMaster_;
LocalTxs& localTxs_;
InboundTransactions& inboundTransactions_;
Adaptor adaptor_;
Consensus<Adaptor> consensus_;
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_;
bool validating_ = false;
bool simulating_ = false;
};
}

View File

@@ -33,14 +33,16 @@ RCLCxPeerPos::RCLCxPeerPos(
Slice const& signature,
uint256 const& suppression,
Proposal&& proposal)
: proposal_{std::move(proposal)}
, mSuppression{suppression}
, publicKey_{publicKey}
, signature_{signature}
: data_{std::make_shared<Data>(
publicKey,
signature,
suppression,
std::move(proposal))}
{
}
uint256
RCLCxPeerPos::getSigningHash() const
RCLCxPeerPos::signingHash() const
{
return sha512Half(
HashPrefix::proposal,
@@ -53,16 +55,18 @@ RCLCxPeerPos::getSigningHash() const
bool
RCLCxPeerPos::checkSign() const
{
return verifyDigest(publicKey_, getSigningHash(), signature_, false);
return verifyDigest(
publicKey(), signingHash(), signature(), false);
}
Json::Value
RCLCxPeerPos::getJson() const
{
auto ret = proposal().getJson();
if (publicKey_.size())
ret[jss::peer_id] = toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey_);
if (publicKey().size())
ret[jss::peer_id] = toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey());
return ret;
}
@@ -87,4 +91,16 @@ proposalUniqueId(
return s.getSHA512Half();
}
RCLCxPeerPos::Data::Data(
PublicKey const& publicKey,
Slice const& signature,
uint256 const& suppress,
Proposal&& proposal)
: publicKey_{publicKey}
, signature_{signature}
, supression_{suppress}
, proposal_{std::move(proposal)}
{
}
} // ripple

View File

@@ -36,19 +36,12 @@ namespace ripple {
/** A peer's signed, proposed position for use in RCLConsensus.
Carries a ConsensusProposal signed by a peer.
Carries a ConsensusProposal signed by a peer. Provides value semantics
but manages shared storage of the peer position internally.
*/
class RCLCxPeerPos : public CountedObject<RCLCxPeerPos>
class RCLCxPeerPos
{
public:
static char const*
getCountedObjectName()
{
return "RCLCxPeerPos";
}
using pointer = std::shared_ptr<RCLCxPeerPos>;
using ref = const pointer&;
//< The type of the proposed position
using Proposal = ConsensusProposal<NodeID, uint256, uint256>;
@@ -70,7 +63,7 @@ public:
//! Create the signing hash for the proposal
uint256
getSigningHash() const;
signingHash() const;
//! Verify the signing hash of the proposal
bool
@@ -78,45 +71,59 @@ public:
//! Signature of the proposal (not necessarily verified)
Slice
getSignature() const
signature() const
{
return signature_;
return data_->signature_;
}
//! Public key of peer that sent the proposal
PublicKey const&
getPublicKey() const
publicKey() const
{
return publicKey_;
return data_->publicKey_;
}
//! ?????
uint256 const&
getSuppressionID() const
suppressionID() const
{
return mSuppression;
return data_->supression_;
}
//! The consensus proposal
Proposal const&
Proposal const &
proposal() const
{
return proposal_;
return data_->proposal_;
}
/// @cond Ignore
//! Add a conversion operator to conform to the Consensus interface
operator Proposal const&() const
{
return proposal_;
}
/// @endcond
//! JSON representation of proposal
Json::Value
getJson() const;
private:
struct Data : public CountedObject<Data>
{
PublicKey publicKey_;
Buffer signature_;
uint256 supression_;
Proposal proposal_;
Data(
PublicKey const& publicKey,
Slice const& signature,
uint256 const& suppress,
Proposal&& proposal);
static char const*
getCountedObjectName()
{
return "RCLCxPeerPos::Data";
}
};
std::shared_ptr<Data> data_;
template <class Hasher>
void
hash_append(Hasher& h) const
@@ -129,10 +136,6 @@ private:
hash_append(h, proposal().position());
}
Proposal proposal_;
uint256 mSuppression;
PublicKey publicKey_;
Buffer signature_;
};
/** Calculate a unique identifier for a signed proposal.

View File

@@ -42,6 +42,7 @@
#include <ripple/app/misc/SHAMapStore.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/misc/ValidatorSite.h>
#include <ripple/app/misc/ValidatorKeys.h>
#include <ripple/app/paths/PathRequests.h>
#include <ripple/app/tx/apply.h>
#include <ripple/basics/ResolverAsio.h>
@@ -305,6 +306,7 @@ public:
std::unique_ptr <CollectorManager> m_collectorManager;
CachedSLEs cachedSLEs_;
std::pair<PublicKey, SecretKey> nodeIdentity_;
ValidatorKeys const validatorKeys_;
std::unique_ptr <Resource::Manager> m_resourceManager;
@@ -394,8 +396,8 @@ public:
, m_collectorManager (CollectorManager::New (
config_->section (SECTION_INSIGHT), logs_->journal("Collector")))
, cachedSLEs_ (std::chrono::minutes(1), stopwatch())
, validatorKeys_(*config_, m_journal)
, m_resourceManager (Resource::make_Manager (
m_collectorManager->collector(), logs_->journal("Resource")))
@@ -445,7 +447,7 @@ public:
, m_networkOPs (make_NetworkOPs (*this, stopwatch(),
config_->standalone(), config_->NETWORK_QUORUM, config_->START_VALID,
*m_jobQueue, *m_ledgerMaster, *m_jobQueue,
*m_jobQueue, *m_ledgerMaster, *m_jobQueue, validatorKeys_,
logs_->journal("NetworkOPs")))
, cluster_ (std::make_unique<Cluster> (
@@ -570,6 +572,13 @@ public:
return nodeIdentity_;
}
virtual
PublicKey const &
getValidationPublicKey() const override
{
return validatorKeys_.publicKey;
}
NetworkOPs& getOPs () override
{
return *m_networkOPs;
@@ -1086,38 +1095,11 @@ bool ApplicationImp::setup()
}
{
PublicKey valPublic;
SecretKey valSecret;
std::string manifest;
if (config().exists (SECTION_VALIDATOR_TOKEN))
{
if (auto const token = ValidatorToken::make_ValidatorToken (
config().section (SECTION_VALIDATOR_TOKEN).lines ()))
{
valSecret = token->validationSecret;
valPublic = derivePublicKey (KeyType::secp256k1, valSecret);
manifest = std::move(token->manifest);
}
else
{
JLOG(m_journal.fatal()) <<
"Invalid entry in validator token configuration.";
return false;
}
}
else if (config().exists (SECTION_VALIDATION_SEED))
{
auto const seed = parseBase58<Seed>(
config().section (SECTION_VALIDATION_SEED).lines ().front());
if (!seed)
Throw<std::runtime_error> (
"Invalid seed specified in [" SECTION_VALIDATION_SEED "]");
valSecret = generateSecretKey (KeyType::secp256k1, *seed);
valPublic = derivePublicKey (KeyType::secp256k1, valSecret);
}
if(validatorKeys_.configInvalid())
return false;
if (!validatorManifests_->load (
getWalletDB (), "ValidatorManifests", manifest,
getWalletDB (), "ValidatorManifests", validatorKeys_.manifest,
config().section (SECTION_VALIDATOR_KEY_REVOCATION).values ()))
{
JLOG(m_journal.fatal()) << "Invalid configured validator manifest.";
@@ -1127,11 +1109,9 @@ bool ApplicationImp::setup()
publisherManifests_->load (
getWalletDB (), "PublisherManifests");
m_networkOPs->setValidationKeys (valSecret, valPublic);
// Setup trusted validators
if (!validators_->load (
valPublic,
validatorKeys_.publicKey,
config().section (SECTION_VALIDATORS).values (),
config().section (SECTION_VALIDATOR_LIST_KEYS).values ()))
{

View File

@@ -150,6 +150,10 @@ public:
std::pair<PublicKey, SecretKey> const&
nodeIdentity () = 0;
virtual
PublicKey const &
getValidationPublicKey() const = 0;
virtual Resource::Manager& getResourceManager () = 0;
virtual PathRequests& getPathRequests () = 0;
virtual SHAMapStore& getSHAMapStore () = 0;

View File

@@ -36,6 +36,7 @@
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/misc/ValidatorKeys.h>
#include <ripple/app/misc/ValidatorList.h>
#include <ripple/app/misc/impl/AccountTxPaging.h>
#include <ripple/app/tx/apply.h>
@@ -187,7 +188,7 @@ public:
Application& app, clock_type& clock, bool standalone,
std::size_t network_quorum, bool start_valid, JobQueue& job_queue,
LedgerMaster& ledgerMaster, Stoppable& parent,
beast::Journal journal)
ValidatorKeys const & validatorKeys, beast::Journal journal)
: NetworkOPs (parent)
, app_ (app)
, m_clock (clock)
@@ -198,14 +199,15 @@ public:
, m_amendmentBlocked (false)
, m_heartbeatTimer (this)
, m_clusterTimer (this)
, mConsensus (std::make_shared<RCLConsensus>(app,
, mConsensus (app,
make_FeeVote(setup_FeeVote (app_.config().section ("voting")),
app_.logs().journal("FeeVote")),
ledgerMaster,
*m_localTX,
app.getInboundTransactions(),
stopwatch(),
app_.logs().journal("LedgerConsensus")))
validatorKeys,
app_.logs().journal("LedgerConsensus"))
, m_ledgerMaster (ledgerMaster)
, m_job_queue (job_queue)
, m_standalone (standalone)
@@ -296,7 +298,7 @@ public:
// Ledger proposal/close functions.
void processTrustedProposal (
RCLCxPeerPos::pointer proposal,
RCLCxPeerPos proposal,
std::shared_ptr<protocol::TMProposeSet> set,
NodeID const &node) override;
@@ -323,7 +325,6 @@ private:
std::shared_ptr<Ledger const> const& newLCL);
bool checkLastClosedLedger (
const Overlay::PeerSequence&, uint256& networkClosed);
void tryStartConsensus ();
public:
bool beginConsensus (uint256 const& networkClosed) override;
@@ -360,15 +361,7 @@ public:
}
void setAmendmentBlocked () override;
void consensusViewChange () override;
PublicKey const& getValidationPublicKey () const override
{
return mConsensus->getValidationPublicKey ();
}
void setValidationKeys (
SecretKey const& valSecret, PublicKey const& valPublic) override
{
mConsensus->setValidationKeys (valSecret, valPublic);
}
Json::Value getConsensusInfo () override;
Json::Value getServerInfo (bool human, bool admin) override;
void clearLedgerFetch () override;
@@ -549,7 +542,7 @@ private:
DeadlineTimer m_clusterTimer;
JobCounter jobCounter_;
std::shared_ptr<RCLConsensus> mConsensus;
RCLConsensus mConsensus;
LedgerMaster& m_ledgerMaster;
std::shared_ptr<InboundLedger> mAcquiringLedger;
@@ -651,7 +644,7 @@ void NetworkOPsImp::setStateTimer ()
void NetworkOPsImp::setHeartbeatTimer ()
{
m_heartbeatTimer.setExpiration (mConsensus->parms().ledgerGRANULARITY);
m_heartbeatTimer.setExpiration (mConsensus.parms().ledgerGRANULARITY);
}
void NetworkOPsImp::setClusterTimer ()
@@ -697,7 +690,7 @@ void NetworkOPsImp::processHeartbeatTimer ()
<< "Node count (" << numPeers << ") "
<< "has fallen below quorum (" << m_network_quorum << ").";
}
// We do not call mConsensus->timerEntry until there
// We do not call mConsensus.timerEntry until there
// are enough peers providing meaningful inputs to consensus
setHeartbeatTimer ();
@@ -720,7 +713,7 @@ void NetworkOPsImp::processHeartbeatTimer ()
}
mConsensus->timerEntry (app_.timeKeeper().closeTime());
mConsensus.timerEntry (app_.timeKeeper().closeTime());
setHeartbeatTimer ();
}
@@ -775,13 +768,17 @@ void NetworkOPsImp::processClusterTimer ()
std::string NetworkOPsImp::strOperatingMode () const
{
if (mMode == omFULL && mConsensus->haveCorrectLCL())
if (mMode == omFULL)
{
if (mConsensus->proposing ())
return "proposing";
auto const mode = mConsensus.mode();
if (mode != ConsensusMode::wrongLedger)
{
if (mode == ConsensusMode::proposing)
return "proposing";
if (mConsensus->validating ())
return "validating";
if (mConsensus.validating())
return "validating";
}
}
return states_[mMode];
@@ -1252,46 +1249,6 @@ public:
}
};
void NetworkOPsImp::tryStartConsensus ()
{
uint256 networkClosed;
bool ledgerChange = checkLastClosedLedger (
app_.overlay ().getActivePeers (), networkClosed);
if (networkClosed.isZero ())
return;
// WRITEME: Unless we are in omFULL and in the process of doing a consensus,
// we must count how many nodes share our LCL, how many nodes disagree with
// our LCL, and how many validations our LCL has. We also want to check
// timing to make sure there shouldn't be a newer LCL. We need this
// information to do the next three tests.
if (((mMode == omCONNECTED) || (mMode == omSYNCING)) && !ledgerChange)
{
// Count number of peers that agree with us and UNL nodes whose
// validations we have for LCL. If the ledger is good enough, go to
// omTRACKING - TODO
if (!mNeedNetworkLedger)
setMode (omTRACKING);
}
if (((mMode == omCONNECTED) || (mMode == omTRACKING)) && !ledgerChange)
{
// check if the ledger is good enough to go to omFULL
// Note: Do not go to omFULL if we don't have the previous ledger
// check if the ledger is bad enough to go to omCONNECTED -- TODO
auto current = m_ledgerMaster.getCurrentLedger();
if (app_.timeKeeper().now() <
(current->info().parentCloseTime + 2* current->info().closeTimeResolution))
{
setMode (omFULL);
}
}
beginConsensus (networkClosed);
}
bool NetworkOPsImp::checkLastClosedLedger (
const Overlay::PeerSequence& peerList, uint256& networkClosed)
{
@@ -1527,7 +1484,7 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
app_.validators().onConsensusStart (
app_.getValidations().getCurrentPublicKeys ());
mConsensus->startRound (
mConsensus.startRound (
app_.timeKeeper().closeTime(),
networkClosed,
prevLedger);
@@ -1538,19 +1495,19 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
uint256 NetworkOPsImp::getConsensusLCL ()
{
return mConsensus->prevLedgerID ();
return mConsensus.prevLedgerID ();
}
void NetworkOPsImp::processTrustedProposal (
RCLCxPeerPos::pointer peerPos,
RCLCxPeerPos peerPos,
std::shared_ptr<protocol::TMProposeSet> set,
NodeID const& node)
{
mConsensus->storeProposal (peerPos, node);
if (mConsensus->peerProposal (
app_.timeKeeper().closeTime(), peerPos->proposal()))
app_.overlay().relay(*set, peerPos->getSuppressionID());
if (mConsensus.peerProposal(
app_.timeKeeper().closeTime(), peerPos))
{
app_.overlay().relay(*set, peerPos.suppressionID());
}
else
JLOG(m_journal.info()) << "Not relaying trusted proposal";
}
@@ -1573,7 +1530,7 @@ NetworkOPsImp::mapComplete (
// We acquired it because consensus asked us to
if (fromAcquire)
mConsensus->gotTxSet (
mConsensus.gotTxSet (
app_.timeKeeper().closeTime(),
RCLTxSet{map});
}
@@ -1591,7 +1548,42 @@ void NetworkOPsImp::endConsensus ()
}
}
tryStartConsensus();
uint256 networkClosed;
bool ledgerChange = checkLastClosedLedger (
app_.overlay ().getActivePeers (), networkClosed);
if (networkClosed.isZero ())
return;
// WRITEME: Unless we are in omFULL and in the process of doing a consensus,
// we must count how many nodes share our LCL, how many nodes disagree with
// our LCL, and how many validations our LCL has. We also want to check
// timing to make sure there shouldn't be a newer LCL. We need this
// information to do the next three tests.
if (((mMode == omCONNECTED) || (mMode == omSYNCING)) && !ledgerChange)
{
// Count number of peers that agree with us and UNL nodes whose
// validations we have for LCL. If the ledger is good enough, go to
// omTRACKING - TODO
if (!mNeedNetworkLedger)
setMode (omTRACKING);
}
if (((mMode == omCONNECTED) || (mMode == omTRACKING)) && !ledgerChange)
{
// check if the ledger is good enough to go to omFULL
// Note: Do not go to omFULL if we don't have the previous ledger
// check if the ledger is bad enough to go to omCONNECTED -- TODO
auto current = m_ledgerMaster.getCurrentLedger();
if (app_.timeKeeper().now() <
(current->info().parentCloseTime + 2* current->info().closeTimeResolution))
{
setMode (omFULL);
}
}
beginConsensus (networkClosed);
}
void NetworkOPsImp::consensusViewChange ()
@@ -2125,7 +2117,7 @@ bool NetworkOPsImp::recvValidation (
Json::Value NetworkOPsImp::getConsensusInfo ()
{
return mConsensus->getJson (true);
return mConsensus.getJson (true);
}
Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
@@ -2151,7 +2143,7 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
if (admin)
{
if (getValidationPublicKey().size ())
if (!app_.getValidationPublicKey().empty())
{
info[jss::pubkey_validator] = toBase58 (
TokenType::TOKEN_NODE_PUBLIC,
@@ -2181,23 +2173,23 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
info[jss::peers] = Json::UInt (app_.overlay ().size ());
Json::Value lastClose = Json::objectValue;
lastClose[jss::proposers] = Json::UInt(mConsensus->prevProposers());
lastClose[jss::proposers] = Json::UInt(mConsensus.prevProposers());
if (human)
{
lastClose[jss::converge_time_s] =
std::chrono::duration<double>{
mConsensus->prevRoundTime()}.count();
mConsensus.prevRoundTime()}.count();
}
else
{
lastClose[jss::converge_time] =
Json::Int (mConsensus->prevRoundTime().count());
Json::Int (mConsensus.prevRoundTime().count());
}
info[jss::last_close] = lastClose;
// info[jss::consensus] = mConsensus->getJson();
// info[jss::consensus] = mConsensus.getJson();
if (admin)
info[jss::load] = m_job_queue.getJson ();
@@ -2771,7 +2763,7 @@ std::uint32_t NetworkOPsImp::acceptLedger (
// FIXME Could we improve on this and remove the need for a specialized
// API in Consensus?
beginConsensus (m_ledgerMaster.getClosedLedger()->info().hash);
mConsensus->simulate (app_.timeKeeper().closeTime(), consensusDelay);
mConsensus.simulate (app_.timeKeeper().closeTime(), consensusDelay);
return m_ledgerMaster.getCurrentLedger ()->info().seq;
}
@@ -3368,10 +3360,10 @@ std::unique_ptr<NetworkOPs>
make_NetworkOPs (Application& app, NetworkOPs::clock_type& clock, bool standalone,
std::size_t network_quorum, bool startvalid,
JobQueue& job_queue, LedgerMaster& ledgerMaster,
Stoppable& parent, beast::Journal journal)
Stoppable& parent, ValidatorKeys const & validatorKeys, beast::Journal journal)
{
return std::make_unique<NetworkOPsImp> (app, clock, standalone, network_quorum,
startvalid, job_queue, ledgerMaster, parent, journal);
startvalid, job_queue, ledgerMaster, parent, validatorKeys, journal);
}
} // ripple

View File

@@ -41,6 +41,7 @@ namespace ripple {
class Peer;
class LedgerMaster;
class Transaction;
class ValidatorKeys;
// This is the primary interface into the "client" portion of the program.
// Code that wants to do normal operations on the network such as
@@ -150,7 +151,7 @@ public:
//--------------------------------------------------------------------------
// ledger proposal/close functions
virtual void processTrustedProposal (RCLCxPeerPos::pointer peerPos,
virtual void processTrustedProposal (RCLCxPeerPos peerPos,
std::shared_ptr<protocol::TMProposeSet> set,
NodeID const& node) = 0;
@@ -174,9 +175,6 @@ public:
virtual bool isAmendmentBlocked () = 0;
virtual void setAmendmentBlocked () = 0;
virtual void consensusViewChange () = 0;
virtual PublicKey const& getValidationPublicKey () const = 0;
virtual void setValidationKeys (
SecretKey const& valSecret, PublicKey const& valPublic) = 0;
virtual Json::Value getConsensusInfo () = 0;
virtual Json::Value getServerInfo (bool human, bool admin) = 0;
@@ -242,7 +240,7 @@ std::unique_ptr<NetworkOPs>
make_NetworkOPs (Application& app, NetworkOPs::clock_type& clock, bool standalone,
std::size_t network_quorum, bool start_valid,
JobQueue& job_queue, LedgerMaster& ledgerMaster,
Stoppable& parent, beast::Journal journal);
Stoppable& parent, ValidatorKeys const & validatorKeys, beast::Journal journal);
} // ripple

View File

@@ -0,0 +1,54 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2017 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_APP_MISC_VALIDATOR_KEYS_H_INCLUDED
#define RIPPLE_APP_MISC_VALIDATOR_KEYS_H_INCLUDED
#include <ripple/beast/utility/Journal.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <string>
namespace ripple {
class Config;
/** Validator keys and manifest as set in configuration file. Values will be
empty if not configured as a validator or not configured with a manifest.
*/
class ValidatorKeys
{
public:
PublicKey publicKey;
SecretKey secretKey;
std::string manifest;
ValidatorKeys(Config const& config, beast::Journal j);
bool configInvalid() const
{
return configInvalid_;
}
private:
bool configInvalid_ = false; //< Set to true if config was invalid
};
} // namespace ripple
#endif

View File

@@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
/*
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/misc/ValidatorKeys.h>
#include <ripple/app/misc/Manifest.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/core/ConfigSections.h>
namespace ripple {
ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j)
{
if (config.exists(SECTION_VALIDATOR_TOKEN) &&
config.exists(SECTION_VALIDATION_SEED))
{
configInvalid_ = true;
JLOG(j.fatal()) << "Cannot specify both [" SECTION_VALIDATION_SEED
"] and [" SECTION_VALIDATOR_TOKEN "]";
return;
}
if (config.exists(SECTION_VALIDATOR_TOKEN))
{
if (auto const token = ValidatorToken::make_ValidatorToken(
config.section(SECTION_VALIDATOR_TOKEN).lines()))
{
secretKey = token->validationSecret;
publicKey = derivePublicKey(KeyType::secp256k1, secretKey);
manifest = std::move(token->manifest);
}
else
{
configInvalid_ = true;
JLOG(j.fatal())
<< "Invalid token specified in [" SECTION_VALIDATOR_TOKEN "]";
}
}
else if (config.exists(SECTION_VALIDATION_SEED))
{
auto const seed = parseBase58<Seed>(
config.section(SECTION_VALIDATION_SEED).lines().front());
if (!seed)
{
configInvalid_ = true;
JLOG(j.fatal()) <<
"Invalid seed specified in [" SECTION_VALIDATION_SEED "]";
}
else
{
secretKey = generateSecretKey(KeyType::secp256k1, *seed);
publicKey = derivePublicKey(KeyType::secp256k1, secretKey);
}
}
}
} // namespace ripple

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,242 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2017 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONSENSUS_CONSENSUS_TYPES_H_INCLUDED
#define RIPPLE_CONSENSUS_CONSENSUS_TYPES_H_INCLUDED
#include <ripple/basics/chrono.h>
#include <ripple/consensus/ConsensusProposal.h>
#include <ripple/consensus/DisputedTx.h>
#include <chrono>
#include <map>
namespace ripple {
/** Represents how a node currently participates in Consensus.
A node participates in consensus in varying modes, depending on how
the node was configured by its operator and how well it stays in sync
with the network during consensus.
@code
proposing observing
\ /
\---> wrongLedger <---/
^
|
|
v
switchedLedger
@endcode
We enter the round proposing or observing. If we detect we are working
on the wrong prior ledger, we go to wrongLedger and attempt to acquire
the right one. Once we acquire the right one, we go to the switchedLedger
mode. It is possible we fall behind again and find there is a new better
ledger, moving back and forth between wrongLedger and switchLedger as
we attempt to catch up.
*/
enum class ConsensusMode {
//! We are normal participant in consensus and propose our position
proposing,
//! We are observing peer positions, but not proposing our position
observing,
//! We have the wrong ledger and are attempting to acquire it
wrongLedger,
//! We switched ledgers since we started this consensus round but are now
//! running on what we believe is the correct ledger. This mode is as
//! if we entered the round observing, but is used to indicate we did
//! have the wrongLedger at some point.
switchedLedger
};
inline std::string
to_string(ConsensusMode m)
{
switch (m)
{
case ConsensusMode::proposing:
return "proposing";
case ConsensusMode::observing:
return "observing";
case ConsensusMode::wrongLedger:
return "wrongLedger";
case ConsensusMode::switchedLedger:
return "switchedLedger";
default:
return "unknown";
}
}
/** Phases of consensus for a single ledger round.
@code
"close" "accept"
open ------- > establish ---------> accepted
^ | |
|---------------| |
^ "startRound" |
|------------------------------------|
@endcode
The typical transition goes from open to establish to accepted and
then a call to startRound begins the process anew. However, if a wrong prior
ledger is detected and recovered during the establish or accept phase,
consensus will internally go back to open (see Consensus::handleWrongLedger).
*/
enum class ConsensusPhase {
//! We haven't closed our ledger yet, but others might have
open,
//! Establishing consensus by exchanging proposals with our peers
establish,
//! We have accepted a new last closed ledger and are waiting on a call
//! to startRound to begin the next consensus round. No changes
//! to consensus phase occur while in this phase.
accepted,
};
inline std::string
to_string(ConsensusPhase p)
{
switch (p)
{
case ConsensusPhase::open:
return "open";
case ConsensusPhase::establish:
return "establish";
case ConsensusPhase::accepted:
return "accepted";
default:
return "unknown";
}
}
/** Measures the duration of phases of consensus
*/
class ConsensusTimer
{
using time_point = std::chrono::steady_clock::time_point;
time_point start_;
std::chrono::milliseconds dur_;
public:
std::chrono::milliseconds
read() const
{
return dur_;
}
void
tick(std::chrono::milliseconds fixed)
{
dur_ += fixed;
}
void
reset(time_point tp)
{
start_ = tp;
dur_ = std::chrono::milliseconds{0};
}
void
tick(time_point tp)
{
using namespace std::chrono;
dur_ = duration_cast<milliseconds>(tp - start_);
}
};
/** Stores the set of initial close times
The initial consensus proposal from each peer has that peer's view of
when the ledger closed. This object stores all those close times for
analysis of clock drift between peerss.
*/
struct ConsensusCloseTimes
{
//! Close time estimates, keep ordered for predictable traverse
std::map<NetClock::time_point, int> peers;
//! Our close time estimate
NetClock::time_point 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
};
/** Encapsulates the result of consensus.
Stores all relevant data for the outcome of consensus on a single
ledger.
@tparam Traits Traits class defining the concrete consensus types used
by the application.
*/
template <class Traits>
struct ConsensusResult
{
using Ledger_t = typename Traits::Ledger_t;
using TxSet_t = typename Traits::TxSet_t;
using NodeID_t = typename Traits::NodeID_t;
using Tx_t = typename TxSet_t::Tx;
using Proposal_t = ConsensusProposal<
NodeID_t,
typename Ledger_t::ID,
typename TxSet_t::ID>;
using Dispute_t = DisputedTx<Tx_t, NodeID_t>;
ConsensusResult(TxSet_t&& s, Proposal_t&& p)
: set{std::move(s)}, position{std::move(p)}
{
assert(set.id() == position.position());
}
//! The set of transactions consensus agrees go in the ledger
TxSet_t set;
//! Our proposed position on transactions/close time
Proposal_t position;
//! Transactions which are under dispute with our peers
hash_map<typename Tx_t::ID, Dispute_t> disputes;
// Set of TxSet ids we have already compared/created disputes
hash_set<typename TxSet_t::ID> compares;
// Measures the duration of the establish phase for this consensus round
ConsensusTimer roundTime;
// Indicates state in which consensus ended. Once in the accept phase
// will be either Yes or MovedOn
ConsensusState state = ConsensusState::No;
// The number of peers proposing during the round
std::size_t proposers = 0;
};
} // namespace ripple
#endif

View File

@@ -1094,7 +1094,7 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMTransaction> const& m)
flags |= SF_TRUSTED;
}
if (! app_.getOPs().getValidationPublicKey().size())
if (app_.getValidationPublicKey().empty())
{
// For now, be paranoid and have each validator
// check each transaction, regardless of source
@@ -1256,8 +1256,8 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMProposeSet> const& m)
return;
}
if (app_.getOPs().getValidationPublicKey().size() &&
publicKey == app_.getOPs().getValidationPublicKey())
if (!app_.getValidationPublicKey().empty() &&
publicKey == app_.getValidationPublicKey())
{
JLOG(p_journal_.trace()) << "Proposal: self";
return;
@@ -1283,7 +1283,7 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMProposeSet> const& m)
JLOG(p_journal_.trace()) <<
"Proposal: " << (isTrusted ? "trusted" : "UNTRUSTED");
auto proposal = std::make_shared<RCLCxPeerPos> (
auto proposal = RCLCxPeerPos(
publicKey, signature, suppression,
RCLCxPeerPos::Proposal{prevLedger, set.proposeseq (), proposeHash, closeTime,
app_.timeKeeper().closeTime(),calcNodeID(publicKey)});
@@ -1889,7 +1889,7 @@ PeerImp::checkTransaction (int flags,
void
PeerImp::checkPropose (Job& job,
std::shared_ptr <protocol::TMProposeSet> const& packet,
RCLCxPeerPos::pointer peerPos)
RCLCxPeerPos peerPos)
{
bool isTrusted = (job.getType () == jtPROPOSAL_t);
@@ -1899,7 +1899,7 @@ PeerImp::checkPropose (Job& job,
assert (packet);
protocol::TMProposeSet& set = *packet;
if (! cluster() && !peerPos->checkSign ())
if (! cluster() && !peerPos.checkSign ())
{
JLOG(p_journal_.warn()) <<
"Proposal fails sig check";
@@ -1914,12 +1914,12 @@ PeerImp::checkPropose (Job& job,
}
else
{
if (app_.getOPs().getConsensusLCL() == peerPos->proposal().prevLedger())
if (app_.getOPs().getConsensusLCL() == peerPos.proposal().prevLedger())
{
// relay untrusted proposal
JLOG(p_journal_.trace()) <<
"relaying UNTRUSTED proposal";
overlay_.relay(set, peerPos->getSuppressionID());
overlay_.relay(set, peerPos.suppressionID());
}
else
{

View File

@@ -451,7 +451,7 @@ private:
void
checkPropose (Job& job,
std::shared_ptr<protocol::TMProposeSet> const& packet,
RCLCxPeerPos::pointer peerPos);
RCLCxPeerPos peerPos);
void
checkValidation (STValidation::pointer val,

View File

@@ -87,6 +87,12 @@ public:
return size_;
}
bool
empty() const noexcept
{
return size_ == 0;
}
Slice
slice() const noexcept
{

View File

@@ -33,3 +33,4 @@
#include <ripple/app/misc/impl/TxQ.cpp>
#include <ripple/app/misc/impl/ValidatorList.cpp>
#include <ripple/app/misc/impl/ValidatorSite.cpp>
#include <ripple/app/misc/impl/ValidatorKeys.cpp>

View File

@@ -0,0 +1,150 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright 2017 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/app/misc/ValidatorKeys.h>
#include <ripple/beast/unit_test.h>
#include <ripple/core/Config.h>
#include <ripple/core/ConfigSections.h>
#include <string>
namespace ripple {
namespace test {
class ValidatorKeys_test : public beast::unit_test::suite
{
// Used with [validation_seed]
const std::string seed = "shUwVw52ofnCUX5m7kPTKzJdr4HEH";
// Used with [validation_token]
const std::string tokenSecretStr =
"paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi";
const std::vector<std::string> tokenBlob = {
" "
"eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n",
" \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl "
" \n",
"\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE"
"\n",
"\t "
"hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\t "
"\t\n",
"bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2\n",
"hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1\n",
"NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj\n",
"VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n"};
const std::string tokenManifest =
"JAAAAAFxIe1FtwmimvGtH2iCcMJqC9gVFKilGfw1/vCxHXXLplc2GnMhAkE1agqXxBwD"
"wDbID6OMSYuM0FDAlpAgNk8SKFn7MO2fdkcwRQIhAOngu9sAKqXYouJ+l2V0W+sAOkVB"
"+ZRS6PShlJAfUsXfAiBsVJGesaadOJc/aAZokS1vymGmVrlHPKWX3Yywu6in8HASQKPu"
"gBD67kMaRFGvmpATHlGKJdvDFlWPYy5AqDedFv5TJa2w0i21eq3MYywLVJZnFOr7C0kw"
"2AiTzSCjIzditQ8=";
public:
void
run() override
{
beast::Journal j;
// Keys when using [validation_seed]
auto const seedSecretKey =
generateSecretKey(KeyType::secp256k1, *parseBase58<Seed>(seed));
auto const seedPublicKey =
derivePublicKey(KeyType::secp256k1, seedSecretKey);
// Keys when using [validation_token]
auto const tokenSecretKey = *parseBase58<SecretKey>(
TokenType::TOKEN_NODE_PRIVATE, tokenSecretStr);
auto const tokenPublicKey =
derivePublicKey(KeyType::secp256k1, tokenSecretKey);
{
// No config -> no key but valid
Config c;
ValidatorKeys k{c, j};
BEAST_EXPECT(k.publicKey.size() == 0);
BEAST_EXPECT(k.manifest.empty());
BEAST_EXPECT(!k.configInvalid());
}
{
// validation seed section -> empty manifest and valid seeds
Config c;
c.section(SECTION_VALIDATION_SEED).append(seed);
ValidatorKeys k{c, j};
BEAST_EXPECT(k.publicKey == seedPublicKey);
BEAST_EXPECT(k.secretKey == seedSecretKey);
BEAST_EXPECT(k.manifest.empty());
BEAST_EXPECT(!k.configInvalid());
}
{
// validation seed bad seed -> invalid
Config c;
c.section(SECTION_VALIDATION_SEED).append("badseed");
ValidatorKeys k{c, j};
BEAST_EXPECT(k.configInvalid());
BEAST_EXPECT(k.publicKey.size() == 0);
BEAST_EXPECT(k.manifest.empty());
}
{
// validator token
Config c;
c.section(SECTION_VALIDATOR_TOKEN).append(tokenBlob);
ValidatorKeys k{c, j};
BEAST_EXPECT(k.publicKey == tokenPublicKey);
BEAST_EXPECT(k.secretKey == tokenSecretKey);
BEAST_EXPECT(k.manifest == tokenManifest);
BEAST_EXPECT(!k.configInvalid());
}
{
// invalid validator token
Config c;
c.section(SECTION_VALIDATOR_TOKEN).append("badtoken");
ValidatorKeys k{c, j};
BEAST_EXPECT(k.configInvalid());
BEAST_EXPECT(k.publicKey.size() == 0);
BEAST_EXPECT(k.manifest.empty());
}
{
// Cannot specify both
Config c;
c.section(SECTION_VALIDATION_SEED).append(seed);
c.section(SECTION_VALIDATOR_TOKEN).append(tokenBlob);
ValidatorKeys k{c, j};
BEAST_EXPECT(k.configInvalid());
BEAST_EXPECT(k.publicKey.size() == 0);
BEAST_EXPECT(k.manifest.empty());
}
}
}; // namespace test
BEAST_DEFINE_TESTSUITE(ValidatorKeys, app, ripple);
} // namespace test
} // namespace ripple

View File

@@ -21,7 +21,8 @@
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
#include <ripple/consensus/Consensus.h>
#include <ripple/consensus/ConsensusProposal.h>
#include <test/csf/Ledger.h>
#include <test/csf/Tx.h>
#include <test/csf/UNL.h>
@@ -115,20 +116,43 @@ public:
directly from the generic types.
*/
using Proposal = ConsensusProposal<PeerID, Ledger::ID, TxSetType>;
class PeerPosition
{
public:
PeerPosition(Proposal const & p)
: proposal_(p)
{
}
struct Traits
Proposal const&
proposal() const
{
return proposal_;
}
Json::Value
getJson() const
{
return proposal_.getJson();
}
private:
Proposal proposal_;
};
/** Represents a single node participating in the consensus process.
It implements the Adaptor requirements of generic Consensus.
*/
struct Peer
{
using Ledger_t = Ledger;
using NodeID_t = PeerID;
using TxSet_t = TxSet;
};
using PeerPosition_t = PeerPosition;
using Result = ConsensusResult<Peer>;
/** Represents a single node participating in the consensus process.
It implements the Callbacks required by Consensus.
*/
struct Peer : public Consensus<Peer, Traits>
{
using Base = Consensus<Peer, Traits>;
Consensus<Peer> consensus;
//! Our unique ID
PeerID id;
@@ -172,12 +196,17 @@ struct Peer : public Consensus<Peer, Traits>
bool validating_ = true;
bool proposing_ = true;
ConsensusParms parms_;
std::size_t prevProposers_ = 0;
std::chrono::milliseconds prevRoundTime_;
//! All peers start from the default constructed ledger
Peer(PeerID i, BasicNetwork<Peer*>& n, UNL const& u, ConsensusParms p)
: Consensus<Peer, Traits>(n.clock(), p, beast::Journal{})
: consensus(n.clock(), *this, beast::Journal{})
, id{i}
, net{n}
, unl(u)
, parms_(p)
{
ledgers[lastClosedLedger.id()] = lastClosedLedger;
}
@@ -210,12 +239,6 @@ struct Peer : public Consensus<Peer, Traits>
return nullptr;
}
auto const&
proposals(Ledger::ID const& ledgerHash)
{
return peerPositions_[ledgerHash];
}
TxSet const*
acquireTxSet(TxSet::ID const& setId)
{
@@ -245,17 +268,17 @@ struct Peer : public Consensus<Peer, Traits>
}
Result
onClose(Ledger const& prevLedger, NetClock::time_point closeTime, Mode mode)
onClose(Ledger const& prevLedger, NetClock::time_point closeTime, ConsensusMode mode)
{
TxSet res{openTxs};
return Result{TxSet{openTxs},
Proposal{prevLedger.id(),
return Result(TxSet{openTxs},
Proposal(prevLedger.id(),
Proposal::seqJoin,
res.id(),
closeTime,
now(),
id}};
id));
}
void
@@ -263,10 +286,17 @@ struct Peer : public Consensus<Peer, Traits>
Result const& result,
Ledger const& prevLedger,
NetClock::duration const& closeResolution,
CloseTimes const& rawCloseTimes,
Mode const& mode)
ConsensusCloseTimes const& rawCloseTimes,
ConsensusMode const& mode,
Json::Value && consensusJson)
{
onAccept(result, prevLedger, closeResolution, rawCloseTimes, mode);
onAccept(
result,
prevLedger,
closeResolution,
rawCloseTimes,
mode,
std::move(consensusJson));
}
void
@@ -274,8 +304,9 @@ struct Peer : public Consensus<Peer, Traits>
Result const& result,
Ledger const& prevLedger,
NetClock::duration const& closeResolution,
CloseTimes const& rawCloseTimes,
Mode const& mode)
ConsensusCloseTimes const& rawCloseTimes,
ConsensusMode const& mode,
Json::Value && consensusJson)
{
auto newLedger = prevLedger.close(
result.set.txs_,
@@ -283,7 +314,8 @@ struct Peer : public Consensus<Peer, Traits>
rawCloseTimes.self,
result.position.closeTime() != NetClock::time_point{});
ledgers[newLedger.id()] = newLedger;
prevProposers_ = result.proposers;
prevRoundTime_ = result.roundTime.read();
lastClosedLedger = newLedger;
auto it =
@@ -304,16 +336,16 @@ struct Peer : public Consensus<Peer, Traits>
// TODO: reconsider this and instead just save LCL generated here?
if (completedLedgers <= targetLedgers)
{
startRound(
consensus.startRound(
now(), lastClosedLedger.id(), lastClosedLedger, proposing_);
}
}
Ledger::ID
getPrevLedger(Ledger::ID const& ledgerID, Ledger const& ledger, Mode mode)
getPrevLedger(Ledger::ID const& ledgerID, Ledger const& ledger, ConsensusMode mode)
{
// TODO: Use generic validation code
if (mode != Mode::wrongLedger && ledgerID.seq > 0 &&
if (mode != ConsensusMode::wrongLedger && ledgerID.seq > 0 &&
ledger.id().seq > 0)
return peerValidations.getBestLCL(ledgerID, ledger.parentID());
return ledgerID;
@@ -323,24 +355,31 @@ struct Peer : public Consensus<Peer, Traits>
propose(Proposal const& pos)
{
if (proposing_)
relay(pos);
relay(PeerPosition(pos));
}
ConsensusParms const &
parms() const
{
return parms_;
}
//-------------------------------------------------------------------------
// non-callback helpers
void
receive(Proposal const& p)
receive(PeerPosition const& peerPos)
{
Proposal const & p = peerPos.proposal();
if (unl.find(p.nodeID()) == unl.end())
return;
// TODO: Be sure this is a new proposal!!!!!
// TODO: Supress repeats more efficiently
auto& dest = peerPositions_[p.prevLedger()];
if (std::find(dest.begin(), dest.end(), p) != dest.end())
return;
dest.push_back(p);
peerProposal(now(), p);
consensus.peerProposal(now(), peerPos);
}
void
@@ -349,7 +388,7 @@ struct Peer : public Consensus<Peer, Traits>
// save and map complete?
auto it = txSets.insert(std::make_pair(txs.id(), txs));
if (it.second)
gotTxSet(now(), txs);
consensus.gotTxSet(now(), txs);
}
void
@@ -392,7 +431,7 @@ struct Peer : public Consensus<Peer, Traits>
void
timerEntry()
{
Base::timerEntry(now());
consensus.timerEntry(now());
// only reschedule if not completed
if (completedLedgers < targetLedgers)
net.timer(parms().ledgerGRANULARITY, [&]() { timerEntry(); });
@@ -406,7 +445,7 @@ struct Peer : public Consensus<Peer, Traits>
// so there is no gaurantee that bestLCL == lastClosedLedger.id()
auto bestLCL = peerValidations.getBestLCL(
lastClosedLedger.id(), lastClosedLedger.parentID());
startRound(now(), bestLCL, lastClosedLedger, proposing_);
consensus.startRound(now(), bestLCL, lastClosedLedger, proposing_);
}
NetClock::time_point
@@ -435,6 +474,28 @@ struct Peer : public Consensus<Peer, Traits>
else
net.timer(when, std::forward<T>(what));
}
Ledger::ID
prevLedgerID()
{
return consensus.prevLedgerID();
}
std::size_t
prevProposers()
{
return prevProposers_;
}
std::chrono::milliseconds
prevRoundTime()
{
return prevRoundTime_;
}
// Not interested in tracking consensus mode
void
onModeChange(ConsensusMode, ConsensusMode) {}
};
} // csf

View File

@@ -45,6 +45,7 @@
#include <test/app/Transaction_ordering_test.cpp>
#include <test/app/TrustAndBalance_test.cpp>
#include <test/app/TxQ_test.cpp>
#include <test/app/ValidatorKeys_test.cpp>
#include <test/app/ValidatorList_test.cpp>
#include <test/app/ValidatorSite_test.cpp>
#include <test/app/SetTrust_test.cpp>