mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-24 13:05:53 +00:00
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:
@@ -1099,6 +1099,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorList.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
@@ -1131,6 +1135,8 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorKeys.h">
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorSite.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorSite.h">
|
||||||
@@ -1865,6 +1871,8 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
|
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\consensus\ConsensusTypes.h">
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
|
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
|
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
|
||||||
@@ -4323,6 +4331,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\src\test\app\ValidatorList_test.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -1635,6 +1635,9 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\app\misc\impl\TxQ.cpp">
|
<ClCompile Include="..\..\src\ripple\app\misc\impl\TxQ.cpp">
|
||||||
<Filter>ripple\app\misc\impl</Filter>
|
<Filter>ripple\app\misc\impl</Filter>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorList.cpp">
|
||||||
<Filter>ripple\app\misc\impl</Filter>
|
<Filter>ripple\app\misc\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -1671,6 +1674,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorKeys.h">
|
||||||
|
<Filter>ripple\app\misc</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -2511,6 +2517,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
|
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
|
||||||
<Filter>ripple\consensus</Filter>
|
<Filter>ripple\consensus</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\consensus\ConsensusTypes.h">
|
||||||
|
<Filter>ripple\consensus</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
|
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
|
||||||
<Filter>ripple\consensus</Filter>
|
<Filter>ripple\consensus</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -5121,6 +5130,9 @@
|
|||||||
<ClCompile Include="..\..\src\test\app\TxQ_test.cpp">
|
<ClCompile Include="..\..\src\test\app\TxQ_test.cpp">
|
||||||
<Filter>test\app</Filter>
|
<Filter>test\app</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\test\app\ValidatorKeys_test.cpp">
|
||||||
|
<Filter>test\app</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\test\app\ValidatorList_test.cpp">
|
<ClCompile Include="..\..\src\test\app\ValidatorList_test.cpp">
|
||||||
<Filter>test\app</Filter>
|
<Filter>test\app</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -480,69 +480,87 @@ struct Ledger
|
|||||||
//... implementation specific
|
//... 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
|
// 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;
|
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
|
// Represents a transction under dispute this round
|
||||||
template <class Tx_t, class NodeID_t> class DisputedTx;
|
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:
|
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.
|
// Kick-off the next round of consensus.
|
||||||
void startRound(
|
void startRound(
|
||||||
NetClock::time_point const& now,
|
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.
|
The stub below shows the set of callback/helper functions required in the implementing class.
|
||||||
|
|
||||||
```
|
```
|
||||||
struct Traits
|
struct Adaptor
|
||||||
{
|
{
|
||||||
using Ledger_t = Ledger;
|
using Ledger_t = Ledger;
|
||||||
using TxSet_t = TxSet;
|
using TxSet_t = TxSet;
|
||||||
using NodeID_t = ...; // Integer-like std::uint32_t to uniquely identify a node
|
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.
|
// Attempt to acquire a specific ledger from the network.
|
||||||
boost::optional<Ledger> acquireLedger(Ledger::ID const & ledgerID);
|
boost::optional<Ledger> acquireLedger(Ledger::ID const & ledgerID);
|
||||||
|
|
||||||
// Acquire the transaction set associated with a proposed position.
|
// Acquire the transaction set associated with a proposed position.
|
||||||
boost::optional<TxSet> acquireTxSet(TxSet::ID const & setID);
|
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
|
// Whether any transactions are in the open ledger
|
||||||
bool hasOpenTransactions() const;
|
bool hasOpenTransactions() const;
|
||||||
|
|
||||||
@@ -602,24 +614,27 @@ class ConsensusImp : public Consensus<ConsensusImp, Traits>
|
|||||||
// application thinks consensus should use as the prior ledger.
|
// application thinks consensus should use as the prior ledger.
|
||||||
Ledger::ID getPrevLedger(Ledger::ID const & prevLedgerID,
|
Ledger::ID getPrevLedger(Ledger::ID const & prevLedgerID,
|
||||||
Ledger const & prevLedger,
|
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
|
// Called when ledger closes. Implementation should generate an initial Result
|
||||||
// with position based on the current open ledger's transactions.
|
// 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
|
// Called when ledger is accepted by consensus
|
||||||
void onAccept(Result const & result,
|
void onAccept(ConsensusResult const & result,
|
||||||
RCLCxLedger const & prevLedger,
|
RCLCxLedger const & prevLedger,
|
||||||
NetClock::duration closeResolution,
|
NetClock::duration closeResolution,
|
||||||
CloseTimes const & rawCloseTimes,
|
ConsensusCloseTimes const & rawCloseTimes,
|
||||||
Mode const & mode);
|
ConsensusMode const & mode);
|
||||||
|
|
||||||
// Propose the position to peers.
|
// Propose the position to peers.
|
||||||
void propose(ConsensusProposal<...> const & pos);
|
void propose(ConsensusProposal<...> const & pos);
|
||||||
|
|
||||||
// Relay a received peer proposal on to other peer's.
|
// 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
|
// Relay a disputed transaction to peers
|
||||||
void relay(TxSet::Tx const & tx);
|
void relay(TxSet::Tx const & tx);
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ INPUT = \
|
|||||||
../src/test/jtx/WSClient.h \
|
../src/test/jtx/WSClient.h \
|
||||||
../src/ripple/consensus/Consensus.h \
|
../src/ripple/consensus/Consensus.h \
|
||||||
../src/ripple/consensus/ConsensusProposal.h \
|
../src/ripple/consensus/ConsensusProposal.h \
|
||||||
|
../src/ripple/consensus/ConsensusTypes.h \
|
||||||
../src/ripple/consensus/DisputedTx.h \
|
../src/ripple/consensus/DisputedTx.h \
|
||||||
../src/ripple/consensus/LedgerTiming.h \
|
../src/ripple/consensus/LedgerTiming.h \
|
||||||
../src/ripple/consensus/Validations.h \
|
../src/ripple/consensus/Validations.h \
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <ripple/app/misc/LoadFeeTrack.h>
|
#include <ripple/app/misc/LoadFeeTrack.h>
|
||||||
#include <ripple/app/misc/NetworkOPs.h>
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
#include <ripple/app/misc/TxQ.h>
|
#include <ripple/app/misc/TxQ.h>
|
||||||
|
#include <ripple/app/misc/ValidatorKeys.h>
|
||||||
#include <ripple/app/misc/ValidatorList.h>
|
#include <ripple/app/misc/ValidatorList.h>
|
||||||
#include <ripple/app/tx/apply.h>
|
#include <ripple/app/tx/apply.h>
|
||||||
#include <ripple/basics/make_lock.h>
|
#include <ripple/basics/make_lock.h>
|
||||||
@@ -48,21 +49,45 @@ RCLConsensus::RCLConsensus(
|
|||||||
LedgerMaster& ledgerMaster,
|
LedgerMaster& ledgerMaster,
|
||||||
LocalTxs& localTxs,
|
LocalTxs& localTxs,
|
||||||
InboundTransactions& inboundTransactions,
|
InboundTransactions& inboundTransactions,
|
||||||
typename Base::clock_type const& clock,
|
Consensus<Adaptor>::clock_type const& clock,
|
||||||
|
ValidatorKeys const& validatorKeys,
|
||||||
beast::Journal journal)
|
beast::Journal journal)
|
||||||
: Base(clock, ConsensusParms{}, journal)
|
: adaptor_(
|
||||||
, app_(app)
|
app,
|
||||||
, feeVote_(std::move(feeVote))
|
std::move(feeVote),
|
||||||
, ledgerMaster_(ledgerMaster)
|
ledgerMaster,
|
||||||
, localTxs_(localTxs)
|
localTxs,
|
||||||
, inboundTransactions_{inboundTransactions}
|
inboundTransactions,
|
||||||
|
validatorKeys,
|
||||||
|
journal)
|
||||||
|
, consensus_(clock, adaptor_, journal)
|
||||||
, j_(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>
|
boost::optional<RCLCxLedger>
|
||||||
RCLConsensus::acquireLedger(LedgerHash const& ledger)
|
RCLConsensus::Adaptor::acquireLedger(LedgerHash const& ledger)
|
||||||
{
|
{
|
||||||
// we need to switch the ledger we're working from
|
// we need to switch the ledger we're working from
|
||||||
auto buildLCL = ledgerMaster_.getLedgerByHash(ledger);
|
auto buildLCL = ledgerMaster_.getLedgerByHash(ledger);
|
||||||
@@ -96,37 +121,9 @@ RCLConsensus::acquireLedger(LedgerHash const& ledger)
|
|||||||
return RCLCxLedger(buildLCL);
|
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
|
void
|
||||||
RCLConsensus::storeProposal(RCLCxPeerPos::ref peerPos, NodeID const& nodeID)
|
RCLConsensus::Adaptor::relay(RCLCxPeerPos const& peerPos)
|
||||||
{
|
|
||||||
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;
|
protocol::TMProposeSet prop;
|
||||||
|
|
||||||
@@ -140,17 +137,17 @@ RCLConsensus::relay(RCLCxPeerPos const& peerPos)
|
|||||||
prop.set_previousledger(
|
prop.set_previousledger(
|
||||||
proposal.prevLedger().begin(), proposal.position().size());
|
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());
|
prop.set_nodepubkey(pk.data(), pk.size());
|
||||||
|
|
||||||
auto const sig = peerPos.getSignature();
|
auto const sig = peerPos.signature();
|
||||||
prop.set_signature(sig.data(), sig.size());
|
prop.set_signature(sig.data(), sig.size());
|
||||||
|
|
||||||
app_.overlay().relay(prop, peerPos.getSuppressionID());
|
app_.overlay().relay(prop, peerPos.suppressionID());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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 we didn't relay this transaction recently, relay it to all peers
|
||||||
if (app_.getHashRouter().shouldRelay(tx.id()))
|
if (app_.getHashRouter().shouldRelay(tx.id()))
|
||||||
@@ -166,7 +163,7 @@ RCLConsensus::relay(RCLCxTx const& tx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
RCLConsensus::propose(RCLCxPeerPos::Proposal const& proposal)
|
RCLConsensus::Adaptor::propose(RCLCxPeerPos::Proposal const& proposal)
|
||||||
{
|
{
|
||||||
JLOG(j_.trace()) << "We propose: "
|
JLOG(j_.trace()) << "We propose: "
|
||||||
<< (proposal.isBowOut()
|
<< (proposal.isBowOut()
|
||||||
@@ -199,13 +196,13 @@ RCLConsensus::propose(RCLCxPeerPos::Proposal const& proposal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::relay(RCLTxSet const& set)
|
RCLConsensus::Adaptor::relay(RCLTxSet const& set)
|
||||||
{
|
{
|
||||||
inboundTransactions_.giveSet(set.id(), set.map_, false);
|
inboundTransactions_.giveSet(set.id(), set.map_, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<RCLTxSet>
|
boost::optional<RCLTxSet>
|
||||||
RCLConsensus::acquireTxSet(RCLTxSet::ID const& setId)
|
RCLConsensus::Adaptor::acquireTxSet(RCLTxSet::ID const& setId)
|
||||||
{
|
{
|
||||||
if (auto set = inboundTransactions_.getSet(setId, true))
|
if (auto set = inboundTransactions_.getSet(setId, true))
|
||||||
{
|
{
|
||||||
@@ -215,32 +212,32 @@ RCLConsensus::acquireTxSet(RCLTxSet::ID const& setId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
RCLConsensus::hasOpenTransactions() const
|
RCLConsensus::Adaptor::hasOpenTransactions() const
|
||||||
{
|
{
|
||||||
return !app_.openLedger().empty();
|
return !app_.openLedger().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
RCLConsensus::proposersValidated(LedgerHash const& h) const
|
RCLConsensus::Adaptor::proposersValidated(LedgerHash const& h) const
|
||||||
{
|
{
|
||||||
return app_.getValidations().numTrustedForLedger(h);
|
return app_.getValidations().numTrustedForLedger(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
RCLConsensus::proposersFinished(LedgerHash const& h) const
|
RCLConsensus::Adaptor::proposersFinished(LedgerHash const& h) const
|
||||||
{
|
{
|
||||||
return app_.getValidations().getNodesAfter(h);
|
return app_.getValidations().getNodesAfter(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256
|
uint256
|
||||||
RCLConsensus::getPrevLedger(
|
RCLConsensus::Adaptor::getPrevLedger(
|
||||||
uint256 ledgerID,
|
uint256 ledgerID,
|
||||||
RCLCxLedger const& ledger,
|
RCLCxLedger const& ledger,
|
||||||
Mode mode)
|
ConsensusMode mode)
|
||||||
{
|
{
|
||||||
uint256 parentID;
|
uint256 parentID;
|
||||||
// Only set the parent ID if we believe ledger is the right ledger
|
// Only set the parent ID if we believe ledger is the right ledger
|
||||||
if (mode != Mode::wrongLedger)
|
if (mode != ConsensusMode::wrongLedger)
|
||||||
parentID = ledger.parentID();
|
parentID = ledger.parentID();
|
||||||
|
|
||||||
// Get validators that are on our ledger, or "close" to being on
|
// Get validators that are on our ledger, or "close" to being on
|
||||||
@@ -265,28 +262,27 @@ RCLConsensus::getPrevLedger(
|
|||||||
|
|
||||||
if (netLgr != ledgerID)
|
if (netLgr != ledgerID)
|
||||||
{
|
{
|
||||||
if (mode != Mode::wrongLedger)
|
if (mode != ConsensusMode::wrongLedger)
|
||||||
app_.getOPs().consensusViewChange();
|
app_.getOPs().consensusViewChange();
|
||||||
|
|
||||||
if (auto stream = j_.debug())
|
if (auto stream = j_.debug())
|
||||||
{
|
{
|
||||||
for (auto& it : vals)
|
for (auto& it : vals)
|
||||||
stream << "V: " << it.first << ", " << it.second.count;
|
stream << "V: " << it.first << ", " << it.second.count;
|
||||||
stream << getJson(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return netLgr;
|
return netLgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RCLConsensus::Result
|
auto
|
||||||
RCLConsensus::onClose(
|
RCLConsensus::Adaptor::onClose(
|
||||||
RCLCxLedger const& ledger,
|
RCLCxLedger const& ledger,
|
||||||
NetClock::time_point const& closeTime,
|
NetClock::time_point const& closeTime,
|
||||||
Mode mode)
|
ConsensusMode mode) -> Result
|
||||||
{
|
{
|
||||||
const bool wrongLCL = mode == Mode::wrongLedger;
|
const bool wrongLCL = mode == ConsensusMode::wrongLedger;
|
||||||
const bool proposing = mode == Mode::proposing;
|
const bool proposing = mode == ConsensusMode::proposing;
|
||||||
|
|
||||||
notify(protocol::neCLOSING_LEDGER, ledger, !wrongLCL);
|
notify(protocol::neCLOSING_LEDGER, ledger, !wrongLCL);
|
||||||
|
|
||||||
@@ -346,47 +342,68 @@ RCLConsensus::onClose(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::onForceAccept(
|
RCLConsensus::Adaptor::onForceAccept(
|
||||||
Result const& result,
|
Result const& result,
|
||||||
RCLCxLedger const& prevLedger,
|
RCLCxLedger const& prevLedger,
|
||||||
NetClock::duration const& closeResolution,
|
NetClock::duration const& closeResolution,
|
||||||
CloseTimes const& rawCloseTimes,
|
ConsensusCloseTimes const& rawCloseTimes,
|
||||||
Mode const& mode)
|
ConsensusMode const& mode,
|
||||||
|
Json::Value && consensusJson)
|
||||||
{
|
{
|
||||||
doAccept(result, prevLedger, closeResolution, rawCloseTimes, mode);
|
doAccept(
|
||||||
|
result,
|
||||||
|
prevLedger,
|
||||||
|
closeResolution,
|
||||||
|
rawCloseTimes,
|
||||||
|
mode,
|
||||||
|
std::move(consensusJson));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::onAccept(
|
RCLConsensus::Adaptor::onAccept(
|
||||||
Result const& result,
|
Result const& result,
|
||||||
RCLCxLedger const& prevLedger,
|
RCLCxLedger const& prevLedger,
|
||||||
NetClock::duration const& closeResolution,
|
NetClock::duration const& closeResolution,
|
||||||
CloseTimes const& rawCloseTimes,
|
ConsensusCloseTimes const& rawCloseTimes,
|
||||||
Mode const& mode)
|
ConsensusMode const& mode,
|
||||||
|
Json::Value && consensusJson)
|
||||||
{
|
{
|
||||||
app_.getJobQueue().addJob(
|
app_.getJobQueue().addJob(
|
||||||
jtACCEPT, "acceptLedger", [&, that = this->shared_from_this() ](auto&) {
|
jtACCEPT,
|
||||||
// note that no lock is held inside this thread, which
|
"acceptLedger",
|
||||||
// is fine since once a ledger is accepted, consensus
|
[&, cj = std::move(consensusJson) ](auto&) mutable {
|
||||||
// will not touch any internal state until startRound is called
|
// Note that no lock is held or acquired during this job.
|
||||||
that->doAccept(
|
// This is because generic Consensus guarantees that once a ledger
|
||||||
result, prevLedger, closeResolution, rawCloseTimes, mode);
|
// is accepted, the consensus results and capture by reference state
|
||||||
that->app_.getOPs().endConsensus();
|
// 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
|
void
|
||||||
RCLConsensus::doAccept(
|
RCLConsensus::Adaptor::doAccept(
|
||||||
Result const& result,
|
Result const& result,
|
||||||
RCLCxLedger const& prevLedger,
|
RCLCxLedger const& prevLedger,
|
||||||
NetClock::duration closeResolution,
|
NetClock::duration closeResolution,
|
||||||
CloseTimes const& rawCloseTimes,
|
ConsensusCloseTimes const& rawCloseTimes,
|
||||||
Mode const& mode)
|
ConsensusMode const& mode,
|
||||||
|
Json::Value && consensusJson)
|
||||||
{
|
{
|
||||||
|
prevProposers_ = result.proposers;
|
||||||
|
prevRoundTime_ = result.roundTime.read();
|
||||||
|
|
||||||
bool closeTimeCorrect;
|
bool closeTimeCorrect;
|
||||||
|
|
||||||
const bool proposing = mode == Mode::proposing;
|
const bool proposing = mode == ConsensusMode::proposing;
|
||||||
const bool haveCorrectLCL = mode != Mode::wrongLedger;
|
const bool haveCorrectLCL = mode != ConsensusMode::wrongLedger;
|
||||||
const bool consensusFail = result.state == ConsensusState::MovedOn;
|
const bool consensusFail = result.state == ConsensusState::MovedOn;
|
||||||
|
|
||||||
auto consensusCloseTime = result.position.closeTime();
|
auto consensusCloseTime = result.position.closeTime();
|
||||||
@@ -447,7 +464,7 @@ RCLConsensus::doAccept(
|
|||||||
JLOG(j_.info()) << "CNF buildLCL " << newLCLHash;
|
JLOG(j_.info()) << "CNF buildLCL " << newLCLHash;
|
||||||
|
|
||||||
// See if we can accept a ledger as fully-validated
|
// 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,
|
// we entered the round with the network,
|
||||||
// see how close our close time is to other node's
|
// see how close our close time is to other node's
|
||||||
// close time reports, and update our clock.
|
// 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;
|
auto closeTime = rawCloseTimes.self;
|
||||||
|
|
||||||
@@ -577,7 +594,7 @@ RCLConsensus::doAccept(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::notify(
|
RCLConsensus::Adaptor::notify(
|
||||||
protocol::NodeEvent ne,
|
protocol::NodeEvent ne,
|
||||||
RCLCxLedger const& ledger,
|
RCLCxLedger const& ledger,
|
||||||
bool haveCorrectLCL)
|
bool haveCorrectLCL)
|
||||||
@@ -713,7 +730,7 @@ applyTransactions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
RCLCxLedger
|
RCLCxLedger
|
||||||
RCLConsensus::buildLCL(
|
RCLConsensus::Adaptor::buildLCL(
|
||||||
RCLCxLedger const& previousLedger,
|
RCLCxLedger const& previousLedger,
|
||||||
RCLTxSet const& set,
|
RCLTxSet const& set,
|
||||||
NetClock::time_point closeTime,
|
NetClock::time_point closeTime,
|
||||||
@@ -809,7 +826,7 @@ RCLConsensus::buildLCL(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::validate(RCLCxLedger const& ledger, bool proposing)
|
RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, bool proposing)
|
||||||
{
|
{
|
||||||
auto validationTime = app_.timeKeeper().closeTime();
|
auto validationTime = app_.timeKeeper().closeTime();
|
||||||
if (validationTime <= lastValidationTime_)
|
if (validationTime <= lastValidationTime_)
|
||||||
@@ -852,37 +869,26 @@ RCLConsensus::validate(RCLCxLedger const& ledger, bool proposing)
|
|||||||
Json::Value
|
Json::Value
|
||||||
RCLConsensus::getJson(bool full) const
|
RCLConsensus::getJson(bool full) const
|
||||||
{
|
{
|
||||||
auto ret = Base::getJson(full);
|
Json::Value ret;
|
||||||
ret["validating"] = validating_;
|
{
|
||||||
|
ScopedLockType _{mutex_};
|
||||||
|
ret = consensus_.getJson(full);
|
||||||
|
}
|
||||||
|
ret["validating"] = adaptor_.validating();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
PublicKey const&
|
|
||||||
RCLConsensus::getValidationPublicKey() const
|
|
||||||
{
|
|
||||||
return valPublic_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RCLConsensus::setValidationKeys(
|
|
||||||
SecretKey const& valSecret,
|
|
||||||
PublicKey const& valPublic)
|
|
||||||
{
|
|
||||||
valSecret_ = valSecret;
|
|
||||||
valPublic_ = valPublic;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::timerEntry(NetClock::time_point const& now)
|
RCLConsensus::timerEntry(NetClock::time_point const& now)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Base::timerEntry(now);
|
ScopedLockType _{mutex_};
|
||||||
|
consensus_.timerEntry(now);
|
||||||
}
|
}
|
||||||
catch (SHAMapMissingNode const& mn)
|
catch (SHAMapMissingNode const& mn)
|
||||||
{
|
{
|
||||||
// This should never happen
|
// This should never happen
|
||||||
leaveConsensus();
|
|
||||||
JLOG(j_.error()) << "Missing node during consensus process " << mn;
|
JLOG(j_.error()) << "Missing node during consensus process " << mn;
|
||||||
Rethrow();
|
Rethrow();
|
||||||
}
|
}
|
||||||
@@ -893,31 +899,45 @@ RCLConsensus::gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Base::gotTxSet(now, txSet);
|
ScopedLockType _{mutex_};
|
||||||
|
consensus_.gotTxSet(now, txSet);
|
||||||
}
|
}
|
||||||
catch (SHAMapMissingNode const& mn)
|
catch (SHAMapMissingNode const& mn)
|
||||||
{
|
{
|
||||||
// This should never happen
|
// This should never happen
|
||||||
leaveConsensus();
|
|
||||||
JLOG(j_.error()) << "Missing node during consensus process " << mn;
|
JLOG(j_.error()) << "Missing node during consensus process " << mn;
|
||||||
Rethrow();
|
Rethrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! @see Consensus::simulate
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLConsensus::startRound(
|
RCLConsensus::simulate(
|
||||||
NetClock::time_point const& now,
|
NetClock::time_point const& now,
|
||||||
RCLCxLedger::ID const& prevLgrId,
|
boost::optional<std::chrono::milliseconds> consensusDelay)
|
||||||
RCLCxLedger const& prevLgr)
|
{
|
||||||
|
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
|
// We have a key, and we have some idea what the ledger is
|
||||||
validating_ =
|
validating_ =
|
||||||
!app_.getOPs().isNeedNetworkLedger() && (valPublic_.size() != 0);
|
!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_)
|
if (validating_)
|
||||||
{
|
{
|
||||||
JLOG(j_.info()) << "Entering consensus process, validating";
|
JLOG(j_.info()) << "Entering consensus process, validating";
|
||||||
@@ -931,6 +951,19 @@ RCLConsensus::startRound(
|
|||||||
// Notify inbOund ledgers that we are starting a new round
|
// Notify inbOund ledgers that we are starting a new round
|
||||||
inboundTransactions_.newRound(prevLgr.seq());
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,34 +34,332 @@
|
|||||||
#include <ripple/protocol/RippleLedgerHash.h>
|
#include <ripple/protocol/RippleLedgerHash.h>
|
||||||
#include <ripple/protocol/STValidation.h>
|
#include <ripple/protocol/STValidation.h>
|
||||||
#include <ripple/shamap/SHAMap.h>
|
#include <ripple/shamap/SHAMap.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
class InboundTransactions;
|
class InboundTransactions;
|
||||||
class LocalTxs;
|
class LocalTxs;
|
||||||
class LedgerMaster;
|
class LedgerMaster;
|
||||||
|
class ValidatorKeys;
|
||||||
|
|
||||||
//! Types used to adapt consensus for RCL
|
/** Manages the generic consensus algorithm for use by the 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..
|
|
||||||
*/
|
*/
|
||||||
class RCLConsensus final : public Consensus<RCLConsensus, RCLCxTraits>,
|
class RCLConsensus
|
||||||
public std::enable_shared_from_this<RCLConsensus>,
|
|
||||||
public CountedObject<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:
|
public:
|
||||||
//! Constructor
|
//! Constructor
|
||||||
@@ -71,7 +369,8 @@ public:
|
|||||||
LedgerMaster& ledgerMaster,
|
LedgerMaster& ledgerMaster,
|
||||||
LocalTxs& localTxs,
|
LocalTxs& localTxs,
|
||||||
InboundTransactions& inboundTransactions,
|
InboundTransactions& inboundTransactions,
|
||||||
typename Base::clock_type const& clock,
|
Consensus<Adaptor>::clock_type const& clock,
|
||||||
|
ValidatorKeys const & validatorKeys,
|
||||||
beast::Journal journal);
|
beast::Journal journal);
|
||||||
|
|
||||||
RCLConsensus(RCLConsensus const&) = delete;
|
RCLConsensus(RCLConsensus const&) = delete;
|
||||||
@@ -79,310 +378,96 @@ public:
|
|||||||
RCLConsensus&
|
RCLConsensus&
|
||||||
operator=(RCLConsensus const&) = delete;
|
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.
|
//! Whether we are validating consensus ledgers.
|
||||||
bool
|
bool
|
||||||
validating() const
|
validating() const
|
||||||
{
|
{
|
||||||
return validating_;
|
return adaptor_.validating();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
//! Get the number of proposing peers that participated in the previous
|
||||||
haveCorrectLCL() const
|
//! round.
|
||||||
|
std::size_t
|
||||||
|
prevProposers() const
|
||||||
{
|
{
|
||||||
return mode() != Mode::wrongLedger;
|
return adaptor_.prevProposers();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
/** Get duration of the previous round.
|
||||||
proposing() const
|
|
||||||
{
|
|
||||||
return mode() == Mode::proposing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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.
|
@return Last round duration in milliseconds
|
||||||
|
|
||||||
@param full True if verbose response desired.
|
|
||||||
@return The Json state.
|
|
||||||
*/
|
*/
|
||||||
|
std::chrono::milliseconds
|
||||||
|
prevRoundTime() const
|
||||||
|
{
|
||||||
|
return adaptor_.prevRoundTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! @see Consensus::mode
|
||||||
|
ConsensusMode
|
||||||
|
mode() const
|
||||||
|
{
|
||||||
|
return adaptor_.mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! @see Consensus::getJson
|
||||||
Json::Value
|
Json::Value
|
||||||
getJson(bool full) const;
|
getJson(bool full) const;
|
||||||
|
|
||||||
//! See Consensus::startRound
|
//! @see Consensus::startRound
|
||||||
void
|
void
|
||||||
startRound(
|
startRound(
|
||||||
NetClock::time_point const& now,
|
NetClock::time_point const& now,
|
||||||
RCLCxLedger::ID const& prevLgrId,
|
RCLCxLedger::ID const& prevLgrId,
|
||||||
RCLCxLedger const& prevLgr);
|
RCLCxLedger const& prevLgr);
|
||||||
|
|
||||||
//! See Consensus::timerEntry
|
//! @see Consensus::timerEntry
|
||||||
void
|
void
|
||||||
timerEntry(NetClock::time_point const& now);
|
timerEntry(NetClock::time_point const& now);
|
||||||
|
|
||||||
//! See Consensus::gotTxSet
|
//! @see Consensus::gotTxSet
|
||||||
void
|
void
|
||||||
gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet);
|
gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet);
|
||||||
|
|
||||||
/** Returns validation public key */
|
// @see Consensus::prevLedgerID
|
||||||
PublicKey const&
|
RCLCxLedger::ID
|
||||||
getValidationPublicKey() const;
|
prevLedgerID() const
|
||||||
|
{
|
||||||
|
ScopedLockType _{mutex_};
|
||||||
|
return consensus_.prevLedgerID();
|
||||||
|
}
|
||||||
|
|
||||||
/** Set validation private and public key pair. */
|
//! @see Consensus::simulate
|
||||||
void
|
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:
|
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>;
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
Adaptor adaptor_;
|
||||||
// Consensus type requirements.
|
Consensus<Adaptor> consensus_;
|
||||||
|
|
||||||
/** 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_;
|
|
||||||
beast::Journal j_;
|
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;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,14 +33,16 @@ RCLCxPeerPos::RCLCxPeerPos(
|
|||||||
Slice const& signature,
|
Slice const& signature,
|
||||||
uint256 const& suppression,
|
uint256 const& suppression,
|
||||||
Proposal&& proposal)
|
Proposal&& proposal)
|
||||||
: proposal_{std::move(proposal)}
|
: data_{std::make_shared<Data>(
|
||||||
, mSuppression{suppression}
|
publicKey,
|
||||||
, publicKey_{publicKey}
|
signature,
|
||||||
, signature_{signature}
|
suppression,
|
||||||
|
std::move(proposal))}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256
|
uint256
|
||||||
RCLCxPeerPos::getSigningHash() const
|
RCLCxPeerPos::signingHash() const
|
||||||
{
|
{
|
||||||
return sha512Half(
|
return sha512Half(
|
||||||
HashPrefix::proposal,
|
HashPrefix::proposal,
|
||||||
@@ -53,16 +55,18 @@ RCLCxPeerPos::getSigningHash() const
|
|||||||
bool
|
bool
|
||||||
RCLCxPeerPos::checkSign() const
|
RCLCxPeerPos::checkSign() const
|
||||||
{
|
{
|
||||||
return verifyDigest(publicKey_, getSigningHash(), signature_, false);
|
return verifyDigest(
|
||||||
|
publicKey(), signingHash(), signature(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Json::Value
|
Json::Value
|
||||||
RCLCxPeerPos::getJson() const
|
RCLCxPeerPos::getJson() const
|
||||||
{
|
{
|
||||||
auto ret = proposal().getJson();
|
auto ret = proposal().getJson();
|
||||||
|
|
||||||
if (publicKey_.size())
|
if (publicKey().size())
|
||||||
ret[jss::peer_id] = toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey_);
|
ret[jss::peer_id] = toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey());
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -87,4 +91,16 @@ proposalUniqueId(
|
|||||||
return s.getSHA512Half();
|
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
|
} // ripple
|
||||||
|
|||||||
@@ -36,19 +36,12 @@ namespace ripple {
|
|||||||
|
|
||||||
/** A peer's signed, proposed position for use in RCLConsensus.
|
/** 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:
|
public:
|
||||||
static char const*
|
|
||||||
getCountedObjectName()
|
|
||||||
{
|
|
||||||
return "RCLCxPeerPos";
|
|
||||||
}
|
|
||||||
using pointer = std::shared_ptr<RCLCxPeerPos>;
|
|
||||||
using ref = const pointer&;
|
|
||||||
|
|
||||||
//< The type of the proposed position
|
//< The type of the proposed position
|
||||||
using Proposal = ConsensusProposal<NodeID, uint256, uint256>;
|
using Proposal = ConsensusProposal<NodeID, uint256, uint256>;
|
||||||
|
|
||||||
@@ -70,7 +63,7 @@ public:
|
|||||||
|
|
||||||
//! Create the signing hash for the proposal
|
//! Create the signing hash for the proposal
|
||||||
uint256
|
uint256
|
||||||
getSigningHash() const;
|
signingHash() const;
|
||||||
|
|
||||||
//! Verify the signing hash of the proposal
|
//! Verify the signing hash of the proposal
|
||||||
bool
|
bool
|
||||||
@@ -78,45 +71,59 @@ public:
|
|||||||
|
|
||||||
//! Signature of the proposal (not necessarily verified)
|
//! Signature of the proposal (not necessarily verified)
|
||||||
Slice
|
Slice
|
||||||
getSignature() const
|
signature() const
|
||||||
{
|
{
|
||||||
return signature_;
|
return data_->signature_;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Public key of peer that sent the proposal
|
//! Public key of peer that sent the proposal
|
||||||
PublicKey const&
|
PublicKey const&
|
||||||
getPublicKey() const
|
publicKey() const
|
||||||
{
|
{
|
||||||
return publicKey_;
|
return data_->publicKey_;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! ?????
|
//! ?????
|
||||||
uint256 const&
|
uint256 const&
|
||||||
getSuppressionID() const
|
suppressionID() const
|
||||||
{
|
{
|
||||||
return mSuppression;
|
return data_->supression_;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! The consensus proposal
|
Proposal const &
|
||||||
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 representation of proposal
|
||||||
Json::Value
|
Json::Value
|
||||||
getJson() const;
|
getJson() const;
|
||||||
|
|
||||||
private:
|
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>
|
template <class Hasher>
|
||||||
void
|
void
|
||||||
hash_append(Hasher& h) const
|
hash_append(Hasher& h) const
|
||||||
@@ -129,10 +136,6 @@ private:
|
|||||||
hash_append(h, proposal().position());
|
hash_append(h, proposal().position());
|
||||||
}
|
}
|
||||||
|
|
||||||
Proposal proposal_;
|
|
||||||
uint256 mSuppression;
|
|
||||||
PublicKey publicKey_;
|
|
||||||
Buffer signature_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Calculate a unique identifier for a signed proposal.
|
/** Calculate a unique identifier for a signed proposal.
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include <ripple/app/misc/SHAMapStore.h>
|
#include <ripple/app/misc/SHAMapStore.h>
|
||||||
#include <ripple/app/misc/TxQ.h>
|
#include <ripple/app/misc/TxQ.h>
|
||||||
#include <ripple/app/misc/ValidatorSite.h>
|
#include <ripple/app/misc/ValidatorSite.h>
|
||||||
|
#include <ripple/app/misc/ValidatorKeys.h>
|
||||||
#include <ripple/app/paths/PathRequests.h>
|
#include <ripple/app/paths/PathRequests.h>
|
||||||
#include <ripple/app/tx/apply.h>
|
#include <ripple/app/tx/apply.h>
|
||||||
#include <ripple/basics/ResolverAsio.h>
|
#include <ripple/basics/ResolverAsio.h>
|
||||||
@@ -305,6 +306,7 @@ public:
|
|||||||
std::unique_ptr <CollectorManager> m_collectorManager;
|
std::unique_ptr <CollectorManager> m_collectorManager;
|
||||||
CachedSLEs cachedSLEs_;
|
CachedSLEs cachedSLEs_;
|
||||||
std::pair<PublicKey, SecretKey> nodeIdentity_;
|
std::pair<PublicKey, SecretKey> nodeIdentity_;
|
||||||
|
ValidatorKeys const validatorKeys_;
|
||||||
|
|
||||||
std::unique_ptr <Resource::Manager> m_resourceManager;
|
std::unique_ptr <Resource::Manager> m_resourceManager;
|
||||||
|
|
||||||
@@ -394,8 +396,8 @@ public:
|
|||||||
|
|
||||||
, m_collectorManager (CollectorManager::New (
|
, m_collectorManager (CollectorManager::New (
|
||||||
config_->section (SECTION_INSIGHT), logs_->journal("Collector")))
|
config_->section (SECTION_INSIGHT), logs_->journal("Collector")))
|
||||||
|
|
||||||
, cachedSLEs_ (std::chrono::minutes(1), stopwatch())
|
, cachedSLEs_ (std::chrono::minutes(1), stopwatch())
|
||||||
|
, validatorKeys_(*config_, m_journal)
|
||||||
|
|
||||||
, m_resourceManager (Resource::make_Manager (
|
, m_resourceManager (Resource::make_Manager (
|
||||||
m_collectorManager->collector(), logs_->journal("Resource")))
|
m_collectorManager->collector(), logs_->journal("Resource")))
|
||||||
@@ -445,7 +447,7 @@ public:
|
|||||||
|
|
||||||
, m_networkOPs (make_NetworkOPs (*this, stopwatch(),
|
, m_networkOPs (make_NetworkOPs (*this, stopwatch(),
|
||||||
config_->standalone(), config_->NETWORK_QUORUM, config_->START_VALID,
|
config_->standalone(), config_->NETWORK_QUORUM, config_->START_VALID,
|
||||||
*m_jobQueue, *m_ledgerMaster, *m_jobQueue,
|
*m_jobQueue, *m_ledgerMaster, *m_jobQueue, validatorKeys_,
|
||||||
logs_->journal("NetworkOPs")))
|
logs_->journal("NetworkOPs")))
|
||||||
|
|
||||||
, cluster_ (std::make_unique<Cluster> (
|
, cluster_ (std::make_unique<Cluster> (
|
||||||
@@ -570,6 +572,13 @@ public:
|
|||||||
return nodeIdentity_;
|
return nodeIdentity_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
PublicKey const &
|
||||||
|
getValidationPublicKey() const override
|
||||||
|
{
|
||||||
|
return validatorKeys_.publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
NetworkOPs& getOPs () override
|
NetworkOPs& getOPs () override
|
||||||
{
|
{
|
||||||
return *m_networkOPs;
|
return *m_networkOPs;
|
||||||
@@ -1086,38 +1095,11 @@ bool ApplicationImp::setup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
PublicKey valPublic;
|
if(validatorKeys_.configInvalid())
|
||||||
SecretKey valSecret;
|
return false;
|
||||||
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 (!validatorManifests_->load (
|
if (!validatorManifests_->load (
|
||||||
getWalletDB (), "ValidatorManifests", manifest,
|
getWalletDB (), "ValidatorManifests", validatorKeys_.manifest,
|
||||||
config().section (SECTION_VALIDATOR_KEY_REVOCATION).values ()))
|
config().section (SECTION_VALIDATOR_KEY_REVOCATION).values ()))
|
||||||
{
|
{
|
||||||
JLOG(m_journal.fatal()) << "Invalid configured validator manifest.";
|
JLOG(m_journal.fatal()) << "Invalid configured validator manifest.";
|
||||||
@@ -1127,11 +1109,9 @@ bool ApplicationImp::setup()
|
|||||||
publisherManifests_->load (
|
publisherManifests_->load (
|
||||||
getWalletDB (), "PublisherManifests");
|
getWalletDB (), "PublisherManifests");
|
||||||
|
|
||||||
m_networkOPs->setValidationKeys (valSecret, valPublic);
|
|
||||||
|
|
||||||
// Setup trusted validators
|
// Setup trusted validators
|
||||||
if (!validators_->load (
|
if (!validators_->load (
|
||||||
valPublic,
|
validatorKeys_.publicKey,
|
||||||
config().section (SECTION_VALIDATORS).values (),
|
config().section (SECTION_VALIDATORS).values (),
|
||||||
config().section (SECTION_VALIDATOR_LIST_KEYS).values ()))
|
config().section (SECTION_VALIDATOR_LIST_KEYS).values ()))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -150,6 +150,10 @@ public:
|
|||||||
std::pair<PublicKey, SecretKey> const&
|
std::pair<PublicKey, SecretKey> const&
|
||||||
nodeIdentity () = 0;
|
nodeIdentity () = 0;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
PublicKey const &
|
||||||
|
getValidationPublicKey() const = 0;
|
||||||
|
|
||||||
virtual Resource::Manager& getResourceManager () = 0;
|
virtual Resource::Manager& getResourceManager () = 0;
|
||||||
virtual PathRequests& getPathRequests () = 0;
|
virtual PathRequests& getPathRequests () = 0;
|
||||||
virtual SHAMapStore& getSHAMapStore () = 0;
|
virtual SHAMapStore& getSHAMapStore () = 0;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include <ripple/app/misc/LoadFeeTrack.h>
|
#include <ripple/app/misc/LoadFeeTrack.h>
|
||||||
#include <ripple/app/misc/Transaction.h>
|
#include <ripple/app/misc/Transaction.h>
|
||||||
#include <ripple/app/misc/TxQ.h>
|
#include <ripple/app/misc/TxQ.h>
|
||||||
|
#include <ripple/app/misc/ValidatorKeys.h>
|
||||||
#include <ripple/app/misc/ValidatorList.h>
|
#include <ripple/app/misc/ValidatorList.h>
|
||||||
#include <ripple/app/misc/impl/AccountTxPaging.h>
|
#include <ripple/app/misc/impl/AccountTxPaging.h>
|
||||||
#include <ripple/app/tx/apply.h>
|
#include <ripple/app/tx/apply.h>
|
||||||
@@ -187,7 +188,7 @@ public:
|
|||||||
Application& app, clock_type& clock, bool standalone,
|
Application& app, clock_type& clock, bool standalone,
|
||||||
std::size_t network_quorum, bool start_valid, JobQueue& job_queue,
|
std::size_t network_quorum, bool start_valid, JobQueue& job_queue,
|
||||||
LedgerMaster& ledgerMaster, Stoppable& parent,
|
LedgerMaster& ledgerMaster, Stoppable& parent,
|
||||||
beast::Journal journal)
|
ValidatorKeys const & validatorKeys, beast::Journal journal)
|
||||||
: NetworkOPs (parent)
|
: NetworkOPs (parent)
|
||||||
, app_ (app)
|
, app_ (app)
|
||||||
, m_clock (clock)
|
, m_clock (clock)
|
||||||
@@ -198,14 +199,15 @@ public:
|
|||||||
, m_amendmentBlocked (false)
|
, m_amendmentBlocked (false)
|
||||||
, m_heartbeatTimer (this)
|
, m_heartbeatTimer (this)
|
||||||
, m_clusterTimer (this)
|
, m_clusterTimer (this)
|
||||||
, mConsensus (std::make_shared<RCLConsensus>(app,
|
, mConsensus (app,
|
||||||
make_FeeVote(setup_FeeVote (app_.config().section ("voting")),
|
make_FeeVote(setup_FeeVote (app_.config().section ("voting")),
|
||||||
app_.logs().journal("FeeVote")),
|
app_.logs().journal("FeeVote")),
|
||||||
ledgerMaster,
|
ledgerMaster,
|
||||||
*m_localTX,
|
*m_localTX,
|
||||||
app.getInboundTransactions(),
|
app.getInboundTransactions(),
|
||||||
stopwatch(),
|
stopwatch(),
|
||||||
app_.logs().journal("LedgerConsensus")))
|
validatorKeys,
|
||||||
|
app_.logs().journal("LedgerConsensus"))
|
||||||
, m_ledgerMaster (ledgerMaster)
|
, m_ledgerMaster (ledgerMaster)
|
||||||
, m_job_queue (job_queue)
|
, m_job_queue (job_queue)
|
||||||
, m_standalone (standalone)
|
, m_standalone (standalone)
|
||||||
@@ -296,7 +298,7 @@ public:
|
|||||||
|
|
||||||
// Ledger proposal/close functions.
|
// Ledger proposal/close functions.
|
||||||
void processTrustedProposal (
|
void processTrustedProposal (
|
||||||
RCLCxPeerPos::pointer proposal,
|
RCLCxPeerPos proposal,
|
||||||
std::shared_ptr<protocol::TMProposeSet> set,
|
std::shared_ptr<protocol::TMProposeSet> set,
|
||||||
NodeID const &node) override;
|
NodeID const &node) override;
|
||||||
|
|
||||||
@@ -323,7 +325,6 @@ private:
|
|||||||
std::shared_ptr<Ledger const> const& newLCL);
|
std::shared_ptr<Ledger const> const& newLCL);
|
||||||
bool checkLastClosedLedger (
|
bool checkLastClosedLedger (
|
||||||
const Overlay::PeerSequence&, uint256& networkClosed);
|
const Overlay::PeerSequence&, uint256& networkClosed);
|
||||||
void tryStartConsensus ();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool beginConsensus (uint256 const& networkClosed) override;
|
bool beginConsensus (uint256 const& networkClosed) override;
|
||||||
@@ -360,15 +361,7 @@ public:
|
|||||||
}
|
}
|
||||||
void setAmendmentBlocked () override;
|
void setAmendmentBlocked () override;
|
||||||
void consensusViewChange () 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 getConsensusInfo () override;
|
||||||
Json::Value getServerInfo (bool human, bool admin) override;
|
Json::Value getServerInfo (bool human, bool admin) override;
|
||||||
void clearLedgerFetch () override;
|
void clearLedgerFetch () override;
|
||||||
@@ -549,7 +542,7 @@ private:
|
|||||||
DeadlineTimer m_clusterTimer;
|
DeadlineTimer m_clusterTimer;
|
||||||
JobCounter jobCounter_;
|
JobCounter jobCounter_;
|
||||||
|
|
||||||
std::shared_ptr<RCLConsensus> mConsensus;
|
RCLConsensus mConsensus;
|
||||||
|
|
||||||
LedgerMaster& m_ledgerMaster;
|
LedgerMaster& m_ledgerMaster;
|
||||||
std::shared_ptr<InboundLedger> mAcquiringLedger;
|
std::shared_ptr<InboundLedger> mAcquiringLedger;
|
||||||
@@ -651,7 +644,7 @@ void NetworkOPsImp::setStateTimer ()
|
|||||||
|
|
||||||
void NetworkOPsImp::setHeartbeatTimer ()
|
void NetworkOPsImp::setHeartbeatTimer ()
|
||||||
{
|
{
|
||||||
m_heartbeatTimer.setExpiration (mConsensus->parms().ledgerGRANULARITY);
|
m_heartbeatTimer.setExpiration (mConsensus.parms().ledgerGRANULARITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkOPsImp::setClusterTimer ()
|
void NetworkOPsImp::setClusterTimer ()
|
||||||
@@ -697,7 +690,7 @@ void NetworkOPsImp::processHeartbeatTimer ()
|
|||||||
<< "Node count (" << numPeers << ") "
|
<< "Node count (" << numPeers << ") "
|
||||||
<< "has fallen below quorum (" << m_network_quorum << ").";
|
<< "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
|
// are enough peers providing meaningful inputs to consensus
|
||||||
setHeartbeatTimer ();
|
setHeartbeatTimer ();
|
||||||
|
|
||||||
@@ -720,7 +713,7 @@ void NetworkOPsImp::processHeartbeatTimer ()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mConsensus->timerEntry (app_.timeKeeper().closeTime());
|
mConsensus.timerEntry (app_.timeKeeper().closeTime());
|
||||||
|
|
||||||
setHeartbeatTimer ();
|
setHeartbeatTimer ();
|
||||||
}
|
}
|
||||||
@@ -775,13 +768,17 @@ void NetworkOPsImp::processClusterTimer ()
|
|||||||
|
|
||||||
std::string NetworkOPsImp::strOperatingMode () const
|
std::string NetworkOPsImp::strOperatingMode () const
|
||||||
{
|
{
|
||||||
if (mMode == omFULL && mConsensus->haveCorrectLCL())
|
if (mMode == omFULL)
|
||||||
{
|
{
|
||||||
if (mConsensus->proposing ())
|
auto const mode = mConsensus.mode();
|
||||||
return "proposing";
|
if (mode != ConsensusMode::wrongLedger)
|
||||||
|
{
|
||||||
|
if (mode == ConsensusMode::proposing)
|
||||||
|
return "proposing";
|
||||||
|
|
||||||
if (mConsensus->validating ())
|
if (mConsensus.validating())
|
||||||
return "validating";
|
return "validating";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return states_[mMode];
|
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 (
|
bool NetworkOPsImp::checkLastClosedLedger (
|
||||||
const Overlay::PeerSequence& peerList, uint256& networkClosed)
|
const Overlay::PeerSequence& peerList, uint256& networkClosed)
|
||||||
{
|
{
|
||||||
@@ -1527,7 +1484,7 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
|
|||||||
app_.validators().onConsensusStart (
|
app_.validators().onConsensusStart (
|
||||||
app_.getValidations().getCurrentPublicKeys ());
|
app_.getValidations().getCurrentPublicKeys ());
|
||||||
|
|
||||||
mConsensus->startRound (
|
mConsensus.startRound (
|
||||||
app_.timeKeeper().closeTime(),
|
app_.timeKeeper().closeTime(),
|
||||||
networkClosed,
|
networkClosed,
|
||||||
prevLedger);
|
prevLedger);
|
||||||
@@ -1538,19 +1495,19 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
|
|||||||
|
|
||||||
uint256 NetworkOPsImp::getConsensusLCL ()
|
uint256 NetworkOPsImp::getConsensusLCL ()
|
||||||
{
|
{
|
||||||
return mConsensus->prevLedgerID ();
|
return mConsensus.prevLedgerID ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkOPsImp::processTrustedProposal (
|
void NetworkOPsImp::processTrustedProposal (
|
||||||
RCLCxPeerPos::pointer peerPos,
|
RCLCxPeerPos peerPos,
|
||||||
std::shared_ptr<protocol::TMProposeSet> set,
|
std::shared_ptr<protocol::TMProposeSet> set,
|
||||||
NodeID const& node)
|
NodeID const& node)
|
||||||
{
|
{
|
||||||
mConsensus->storeProposal (peerPos, node);
|
if (mConsensus.peerProposal(
|
||||||
|
app_.timeKeeper().closeTime(), peerPos))
|
||||||
if (mConsensus->peerProposal (
|
{
|
||||||
app_.timeKeeper().closeTime(), peerPos->proposal()))
|
app_.overlay().relay(*set, peerPos.suppressionID());
|
||||||
app_.overlay().relay(*set, peerPos->getSuppressionID());
|
}
|
||||||
else
|
else
|
||||||
JLOG(m_journal.info()) << "Not relaying trusted proposal";
|
JLOG(m_journal.info()) << "Not relaying trusted proposal";
|
||||||
}
|
}
|
||||||
@@ -1573,7 +1530,7 @@ NetworkOPsImp::mapComplete (
|
|||||||
|
|
||||||
// We acquired it because consensus asked us to
|
// We acquired it because consensus asked us to
|
||||||
if (fromAcquire)
|
if (fromAcquire)
|
||||||
mConsensus->gotTxSet (
|
mConsensus.gotTxSet (
|
||||||
app_.timeKeeper().closeTime(),
|
app_.timeKeeper().closeTime(),
|
||||||
RCLTxSet{map});
|
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 ()
|
void NetworkOPsImp::consensusViewChange ()
|
||||||
@@ -2125,7 +2117,7 @@ bool NetworkOPsImp::recvValidation (
|
|||||||
|
|
||||||
Json::Value NetworkOPsImp::getConsensusInfo ()
|
Json::Value NetworkOPsImp::getConsensusInfo ()
|
||||||
{
|
{
|
||||||
return mConsensus->getJson (true);
|
return mConsensus.getJson (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
|
Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
|
||||||
@@ -2151,7 +2143,7 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
|
|||||||
|
|
||||||
if (admin)
|
if (admin)
|
||||||
{
|
{
|
||||||
if (getValidationPublicKey().size ())
|
if (!app_.getValidationPublicKey().empty())
|
||||||
{
|
{
|
||||||
info[jss::pubkey_validator] = toBase58 (
|
info[jss::pubkey_validator] = toBase58 (
|
||||||
TokenType::TOKEN_NODE_PUBLIC,
|
TokenType::TOKEN_NODE_PUBLIC,
|
||||||
@@ -2181,23 +2173,23 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
|
|||||||
info[jss::peers] = Json::UInt (app_.overlay ().size ());
|
info[jss::peers] = Json::UInt (app_.overlay ().size ());
|
||||||
|
|
||||||
Json::Value lastClose = Json::objectValue;
|
Json::Value lastClose = Json::objectValue;
|
||||||
lastClose[jss::proposers] = Json::UInt(mConsensus->prevProposers());
|
lastClose[jss::proposers] = Json::UInt(mConsensus.prevProposers());
|
||||||
|
|
||||||
if (human)
|
if (human)
|
||||||
{
|
{
|
||||||
lastClose[jss::converge_time_s] =
|
lastClose[jss::converge_time_s] =
|
||||||
std::chrono::duration<double>{
|
std::chrono::duration<double>{
|
||||||
mConsensus->prevRoundTime()}.count();
|
mConsensus.prevRoundTime()}.count();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lastClose[jss::converge_time] =
|
lastClose[jss::converge_time] =
|
||||||
Json::Int (mConsensus->prevRoundTime().count());
|
Json::Int (mConsensus.prevRoundTime().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
info[jss::last_close] = lastClose;
|
info[jss::last_close] = lastClose;
|
||||||
|
|
||||||
// info[jss::consensus] = mConsensus->getJson();
|
// info[jss::consensus] = mConsensus.getJson();
|
||||||
|
|
||||||
if (admin)
|
if (admin)
|
||||||
info[jss::load] = m_job_queue.getJson ();
|
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
|
// FIXME Could we improve on this and remove the need for a specialized
|
||||||
// API in Consensus?
|
// API in Consensus?
|
||||||
beginConsensus (m_ledgerMaster.getClosedLedger()->info().hash);
|
beginConsensus (m_ledgerMaster.getClosedLedger()->info().hash);
|
||||||
mConsensus->simulate (app_.timeKeeper().closeTime(), consensusDelay);
|
mConsensus.simulate (app_.timeKeeper().closeTime(), consensusDelay);
|
||||||
return m_ledgerMaster.getCurrentLedger ()->info().seq;
|
return m_ledgerMaster.getCurrentLedger ()->info().seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3368,10 +3360,10 @@ std::unique_ptr<NetworkOPs>
|
|||||||
make_NetworkOPs (Application& app, NetworkOPs::clock_type& clock, bool standalone,
|
make_NetworkOPs (Application& app, NetworkOPs::clock_type& clock, bool standalone,
|
||||||
std::size_t network_quorum, bool startvalid,
|
std::size_t network_quorum, bool startvalid,
|
||||||
JobQueue& job_queue, LedgerMaster& ledgerMaster,
|
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,
|
return std::make_unique<NetworkOPsImp> (app, clock, standalone, network_quorum,
|
||||||
startvalid, job_queue, ledgerMaster, parent, journal);
|
startvalid, job_queue, ledgerMaster, parent, validatorKeys, journal);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace ripple {
|
|||||||
class Peer;
|
class Peer;
|
||||||
class LedgerMaster;
|
class LedgerMaster;
|
||||||
class Transaction;
|
class Transaction;
|
||||||
|
class ValidatorKeys;
|
||||||
|
|
||||||
// This is the primary interface into the "client" portion of the program.
|
// This is the primary interface into the "client" portion of the program.
|
||||||
// Code that wants to do normal operations on the network such as
|
// Code that wants to do normal operations on the network such as
|
||||||
@@ -150,7 +151,7 @@ public:
|
|||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
// ledger proposal/close functions
|
// ledger proposal/close functions
|
||||||
virtual void processTrustedProposal (RCLCxPeerPos::pointer peerPos,
|
virtual void processTrustedProposal (RCLCxPeerPos peerPos,
|
||||||
std::shared_ptr<protocol::TMProposeSet> set,
|
std::shared_ptr<protocol::TMProposeSet> set,
|
||||||
NodeID const& node) = 0;
|
NodeID const& node) = 0;
|
||||||
|
|
||||||
@@ -174,9 +175,6 @@ public:
|
|||||||
virtual bool isAmendmentBlocked () = 0;
|
virtual bool isAmendmentBlocked () = 0;
|
||||||
virtual void setAmendmentBlocked () = 0;
|
virtual void setAmendmentBlocked () = 0;
|
||||||
virtual void consensusViewChange () = 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 getConsensusInfo () = 0;
|
||||||
virtual Json::Value getServerInfo (bool human, bool admin) = 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,
|
make_NetworkOPs (Application& app, NetworkOPs::clock_type& clock, bool standalone,
|
||||||
std::size_t network_quorum, bool start_valid,
|
std::size_t network_quorum, bool start_valid,
|
||||||
JobQueue& job_queue, LedgerMaster& ledgerMaster,
|
JobQueue& job_queue, LedgerMaster& ledgerMaster,
|
||||||
Stoppable& parent, beast::Journal journal);
|
Stoppable& parent, ValidatorKeys const & validatorKeys, beast::Journal journal);
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
|
|||||||
54
src/ripple/app/misc/ValidatorKeys.h
Normal file
54
src/ripple/app/misc/ValidatorKeys.h
Normal 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
|
||||||
73
src/ripple/app/misc/impl/ValidatorKeys.cpp
Normal file
73
src/ripple/app/misc/impl/ValidatorKeys.cpp
Normal 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
242
src/ripple/consensus/ConsensusTypes.h
Normal file
242
src/ripple/consensus/ConsensusTypes.h
Normal 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
|
||||||
@@ -1094,7 +1094,7 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMTransaction> const& m)
|
|||||||
flags |= SF_TRUSTED;
|
flags |= SF_TRUSTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! app_.getOPs().getValidationPublicKey().size())
|
if (app_.getValidationPublicKey().empty())
|
||||||
{
|
{
|
||||||
// For now, be paranoid and have each validator
|
// For now, be paranoid and have each validator
|
||||||
// check each transaction, regardless of source
|
// check each transaction, regardless of source
|
||||||
@@ -1256,8 +1256,8 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMProposeSet> const& m)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app_.getOPs().getValidationPublicKey().size() &&
|
if (!app_.getValidationPublicKey().empty() &&
|
||||||
publicKey == app_.getOPs().getValidationPublicKey())
|
publicKey == app_.getValidationPublicKey())
|
||||||
{
|
{
|
||||||
JLOG(p_journal_.trace()) << "Proposal: self";
|
JLOG(p_journal_.trace()) << "Proposal: self";
|
||||||
return;
|
return;
|
||||||
@@ -1283,7 +1283,7 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMProposeSet> const& m)
|
|||||||
JLOG(p_journal_.trace()) <<
|
JLOG(p_journal_.trace()) <<
|
||||||
"Proposal: " << (isTrusted ? "trusted" : "UNTRUSTED");
|
"Proposal: " << (isTrusted ? "trusted" : "UNTRUSTED");
|
||||||
|
|
||||||
auto proposal = std::make_shared<RCLCxPeerPos> (
|
auto proposal = RCLCxPeerPos(
|
||||||
publicKey, signature, suppression,
|
publicKey, signature, suppression,
|
||||||
RCLCxPeerPos::Proposal{prevLedger, set.proposeseq (), proposeHash, closeTime,
|
RCLCxPeerPos::Proposal{prevLedger, set.proposeseq (), proposeHash, closeTime,
|
||||||
app_.timeKeeper().closeTime(),calcNodeID(publicKey)});
|
app_.timeKeeper().closeTime(),calcNodeID(publicKey)});
|
||||||
@@ -1889,7 +1889,7 @@ PeerImp::checkTransaction (int flags,
|
|||||||
void
|
void
|
||||||
PeerImp::checkPropose (Job& job,
|
PeerImp::checkPropose (Job& job,
|
||||||
std::shared_ptr <protocol::TMProposeSet> const& packet,
|
std::shared_ptr <protocol::TMProposeSet> const& packet,
|
||||||
RCLCxPeerPos::pointer peerPos)
|
RCLCxPeerPos peerPos)
|
||||||
{
|
{
|
||||||
bool isTrusted = (job.getType () == jtPROPOSAL_t);
|
bool isTrusted = (job.getType () == jtPROPOSAL_t);
|
||||||
|
|
||||||
@@ -1899,7 +1899,7 @@ PeerImp::checkPropose (Job& job,
|
|||||||
assert (packet);
|
assert (packet);
|
||||||
protocol::TMProposeSet& set = *packet;
|
protocol::TMProposeSet& set = *packet;
|
||||||
|
|
||||||
if (! cluster() && !peerPos->checkSign ())
|
if (! cluster() && !peerPos.checkSign ())
|
||||||
{
|
{
|
||||||
JLOG(p_journal_.warn()) <<
|
JLOG(p_journal_.warn()) <<
|
||||||
"Proposal fails sig check";
|
"Proposal fails sig check";
|
||||||
@@ -1914,12 +1914,12 @@ PeerImp::checkPropose (Job& job,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (app_.getOPs().getConsensusLCL() == peerPos->proposal().prevLedger())
|
if (app_.getOPs().getConsensusLCL() == peerPos.proposal().prevLedger())
|
||||||
{
|
{
|
||||||
// relay untrusted proposal
|
// relay untrusted proposal
|
||||||
JLOG(p_journal_.trace()) <<
|
JLOG(p_journal_.trace()) <<
|
||||||
"relaying UNTRUSTED proposal";
|
"relaying UNTRUSTED proposal";
|
||||||
overlay_.relay(set, peerPos->getSuppressionID());
|
overlay_.relay(set, peerPos.suppressionID());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -451,7 +451,7 @@ private:
|
|||||||
void
|
void
|
||||||
checkPropose (Job& job,
|
checkPropose (Job& job,
|
||||||
std::shared_ptr<protocol::TMProposeSet> const& packet,
|
std::shared_ptr<protocol::TMProposeSet> const& packet,
|
||||||
RCLCxPeerPos::pointer peerPos);
|
RCLCxPeerPos peerPos);
|
||||||
|
|
||||||
void
|
void
|
||||||
checkValidation (STValidation::pointer val,
|
checkValidation (STValidation::pointer val,
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ public:
|
|||||||
return size_;
|
return size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
empty() const noexcept
|
||||||
|
{
|
||||||
|
return size_ == 0;
|
||||||
|
}
|
||||||
|
|
||||||
Slice
|
Slice
|
||||||
slice() const noexcept
|
slice() const noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,3 +33,4 @@
|
|||||||
#include <ripple/app/misc/impl/TxQ.cpp>
|
#include <ripple/app/misc/impl/TxQ.cpp>
|
||||||
#include <ripple/app/misc/impl/ValidatorList.cpp>
|
#include <ripple/app/misc/impl/ValidatorList.cpp>
|
||||||
#include <ripple/app/misc/impl/ValidatorSite.cpp>
|
#include <ripple/app/misc/impl/ValidatorSite.cpp>
|
||||||
|
#include <ripple/app/misc/impl/ValidatorKeys.cpp>
|
||||||
|
|||||||
150
src/test/app/ValidatorKeys_test.cpp
Normal file
150
src/test/app/ValidatorKeys_test.cpp
Normal 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
|
||||||
@@ -21,7 +21,8 @@
|
|||||||
|
|
||||||
#include <boost/container/flat_map.hpp>
|
#include <boost/container/flat_map.hpp>
|
||||||
#include <boost/container/flat_set.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/Ledger.h>
|
||||||
#include <test/csf/Tx.h>
|
#include <test/csf/Tx.h>
|
||||||
#include <test/csf/UNL.h>
|
#include <test/csf/UNL.h>
|
||||||
@@ -115,20 +116,43 @@ public:
|
|||||||
directly from the generic types.
|
directly from the generic types.
|
||||||
*/
|
*/
|
||||||
using Proposal = ConsensusProposal<PeerID, Ledger::ID, TxSetType>;
|
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 Ledger_t = Ledger;
|
||||||
using NodeID_t = PeerID;
|
using NodeID_t = PeerID;
|
||||||
using TxSet_t = TxSet;
|
using TxSet_t = TxSet;
|
||||||
};
|
using PeerPosition_t = PeerPosition;
|
||||||
|
using Result = ConsensusResult<Peer>;
|
||||||
|
|
||||||
/** Represents a single node participating in the consensus process.
|
Consensus<Peer> consensus;
|
||||||
It implements the Callbacks required by Consensus.
|
|
||||||
*/
|
|
||||||
struct Peer : public Consensus<Peer, Traits>
|
|
||||||
{
|
|
||||||
using Base = Consensus<Peer, Traits>;
|
|
||||||
|
|
||||||
//! Our unique ID
|
//! Our unique ID
|
||||||
PeerID id;
|
PeerID id;
|
||||||
@@ -172,12 +196,17 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
bool validating_ = true;
|
bool validating_ = true;
|
||||||
bool proposing_ = true;
|
bool proposing_ = true;
|
||||||
|
|
||||||
|
ConsensusParms parms_;
|
||||||
|
std::size_t prevProposers_ = 0;
|
||||||
|
std::chrono::milliseconds prevRoundTime_;
|
||||||
|
|
||||||
//! All peers start from the default constructed ledger
|
//! All peers start from the default constructed ledger
|
||||||
Peer(PeerID i, BasicNetwork<Peer*>& n, UNL const& u, ConsensusParms p)
|
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}
|
, id{i}
|
||||||
, net{n}
|
, net{n}
|
||||||
, unl(u)
|
, unl(u)
|
||||||
|
, parms_(p)
|
||||||
{
|
{
|
||||||
ledgers[lastClosedLedger.id()] = lastClosedLedger;
|
ledgers[lastClosedLedger.id()] = lastClosedLedger;
|
||||||
}
|
}
|
||||||
@@ -210,12 +239,6 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const&
|
|
||||||
proposals(Ledger::ID const& ledgerHash)
|
|
||||||
{
|
|
||||||
return peerPositions_[ledgerHash];
|
|
||||||
}
|
|
||||||
|
|
||||||
TxSet const*
|
TxSet const*
|
||||||
acquireTxSet(TxSet::ID const& setId)
|
acquireTxSet(TxSet::ID const& setId)
|
||||||
{
|
{
|
||||||
@@ -245,17 +268,17 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result
|
Result
|
||||||
onClose(Ledger const& prevLedger, NetClock::time_point closeTime, Mode mode)
|
onClose(Ledger const& prevLedger, NetClock::time_point closeTime, ConsensusMode mode)
|
||||||
{
|
{
|
||||||
TxSet res{openTxs};
|
TxSet res{openTxs};
|
||||||
|
|
||||||
return Result{TxSet{openTxs},
|
return Result(TxSet{openTxs},
|
||||||
Proposal{prevLedger.id(),
|
Proposal(prevLedger.id(),
|
||||||
Proposal::seqJoin,
|
Proposal::seqJoin,
|
||||||
res.id(),
|
res.id(),
|
||||||
closeTime,
|
closeTime,
|
||||||
now(),
|
now(),
|
||||||
id}};
|
id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -263,10 +286,17 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
Result const& result,
|
Result const& result,
|
||||||
Ledger const& prevLedger,
|
Ledger const& prevLedger,
|
||||||
NetClock::duration const& closeResolution,
|
NetClock::duration const& closeResolution,
|
||||||
CloseTimes const& rawCloseTimes,
|
ConsensusCloseTimes const& rawCloseTimes,
|
||||||
Mode const& mode)
|
ConsensusMode const& mode,
|
||||||
|
Json::Value && consensusJson)
|
||||||
{
|
{
|
||||||
onAccept(result, prevLedger, closeResolution, rawCloseTimes, mode);
|
onAccept(
|
||||||
|
result,
|
||||||
|
prevLedger,
|
||||||
|
closeResolution,
|
||||||
|
rawCloseTimes,
|
||||||
|
mode,
|
||||||
|
std::move(consensusJson));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -274,8 +304,9 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
Result const& result,
|
Result const& result,
|
||||||
Ledger const& prevLedger,
|
Ledger const& prevLedger,
|
||||||
NetClock::duration const& closeResolution,
|
NetClock::duration const& closeResolution,
|
||||||
CloseTimes const& rawCloseTimes,
|
ConsensusCloseTimes const& rawCloseTimes,
|
||||||
Mode const& mode)
|
ConsensusMode const& mode,
|
||||||
|
Json::Value && consensusJson)
|
||||||
{
|
{
|
||||||
auto newLedger = prevLedger.close(
|
auto newLedger = prevLedger.close(
|
||||||
result.set.txs_,
|
result.set.txs_,
|
||||||
@@ -283,7 +314,8 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
rawCloseTimes.self,
|
rawCloseTimes.self,
|
||||||
result.position.closeTime() != NetClock::time_point{});
|
result.position.closeTime() != NetClock::time_point{});
|
||||||
ledgers[newLedger.id()] = newLedger;
|
ledgers[newLedger.id()] = newLedger;
|
||||||
|
prevProposers_ = result.proposers;
|
||||||
|
prevRoundTime_ = result.roundTime.read();
|
||||||
lastClosedLedger = newLedger;
|
lastClosedLedger = newLedger;
|
||||||
|
|
||||||
auto it =
|
auto it =
|
||||||
@@ -304,16 +336,16 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
// TODO: reconsider this and instead just save LCL generated here?
|
// TODO: reconsider this and instead just save LCL generated here?
|
||||||
if (completedLedgers <= targetLedgers)
|
if (completedLedgers <= targetLedgers)
|
||||||
{
|
{
|
||||||
startRound(
|
consensus.startRound(
|
||||||
now(), lastClosedLedger.id(), lastClosedLedger, proposing_);
|
now(), lastClosedLedger.id(), lastClosedLedger, proposing_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ledger::ID
|
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
|
// TODO: Use generic validation code
|
||||||
if (mode != Mode::wrongLedger && ledgerID.seq > 0 &&
|
if (mode != ConsensusMode::wrongLedger && ledgerID.seq > 0 &&
|
||||||
ledger.id().seq > 0)
|
ledger.id().seq > 0)
|
||||||
return peerValidations.getBestLCL(ledgerID, ledger.parentID());
|
return peerValidations.getBestLCL(ledgerID, ledger.parentID());
|
||||||
return ledgerID;
|
return ledgerID;
|
||||||
@@ -323,24 +355,31 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
propose(Proposal const& pos)
|
propose(Proposal const& pos)
|
||||||
{
|
{
|
||||||
if (proposing_)
|
if (proposing_)
|
||||||
relay(pos);
|
relay(PeerPosition(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsensusParms const &
|
||||||
|
parms() const
|
||||||
|
{
|
||||||
|
return parms_;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
// non-callback helpers
|
// non-callback helpers
|
||||||
void
|
void
|
||||||
receive(Proposal const& p)
|
receive(PeerPosition const& peerPos)
|
||||||
{
|
{
|
||||||
|
Proposal const & p = peerPos.proposal();
|
||||||
if (unl.find(p.nodeID()) == unl.end())
|
if (unl.find(p.nodeID()) == unl.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO: Be sure this is a new proposal!!!!!
|
// TODO: Supress repeats more efficiently
|
||||||
auto& dest = peerPositions_[p.prevLedger()];
|
auto& dest = peerPositions_[p.prevLedger()];
|
||||||
if (std::find(dest.begin(), dest.end(), p) != dest.end())
|
if (std::find(dest.begin(), dest.end(), p) != dest.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dest.push_back(p);
|
dest.push_back(p);
|
||||||
peerProposal(now(), p);
|
consensus.peerProposal(now(), peerPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -349,7 +388,7 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
// save and map complete?
|
// save and map complete?
|
||||||
auto it = txSets.insert(std::make_pair(txs.id(), txs));
|
auto it = txSets.insert(std::make_pair(txs.id(), txs));
|
||||||
if (it.second)
|
if (it.second)
|
||||||
gotTxSet(now(), txs);
|
consensus.gotTxSet(now(), txs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -392,7 +431,7 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
void
|
void
|
||||||
timerEntry()
|
timerEntry()
|
||||||
{
|
{
|
||||||
Base::timerEntry(now());
|
consensus.timerEntry(now());
|
||||||
// only reschedule if not completed
|
// only reschedule if not completed
|
||||||
if (completedLedgers < targetLedgers)
|
if (completedLedgers < targetLedgers)
|
||||||
net.timer(parms().ledgerGRANULARITY, [&]() { timerEntry(); });
|
net.timer(parms().ledgerGRANULARITY, [&]() { timerEntry(); });
|
||||||
@@ -406,7 +445,7 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
// so there is no gaurantee that bestLCL == lastClosedLedger.id()
|
// so there is no gaurantee that bestLCL == lastClosedLedger.id()
|
||||||
auto bestLCL = peerValidations.getBestLCL(
|
auto bestLCL = peerValidations.getBestLCL(
|
||||||
lastClosedLedger.id(), lastClosedLedger.parentID());
|
lastClosedLedger.id(), lastClosedLedger.parentID());
|
||||||
startRound(now(), bestLCL, lastClosedLedger, proposing_);
|
consensus.startRound(now(), bestLCL, lastClosedLedger, proposing_);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetClock::time_point
|
NetClock::time_point
|
||||||
@@ -435,6 +474,28 @@ struct Peer : public Consensus<Peer, Traits>
|
|||||||
else
|
else
|
||||||
net.timer(when, std::forward<T>(what));
|
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
|
} // csf
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
#include <test/app/Transaction_ordering_test.cpp>
|
#include <test/app/Transaction_ordering_test.cpp>
|
||||||
#include <test/app/TrustAndBalance_test.cpp>
|
#include <test/app/TrustAndBalance_test.cpp>
|
||||||
#include <test/app/TxQ_test.cpp>
|
#include <test/app/TxQ_test.cpp>
|
||||||
|
#include <test/app/ValidatorKeys_test.cpp>
|
||||||
#include <test/app/ValidatorList_test.cpp>
|
#include <test/app/ValidatorList_test.cpp>
|
||||||
#include <test/app/ValidatorSite_test.cpp>
|
#include <test/app/ValidatorSite_test.cpp>
|
||||||
#include <test/app/SetTrust_test.cpp>
|
#include <test/app/SetTrust_test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user