Expose consensus parameters for simulation (RIPD-1355)

This commit is contained in:
Brad Chase
2017-03-31 14:30:27 -04:00
committed by seelabs
parent 7ae3c91015
commit 3dfb4a13f1
16 changed files with 441 additions and 282 deletions

View File

@@ -1859,16 +1859,18 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h"> <ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\consensus\Consensus.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\consensus\Consensus.h"> <ClInclude Include="..\..\src\ripple\consensus\Consensus.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\ConsensusParms.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h"> <ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h"> <ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\consensus\LedgerTiming.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h"> <ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\Validations.h"> <ClInclude Include="..\..\src\ripple\consensus\Validations.h">

View File

@@ -2502,18 +2502,21 @@
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h"> <ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
<Filter>ripple\conditions\impl</Filter> <Filter>ripple\conditions\impl</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\consensus\Consensus.cpp">
<Filter>ripple\consensus</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\consensus\Consensus.h"> <ClInclude Include="..\..\src\ripple\consensus\Consensus.h">
<Filter>ripple\consensus</Filter> <Filter>ripple\consensus</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\ConsensusParms.h">
<Filter>ripple\consensus</Filter>
</ClInclude>
<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\DisputedTx.h"> <ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
<Filter>ripple\consensus</Filter> <Filter>ripple\consensus</Filter>
</ClInclude> </ClInclude>
<ClCompile Include="..\..\src\ripple\consensus\LedgerTiming.cpp">
<Filter>ripple\consensus</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h"> <ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
<Filter>ripple\consensus</Filter> <Filter>ripple\consensus</Filter>
</ClInclude> </ClInclude>

View File

@@ -114,6 +114,7 @@ INPUT = \
../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 \
../src/ripple/consensus/ConsensusParms.h \
../src/ripple/app/consensus/RCLCxTx.h \ ../src/ripple/app/consensus/RCLCxTx.h \
../src/ripple/app/consensus/RCLCxLedger.h \ ../src/ripple/app/consensus/RCLCxLedger.h \
../src/ripple/app/consensus/RCLConsensus.h \ ../src/ripple/app/consensus/RCLConsensus.h \

View File

@@ -50,7 +50,7 @@ RCLConsensus::RCLConsensus(
InboundTransactions& inboundTransactions, InboundTransactions& inboundTransactions,
typename Base::clock_type const& clock, typename Base::clock_type const& clock,
beast::Journal journal) beast::Journal journal)
: Base(clock, journal) : Base(clock, ConsensusParms{}, journal)
, app_(app) , app_(app)
, feeVote_(std::move(feeVote)) , feeVote_(std::move(feeVote))
, ledgerMaster_(ledgerMaster) , ledgerMaster_(ledgerMaster)

View File

@@ -25,7 +25,7 @@
#include <ripple/app/ledger/AcceptedLedger.h> #include <ripple/app/ledger/AcceptedLedger.h>
#include <ripple/app/ledger/InboundLedgers.h> #include <ripple/app/ledger/InboundLedgers.h>
#include <ripple/app/ledger/LedgerMaster.h> #include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/consensus/LedgerTiming.h> #include <ripple/consensus/ConsensusParms.h>
#include <ripple/app/ledger/LedgerToJson.h> #include <ripple/app/ledger/LedgerToJson.h>
#include <ripple/app/ledger/LocalTxs.h> #include <ripple/app/ledger/LocalTxs.h>
#include <ripple/app/ledger/OpenLedger.h> #include <ripple/app/ledger/OpenLedger.h>
@@ -639,7 +639,7 @@ void NetworkOPsImp::setStateTimer ()
void NetworkOPsImp::setHeartbeatTimer () void NetworkOPsImp::setHeartbeatTimer ()
{ {
m_heartbeatTimer.setExpiration (LEDGER_GRANULARITY); m_heartbeatTimer.setExpiration (mConsensus->parms().ledgerGRANULARITY);
} }
void NetworkOPsImp::setClusterTimer () void NetworkOPsImp::setClusterTimer ()

View File

@@ -19,9 +19,7 @@
#include <BeastConfig.h> #include <BeastConfig.h>
#include <ripple/basics/Log.h> #include <ripple/basics/Log.h>
#include <ripple/consensus/LedgerTiming.h> #include <ripple/consensus/Consensus.h>
#include <algorithm>
#include <iterator>
namespace ripple { namespace ripple {
@@ -35,11 +33,13 @@ shouldCloseLedger(
std::chrono::milliseconds std::chrono::milliseconds
timeSincePrevClose, // Time since last ledger's close time timeSincePrevClose, // Time since last ledger's close time
std::chrono::milliseconds openTime, // Time waiting to close this ledger std::chrono::milliseconds openTime, // Time waiting to close this ledger
std::chrono::seconds idleInterval, std::chrono::milliseconds idleInterval,
ConsensusParms const& parms,
beast::Journal j) beast::Journal j)
{ {
using namespace std::chrono_literals; using namespace std::chrono_literals;
if ((prevRoundTime < -1s) || (prevRoundTime > 10min) || (timeSincePrevClose > 10min)) if ((prevRoundTime < -1s) || (prevRoundTime > 10min) ||
(timeSincePrevClose > 10min))
{ {
// These are unexpected cases, we just close the ledger // These are unexpected cases, we just close the ledger
JLOG(j.warn()) << "shouldCloseLedger Trans=" JLOG(j.warn()) << "shouldCloseLedger Trans="
@@ -64,7 +64,7 @@ shouldCloseLedger(
} }
// Preserve minimum ledger open time // Preserve minimum ledger open time
if (openTime < LEDGER_MIN_CLOSE) if (openTime < parms.ledgerMIN_CLOSE)
{ {
JLOG(j.debug()) << "Must wait minimum time before closing"; JLOG(j.debug()) << "Must wait minimum time before closing";
return false; return false;
@@ -84,7 +84,11 @@ shouldCloseLedger(
} }
bool bool
checkConsensusReached(std::size_t agreeing, std::size_t total, bool count_self) checkConsensusReached(
std::size_t agreeing,
std::size_t total,
bool count_self,
std::size_t minConsensusPct)
{ {
// If we are alone, we have a consensus // If we are alone, we have a consensus
if (total == 0) if (total == 0)
@@ -96,9 +100,9 @@ checkConsensusReached(std::size_t agreeing, std::size_t total, bool count_self)
++total; ++total;
} }
int currentPercentage = (agreeing * 100) / total; std::size_t currentPercentage = (agreeing * 100) / total;
return currentPercentage > minimumConsensusPercentage; return currentPercentage > minConsensusPct;
} }
ConsensusState ConsensusState
@@ -109,6 +113,7 @@ checkConsensus(
std::size_t currentFinished, std::size_t currentFinished,
std::chrono::milliseconds previousAgreeTime, std::chrono::milliseconds previousAgreeTime,
std::chrono::milliseconds currentAgreeTime, std::chrono::milliseconds currentAgreeTime,
ConsensusParms const& parms,
bool proposing, bool proposing,
beast::Journal j) beast::Journal j)
{ {
@@ -118,14 +123,14 @@ checkConsensus(
<< " time=" << currentAgreeTime.count() << "/" << " time=" << currentAgreeTime.count() << "/"
<< previousAgreeTime.count(); << previousAgreeTime.count();
if (currentAgreeTime <= LEDGER_MIN_CONSENSUS) if (currentAgreeTime <= parms.ledgerMIN_CONSENSUS)
return ConsensusState::No; return ConsensusState::No;
if (currentProposers < (prevProposers * 3 / 4)) if (currentProposers < (prevProposers * 3 / 4))
{ {
// Less than 3/4 of the last ledger's proposers are present; don't // Less than 3/4 of the last ledger's proposers are present; don't
// rush: we may need more time. // rush: we may need more time.
if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS)) if (currentAgreeTime < (previousAgreeTime + parms.ledgerMIN_CONSENSUS))
{ {
JLOG(j.trace()) << "too fast, not enough proposers"; JLOG(j.trace()) << "too fast, not enough proposers";
return ConsensusState::No; return ConsensusState::No;
@@ -134,7 +139,8 @@ checkConsensus(
// Have we, together with the nodes on our UNL list, reached the threshold // Have we, together with the nodes on our UNL list, reached the threshold
// to declare consensus? // to declare consensus?
if (checkConsensusReached(currentAgree, currentProposers, proposing)) if (checkConsensusReached(
currentAgree, currentProposers, proposing, parms.minCONSENSUS_PCT))
{ {
JLOG(j.debug()) << "normal consensus"; JLOG(j.debug()) << "normal consensus";
return ConsensusState::Yes; return ConsensusState::Yes;
@@ -142,7 +148,8 @@ checkConsensus(
// Have sufficient nodes on our UNL list moved on and reached the threshold // Have sufficient nodes on our UNL list moved on and reached the threshold
// to declare consensus? // to declare consensus?
if (checkConsensusReached(currentFinished, currentProposers, false)) if (checkConsensusReached(
currentFinished, currentProposers, false, parms.minCONSENSUS_PCT))
{ {
JLOG(j.warn()) << "We see no consensus, but 80% of nodes have moved on"; JLOG(j.warn()) << "We see no consensus, but 80% of nodes have moved on";
return ConsensusState::MovedOn; return ConsensusState::MovedOn;
@@ -153,4 +160,4 @@ checkConsensus(
return ConsensusState::No; return ConsensusState::No;
} }
} // ripple } // namespace ripple

View File

@@ -24,11 +24,79 @@
#include <ripple/basics/chrono.h> #include <ripple/basics/chrono.h>
#include <ripple/beast/utility/Journal.h> #include <ripple/beast/utility/Journal.h>
#include <ripple/consensus/ConsensusProposal.h> #include <ripple/consensus/ConsensusProposal.h>
#include <ripple/consensus/ConsensusParms.h>
#include <ripple/consensus/LedgerTiming.h>
#include <ripple/consensus/DisputedTx.h> #include <ripple/consensus/DisputedTx.h>
#include <ripple/json/json_writer.h> #include <ripple/json/json_writer.h>
namespace ripple { namespace ripple {
/** Determines whether the current ledger should close at this time.
This function should be called when a ledger is open and there is no close
in progress, or when a transaction is received and no close is in progress.
@param anyTransactions indicates whether any transactions have been received
@param prevProposers proposers in the last closing
@param proposersClosed proposers who have currently closed this ledger
@param proposersValidated proposers who have validated the last closed
ledger
@param prevRoundTime time for the previous ledger to reach consensus
@param timeSincePrevClose time since the previous ledger's (possibly rounded)
close time
@param openTime duration this ledger has been open
@param idleInterval the network's desired idle interval
@param parms Consensus constant parameters
@param j journal for logging
*/
bool
shouldCloseLedger(
bool anyTransactions,
std::size_t prevProposers,
std::size_t proposersClosed,
std::size_t proposersValidated,
std::chrono::milliseconds prevRoundTime,
std::chrono::milliseconds timeSincePrevClose,
std::chrono::milliseconds openTime,
std::chrono::milliseconds idleInterval,
ConsensusParms const & parms,
beast::Journal j);
/** 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
};
/** Determine whether the network reached consensus and whether we joined.
@param prevProposers proposers in the last closing (not including us)
@param currentProposers proposers in this closing so far (not including us)
@param currentAgree proposers who agree with us
@param currentFinished proposers who have validated a ledger after this one
@param previousAgreeTime how long, in milliseconds, it took to agree on the
last ledger
@param currentAgreeTime how long, in milliseconds, we've been trying to
agree
@param parms Consensus constant parameters
@param proposing whether we should count ourselves
@param j journal for logging
*/
ConsensusState
checkConsensus(
std::size_t prevProposers,
std::size_t currentProposers,
std::size_t currentAgree,
std::size_t currentFinished,
std::chrono::milliseconds previousAgreeTime,
std::chrono::milliseconds currentAgreeTime,
ConsensusParms const & parms,
bool proposing,
beast::Journal j);
/** Generic implementation of consensus algorithm. /** Generic implementation of consensus algorithm.
Achieves consensus on the next ledger. Achieves consensus on the next ledger.
@@ -351,9 +419,10 @@ public:
/** Constructor. /** Constructor.
@param clock The clock used to internally sample consensus progress @param clock The clock used to internally sample consensus progress
@param p Consensus parameters to use
@param j The journal to log debug output @param j The journal to log debug output
*/ */
Consensus(clock_type const& clock, beast::Journal j); Consensus(clock_type const& clock, ConsensusParms const & p, beast::Journal j);
/** Kick-off the next round of consensus. /** Kick-off the next round of consensus.
@@ -474,6 +543,13 @@ public:
Json::Value Json::Value
getJson(bool full) const; getJson(bool full) const;
/** Get the consensus parameters
*/
ConsensusParms const &
parms() const
{
return parms_;
}
protected: protected:
// Prevent deleting derived instance through base pointer // Prevent deleting derived instance through base pointer
@@ -584,7 +660,7 @@ private:
NetClock::duration closeResolution_ = ledgerDefaultTimeResolution; NetClock::duration closeResolution_ = ledgerDefaultTimeResolution;
// Time it took for the last consensus round to converge // Time it took for the last consensus round to converge
std::chrono::milliseconds prevRoundTime_ = LEDGER_IDLE_INTERVAL; std::chrono::milliseconds prevRoundTime_;
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Network time measurements of consensus progress // Network time measurements of consensus progress
@@ -618,6 +694,9 @@ private:
// nodes that have bowed out of this consensus process // nodes that have bowed out of this consensus process
hash_set<NodeID_t> deadNodes_; hash_set<NodeID_t> deadNodes_;
// Parameters that control consensus algorithm
ConsensusParms const parms_;
// Journal for debugging // Journal for debugging
beast::Journal j_; beast::Journal j_;
}; };
@@ -625,9 +704,12 @@ private:
template <class Derived, class Traits> template <class Derived, class Traits>
Consensus<Derived, Traits>::Consensus( Consensus<Derived, Traits>::Consensus(
clock_type const& clock, clock_type const& clock,
ConsensusParms const & p,
beast::Journal journal) beast::Journal journal)
: lock_(std::make_unique<std::recursive_mutex>()) : lock_{std::make_unique<std::recursive_mutex>()}
, clock_(clock) , clock_{clock}
, prevRoundTime_{p.ledgerIDLE_INTERVAL}
, parms_(p)
, j_{journal} , j_{journal}
{ {
JLOG(j_.debug()) << "Creating consensus object"; JLOG(j_.debug()) << "Creating consensus object";
@@ -1112,9 +1194,9 @@ Consensus<Derived, Traits>::phaseOpen()
sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_); sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_);
} }
auto const idleInterval = std::max<seconds>( auto const idleInterval = std::max<milliseconds>(
LEDGER_IDLE_INTERVAL, parms_.ledgerIDLE_INTERVAL,
duration_cast<seconds>(2 * previousLedger_.closeTimeResolution())); 2 * previousLedger_.closeTimeResolution());
// Decide if we should close the ledger // Decide if we should close the ledger
if (shouldCloseLedger( if (shouldCloseLedger(
@@ -1126,6 +1208,7 @@ Consensus<Derived, Traits>::phaseOpen()
sinceClose, sinceClose,
openTime_.read(), openTime_.read(),
idleInterval, idleInterval,
parms_,
j_)) j_))
{ {
closeLedger(); closeLedger();
@@ -1143,10 +1226,10 @@ Consensus<Derived, Traits>::phaseEstablish()
result_->roundTime.tick(clock_.now()); result_->roundTime.tick(clock_.now());
convergePercent_ = result_->roundTime.read() * 100 / convergePercent_ = result_->roundTime.read() * 100 /
std::max<milliseconds>(prevRoundTime_, AV_MIN_CONSENSUS_TIME); std::max<milliseconds>(prevRoundTime_, parms_.avMIN_CONSENSUS_TIME);
// Give everyone a chance to take an initial position // Give everyone a chance to take an initial position
if (result_->roundTime.read() < LEDGER_MIN_CONSENSUS) if (result_->roundTime.read() < parms_.ledgerMIN_CONSENSUS)
return; return;
updateOurPositions(); updateOurPositions();
@@ -1230,8 +1313,8 @@ Consensus<Derived, Traits>::updateOurPositions()
assert(result_); assert(result_);
// Compute a cutoff time // Compute a cutoff time
auto const peerCutoff = now_ - PROPOSE_FRESHNESS; auto const peerCutoff = now_ - parms_.proposeFRESHNESS;
auto const ourCutoff = now_ - PROPOSE_INTERVAL; auto const ourCutoff = now_ - parms_.proposeINTERVAL;
// Verify freshness of peer positions and compute close times // Verify freshness of peer positions and compute close times
std::map<NetClock::time_point, int> effCloseTimes; std::map<NetClock::time_point, int> effCloseTimes;
@@ -1271,7 +1354,7 @@ Consensus<Derived, Traits>::updateOurPositions()
// Because the threshold for inclusion increases, // Because the threshold for inclusion increases,
// time can change our position on a dispute // time can change our position on a dispute
if (it.second.updateVote( if (it.second.updateVote(
convergePercent_, (mode_ == Mode::proposing))) convergePercent_, (mode_ == Mode::proposing), parms_))
{ {
if (!mutableSet) if (!mutableSet)
mutableSet.emplace(result_->set); mutableSet.emplace(result_->set);
@@ -1309,14 +1392,14 @@ Consensus<Derived, Traits>::updateOurPositions()
{ {
int neededWeight; int neededWeight;
if (convergePercent_ < AV_MID_CONSENSUS_TIME) if (convergePercent_ < parms_.avMID_CONSENSUS_TIME)
neededWeight = AV_INIT_CONSENSUS_PCT; neededWeight = parms_.avINIT_CONSENSUS_PCT;
else if (convergePercent_ < AV_LATE_CONSENSUS_TIME) else if (convergePercent_ < parms_.avLATE_CONSENSUS_TIME)
neededWeight = AV_MID_CONSENSUS_PCT; neededWeight = parms_.avMID_CONSENSUS_PCT;
else if (convergePercent_ < AV_STUCK_CONSENSUS_TIME) else if (convergePercent_ < parms_.avSTUCK_CONSENSUS_TIME)
neededWeight = AV_LATE_CONSENSUS_PCT; neededWeight = parms_.avLATE_CONSENSUS_PCT;
else else
neededWeight = AV_STUCK_CONSENSUS_PCT; neededWeight = parms_.avSTUCK_CONSENSUS_PCT;
int participants = peerProposals_.size(); int participants = peerProposals_.size();
if (mode_ == Mode::proposing) if (mode_ == Mode::proposing)
@@ -1333,7 +1416,7 @@ Consensus<Derived, Traits>::updateOurPositions()
// Threshold to declare consensus // Threshold to declare consensus
int const threshConsensus = int const threshConsensus =
participantsNeeded(participants, AV_CT_CONSENSUS_PCT); participantsNeeded(participants, parms_.avCT_CONSENSUS_PCT);
JLOG(j_.info()) << "Proposers:" << peerProposals_.size() JLOG(j_.info()) << "Proposers:" << peerProposals_.size()
<< " nw:" << neededWeight << " thrV:" << threshVote << " nw:" << neededWeight << " thrV:" << threshVote
@@ -1454,6 +1537,7 @@ Consensus<Derived, Traits>::haveConsensus()
currentFinished, currentFinished,
prevRoundTime_, prevRoundTime_,
result_->roundTime.read(), result_->roundTime.read(),
parms_,
mode_ == Mode::proposing, mode_ == Mode::proposing,
j_); j_);

View File

@@ -0,0 +1,135 @@
//------------------------------------------------------------------------------
/*
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_PARMS_H_INCLUDED
#define RIPPLE_CONSENSUS_CONSENSUS_PARMS_H_INCLUDED
#include <chrono>
#include <cstddef>
namespace ripple {
using namespace std::chrono_literals;
/** Consensus algorithm parameters
Parameters which control the consensus algorithm. This are not
meant to be changed arbitrarily.
*/
struct ConsensusParms
{
//-------------------------------------------------------------------------
// Validation and proposal durations are relative to NetClock times, so use
// second resolution
/** The duration a validation remains current after its ledger's
close time.
This is a safety to protect against very old validations and the time
it takes to adjust the close time accuracy window.
*/
std::chrono::seconds validationVALID_WALL = 5min;
/** Duration a validation remains current after first observed.
The duration a validation remains current after the time we
first saw it. This provides faster recovery in very rare cases where the
number of validations produced by the network is lower than normal
*/
std::chrono::seconds validationVALID_LOCAL = 3min;
/** Duration pre-close in which validations are acceptable.
The number of seconds before a close time that we consider a validation
acceptable. This protects against extreme clock errors
*/
std::chrono::seconds validationVALID_EARLY = 3min;
//! How long we consider a proposal fresh
std::chrono::seconds proposeFRESHNESS = 20s;
//! How often we force generating a new proposal to keep ours fresh
std::chrono::seconds proposeINTERVAL = 12s;
//-------------------------------------------------------------------------
// Consensus durations are relative to the internal Consenus clock and use
// millisecond resolution.
//! The percentage threshold above which we can declare consensus.
std::size_t minCONSENSUS_PCT = 80;
//! The duration a ledger may remain idle before closing
std::chrono::milliseconds ledgerIDLE_INTERVAL = 15s;
//! The number of seconds we wait minimum to ensure participation
std::chrono::milliseconds ledgerMIN_CONSENSUS = 1950ms;
//! Minimum number of seconds to wait to ensure others have computed the LCL
std::chrono::milliseconds ledgerMIN_CLOSE = 2s;
//! How often we check state or change positions
std::chrono::milliseconds ledgerGRANULARITY = 1s;
/** The minimum amount of time to consider the previous round
to have taken.
The minimum amount of time to consider the previous round
to have taken. This ensures that there is an opportunity
for a round at each avalanche threshold even if the
previous consensus was very fast. This should be at least
twice the interval between proposals (0.7s) divided by
the interval between mid and late consensus ([85-50]/100).
*/
std::chrono::milliseconds avMIN_CONSENSUS_TIME = 5s;
//------------------------------------------------------------------------------
// Avalanche tuning
// As a function of the percent this round's duration is of the prior round,
// we increase the threshold for yes vots to add a tranasaction to our
// position.
//! Percentage of nodes on our UNL that must vote yes
std::size_t avINIT_CONSENSUS_PCT = 50;
//! Percentage of previous round duration before we advance
std::size_t avMID_CONSENSUS_TIME = 50;
//! Percentage of nodes that most vote yes after advancing
std::size_t avMID_CONSENSUS_PCT = 65;
//! Percentage of previous round duration before we advance
std::size_t avLATE_CONSENSUS_TIME = 85;
//! Percentage of nodes that most vote yes after advancing
std::size_t avLATE_CONSENSUS_PCT = 70;
//! Percentage of previous round duration before we are stuck
std::size_t avSTUCK_CONSENSUS_TIME = 200;
//! Percentage of nodes that must vote yes after we are stuck
std::size_t avSTUCK_CONSENSUS_PCT = 95;
//! Percentage of nodes required to reach agreement on ledger close time
std::size_t avCT_CONSENSUS_PCT = 75;
};
} // ripple
#endif

View File

@@ -23,7 +23,7 @@
#include <ripple/basics/Log.h> #include <ripple/basics/Log.h>
#include <ripple/basics/base_uint.h> #include <ripple/basics/base_uint.h>
#include <ripple/beast/utility/Journal.h> #include <ripple/beast/utility/Journal.h>
#include <ripple/consensus/LedgerTiming.h> #include <ripple/consensus/ConsensusParms.h>
#include <ripple/protocol/Serializer.h> #include <ripple/protocol/Serializer.h>
#include <ripple/protocol/UintTypes.h> #include <ripple/protocol/UintTypes.h>
#include <memory> #include <memory>
@@ -112,10 +112,11 @@ public:
@param percentTime Percentage progress through consensus, e.g. 50% @param percentTime Percentage progress through consensus, e.g. 50%
through or 90%. through or 90%.
@param proposing Whether we are proposing to our peers in this round. @param proposing Whether we are proposing to our peers in this round.
@param p Consensus parameters controlling thresholds for voting
@return Whether our vote changed @return Whether our vote changed
*/ */
bool bool
updateVote(int percentTime, bool proposing); updateVote(int percentTime, bool proposing, ConsensusParms const& p);
//! JSON representation of dispute, used for debugging //! JSON representation of dispute, used for debugging
Json::Value Json::Value
@@ -190,7 +191,10 @@ DisputedTx<Tx_t, NodeID_t>::unVote(NodeID_t const& peer)
template <class Tx_t, class NodeID_t> template <class Tx_t, class NodeID_t>
bool bool
DisputedTx<Tx_t, NodeID_t>::updateVote(int percentTime, bool proposing) DisputedTx<Tx_t, NodeID_t>::updateVote(
int percentTime,
bool proposing,
ConsensusParms const& p)
{ {
if (ourVote_ && (nays_ == 0)) if (ourVote_ && (nays_ == 0))
return false; return false;
@@ -212,14 +216,14 @@ DisputedTx<Tx_t, NodeID_t>::updateVote(int percentTime, bool proposing)
// //
// To prevent avalanche stalls, we increase the needed weight slightly // To prevent avalanche stalls, we increase the needed weight slightly
// over time. // over time.
if (percentTime < AV_MID_CONSENSUS_TIME) if (percentTime < p.avMID_CONSENSUS_TIME)
newPosition = weight > AV_INIT_CONSENSUS_PCT; newPosition = weight > p.avINIT_CONSENSUS_PCT;
else if (percentTime < AV_LATE_CONSENSUS_TIME) else if (percentTime < p.avLATE_CONSENSUS_TIME)
newPosition = weight > AV_MID_CONSENSUS_PCT; newPosition = weight > p.avMID_CONSENSUS_PCT;
else if (percentTime < AV_STUCK_CONSENSUS_TIME) else if (percentTime < p.avSTUCK_CONSENSUS_TIME)
newPosition = weight > AV_LATE_CONSENSUS_PCT; newPosition = weight > p.avLATE_CONSENSUS_PCT;
else else
newPosition = weight > AV_STUCK_CONSENSUS_PCT; newPosition = weight > p.avSTUCK_CONSENSUS_PCT;
} }
else else
{ {

View File

@@ -27,15 +27,9 @@
namespace ripple { namespace ripple {
//------------------------------------------------------------------------------
// These are protocol parameters used to control the behavior of the system and
// they should not be changed arbitrarily.
//! The percentage threshold above which we can declare consensus.
auto constexpr minimumConsensusPercentage = 80;
using namespace std::chrono_literals; using namespace std::chrono_literals;
/** Possible close time resolutions.
/** Possible ledger close time resolutions.
Values should not be duplicated. Values should not be duplicated.
@see getNextLedgerTimeResolution @see getNextLedgerTimeResolution
@@ -52,61 +46,6 @@ auto constexpr increaseLedgerTimeResolutionEvery = 8;
//! How often we decrease the close time resolution (in numbers of ledgers) //! How often we decrease the close time resolution (in numbers of ledgers)
auto constexpr decreaseLedgerTimeResolutionEvery = 1; auto constexpr decreaseLedgerTimeResolutionEvery = 1;
//! The number of seconds a ledger may remain idle before closing
auto constexpr LEDGER_IDLE_INTERVAL = 15s;
//! The number of seconds we wait minimum to ensure participation
auto constexpr LEDGER_MIN_CONSENSUS = 1950ms;
//! Minimum number of seconds to wait to ensure others have computed the LCL
auto constexpr LEDGER_MIN_CLOSE = 2s;
//! How often we check state or change positions
auto constexpr LEDGER_GRANULARITY = 1s;
//! How long we consider a proposal fresh
auto constexpr PROPOSE_FRESHNESS = 20s;
//! How often we force generating a new proposal to keep ours fresh
auto constexpr PROPOSE_INTERVAL = 12s;
//------------------------------------------------------------------------------
// Avalanche tuning
//! Percentage of nodes on our UNL that must vote yes
auto constexpr AV_INIT_CONSENSUS_PCT = 50;
//! Percentage of previous close time before we advance
auto constexpr AV_MID_CONSENSUS_TIME = 50;
//! Percentage of nodes that most vote yes after advancing
auto constexpr AV_MID_CONSENSUS_PCT = 65;
//! Percentage of previous close time before we advance
auto constexpr AV_LATE_CONSENSUS_TIME = 85;
//! Percentage of nodes that most vote yes after advancing
auto constexpr AV_LATE_CONSENSUS_PCT = 70;
//! Percentage of previous close time before we are stuck
auto constexpr AV_STUCK_CONSENSUS_TIME = 200;
//! Percentage of nodes that must vote yes after we are stuck
auto constexpr AV_STUCK_CONSENSUS_PCT = 95;
//! Percentage of nodes required to reach agreement on ledger close time
auto constexpr AV_CT_CONSENSUS_PCT = 75;
/** The minimum amount of time to consider the previous round
to have taken.
The minimum amount of time to consider the previous round
to have taken. This ensures that there is an opportunity
for a round at each avalanche threshold even if the
previous consensus was very fast. This should be at least
twice the interval between proposals (0.7s) divided by
the interval between mid and late consensus ([85-50]/100).
*/
auto constexpr AV_MIN_CONSENSUS_TIME = 5s;
/** Calculates the close time resolution for the specified ledger. /** Calculates the close time resolution for the specified ledger.
@@ -200,6 +139,8 @@ effCloseTime(
typename time_point::duration const resolution, typename time_point::duration const resolution,
time_point priorCloseTime) time_point priorCloseTime)
{ {
using namespace std::chrono_literals;
if (closeTime == time_point{}) if (closeTime == time_point{})
return closeTime; return closeTime;
@@ -207,78 +148,5 @@ effCloseTime(
roundCloseTime(closeTime, resolution), (priorCloseTime + 1s)); roundCloseTime(closeTime, resolution), (priorCloseTime + 1s));
} }
/** Determines whether the current ledger should close at this time. }
This function should be called when a ledger is open and there is no close
in progress, or when a transaction is received and no close is in progress.
@param anyTransactions indicates whether any transactions have been received
@param prevProposers proposers in the last closing
@param proposersClosed proposers who have currently closed this ledger
@param proposersValidated proposers who have validated the last closed
ledger
@param prevRoundTime time for the previous ledger to reach consensus
@param timeSincePrevClose time since the previous ledger's (possibly rounded)
close time
@param openTime duration this ledger has been open
@param idleInterval the network's desired idle interval
@param j journal for logging
*/
bool
shouldCloseLedger(
bool anyTransactions,
std::size_t prevProposers,
std::size_t proposersClosed,
std::size_t proposersValidated,
std::chrono::milliseconds prevRoundTime,
std::chrono::milliseconds timeSincePrevClose,
std::chrono::milliseconds openTime,
std::chrono::seconds idleInterval,
beast::Journal j);
/** Determine if a consensus has been reached
This function determines if a consensus has been reached
@param agreeing count of agreements with our position
@param total count of participants other than us
@param count_self whether we count ourselves
@return True if a consensus has been reached
*/
bool
checkConsensusReached(std::size_t agreeing, std::size_t total, bool count_self);
/** Whether we have or don't have a consensus */
enum class ConsensusState {
No, //!< We do not have consensus
MovedOn, //!< The network has consensus without us
Yes //!< We have consensus along with the network
};
/** Determine whether the network reached consensus and whether we joined.
@param prevProposers proposers in the last closing (not including us)
@param currentProposers proposers in this closing so far (not including us)
@param currentAgree proposers who agree with us
@param currentFinished proposers who have validated a ledger after this one
@param previousAgreeTime how long, in milliseconds, it took to agree on the
last ledger
@param currentAgreeTime how long, in milliseconds, we've been trying to
agree
@param proposing whether we should count ourselves
@param j journal for logging
*/
ConsensusState
checkConsensus(
std::size_t prevProposers,
std::size_t currentProposers,
std::size_t currentAgree,
std::size_t currentFinished,
std::chrono::milliseconds previousAgreeTime,
std::chrono::milliseconds currentAgreeTime,
bool proposing,
beast::Journal j);
} // ripple
#endif #endif

View File

@@ -18,4 +18,4 @@
//============================================================================== //==============================================================================
#include <BeastConfig.h> #include <BeastConfig.h>
#include <ripple/consensus/LedgerTiming.cpp> #include <ripple/consensus/Consensus.cpp>

View File

@@ -31,13 +31,97 @@ namespace test {
class Consensus_test : public beast::unit_test::suite class Consensus_test : public beast::unit_test::suite
{ {
public: public:
void
testShouldCloseLedger()
{
using namespace std::chrono_literals;
// Use default parameters
ConsensusParms p;
beast::Journal j;
// Bizarre times forcibly close
BEAST_EXPECT(
shouldCloseLedger(true, 10, 10, 10, -10s, 10s, 1s, 1s, p, j));
BEAST_EXPECT(
shouldCloseLedger(true, 10, 10, 10, 100h, 10s, 1s, 1s, p, j));
BEAST_EXPECT(
shouldCloseLedger(true, 10, 10, 10, 10s, 100h, 1s, 1s, p, j));
// Rest of network has closed
BEAST_EXPECT(
shouldCloseLedger(true, 10, 3, 5, 10s, 10s, 10s, 10s, p, j));
// No transactions means wait until end of internval
BEAST_EXPECT(
!shouldCloseLedger(false, 10, 0, 0, 1s, 1s, 1s, 10s, p, j));
BEAST_EXPECT(
shouldCloseLedger(false, 10, 0, 0, 1s, 10s, 1s, 10s, p, j));
// Enforce minimum ledger open time
BEAST_EXPECT(
!shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 1s, 10s, p, j));
// Don't go too much faster than last time
BEAST_EXPECT(
!shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 3s, 10s, p, j));
BEAST_EXPECT(
shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 10s, 10s, p, j));
}
void
testCheckConsensus()
{
using namespace std::chrono_literals;
// Use default parameterss
ConsensusParms p;
beast::Journal j;
// Not enough time has elapsed
BEAST_EXPECT(
ConsensusState::No ==
checkConsensus(10, 2, 2, 0, 3s, 2s, p, true, j));
// If not enough peers have propsed, ensure
// more time for proposals
BEAST_EXPECT(
ConsensusState::No ==
checkConsensus(10, 2, 2, 0, 3s, 4s, p, true, j));
// Enough time has elapsed and we all agree
BEAST_EXPECT(
ConsensusState::Yes ==
checkConsensus(10, 2, 2, 0, 3s, 10s, p, true, j));
// Enough time has elapsed and we don't yet agree
BEAST_EXPECT(
ConsensusState::No ==
checkConsensus(10, 2, 1, 0, 3s, 10s, p, true, j));
// Our peers have moved on
// Enough time has elapsed and we all agree
BEAST_EXPECT(
ConsensusState::MovedOn ==
checkConsensus(10, 2, 1, 8, 3s, 10s, p, true, j));
// No peers makes it easy to agree
BEAST_EXPECT(
ConsensusState::Yes ==
checkConsensus(0, 0, 0, 0, 3s, 10s, p, true, j));
}
void void
testStandalone() testStandalone()
{ {
using namespace std::chrono_literals;
using namespace csf; using namespace csf;
ConsensusParms parms;
auto tg = TrustGraph::makeComplete(1); auto tg = TrustGraph::makeComplete(1);
Sim s(tg, topology(tg, fixed{LEDGER_GRANULARITY})); Sim s(parms, tg, topology(tg, fixed{parms.ledgerGRANULARITY}));
auto& p = s.peers[0]; auto& p = s.peers[0];
@@ -63,10 +147,14 @@ public:
using namespace csf; using namespace csf;
using namespace std::chrono; using namespace std::chrono;
ConsensusParms parms;
auto tg = TrustGraph::makeComplete(5); auto tg = TrustGraph::makeComplete(5);
Sim sim( Sim sim(
parms,
tg, tg,
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)})); topology(
tg,
fixed{round<milliseconds>(0.2 * parms.ledgerGRANULARITY)}));
// everyone submits their own ID as a TX and relay it to peers // everyone submits their own ID as a TX and relay it to peers
for (auto& p : sim.peers) for (auto& p : sim.peers)
@@ -98,12 +186,13 @@ public:
for (auto isParticipant : {true, false}) for (auto isParticipant : {true, false})
{ {
ConsensusParms parms;
auto tg = TrustGraph::makeComplete(5); auto tg = TrustGraph::makeComplete(5);
Sim sim(tg, topology(tg, [](PeerID i, PeerID j) { Sim sim(parms, tg, topology(tg, [&](PeerID i, PeerID j) {
auto delayFactor = (i == 0 || j == 0) ? 1.1 : 0.2; auto delayFactor = (i == 0 || j == 0) ? 1.1 : 0.2;
return round<milliseconds>( return round<milliseconds>(
delayFactor * LEDGER_GRANULARITY); delayFactor * parms.ledgerGRANULARITY);
})); }));
sim.peers[0].proposing_ = sim.peers[0].validating_ = isParticipant; sim.peers[0].proposing_ = sim.peers[0].validating_ = isParticipant;
@@ -183,7 +272,7 @@ public:
// the skews need to be at least 10 seconds. // the skews need to be at least 10 seconds.
// Complicating this matter is that nodes will ignore proposals // Complicating this matter is that nodes will ignore proposals
// with times more than PROPOSE_FRESHNESS =20s in the past. So at // with times more than proposeFRESHNESS =20s in the past. So at
// the minimum granularity, we have at most 3 types of skews // the minimum granularity, we have at most 3 types of skews
// (0s,10s,20s). // (0s,10s,20s).
@@ -191,22 +280,26 @@ public:
// skew. Then no majority (1/3 < 1/2) of nodes will agree on an // skew. Then no majority (1/3 < 1/2) of nodes will agree on an
// actual close time. // actual close time.
ConsensusParms parms;
auto tg = TrustGraph::makeComplete(6); auto tg = TrustGraph::makeComplete(6);
Sim sim( Sim sim(
parms,
tg, tg,
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)})); topology(
tg,
fixed{round<milliseconds>(0.2 * parms.ledgerGRANULARITY)}));
// Run consensus without skew until we have a short close time // Run consensus without skew until we have a short close time
// resolution // resolution
while (sim.peers.front().lastClosedLedger.closeTimeResolution() >= while (sim.peers.front().lastClosedLedger.closeTimeResolution() >=
PROPOSE_FRESHNESS) parms.proposeFRESHNESS)
sim.run(1); sim.run(1);
// Introduce a shift on the time of half the peers // Introduce a shift on the time of half the peers
sim.peers[0].clockSkew = PROPOSE_FRESHNESS / 2; sim.peers[0].clockSkew = parms.proposeFRESHNESS / 2;
sim.peers[1].clockSkew = PROPOSE_FRESHNESS / 2; sim.peers[1].clockSkew = parms.proposeFRESHNESS / 2;
sim.peers[2].clockSkew = PROPOSE_FRESHNESS; sim.peers[2].clockSkew = parms.proposeFRESHNESS;
sim.peers[3].clockSkew = PROPOSE_FRESHNESS; sim.peers[3].clockSkew = parms.proposeFRESHNESS;
// Verify all peers have the same LCL and it has all the Txs // Verify all peers have the same LCL and it has all the Txs
sim.run(1); sim.run(1);
@@ -224,9 +317,12 @@ public:
// Specialized test to exercise a temporary fork in which some peers // Specialized test to exercise a temporary fork in which some peers
// are working on an incorrect prior ledger. // are working on an incorrect prior ledger.
ConsensusParms parms;
// Vary the time it takes to process validations to exercise detecting // Vary the time it takes to process validations to exercise detecting
// the wrong LCL at different phases of consensus // the wrong LCL at different phases of consensus
for (auto validationDelay : {0s, LEDGER_MIN_CLOSE}) for (auto validationDelay : {0ms, parms.ledgerMIN_CLOSE})
{ {
// Consider 10 peers: // Consider 10 peers:
// 0 1 2 3 4 5 6 7 8 9 // 0 1 2 3 4 5 6 7 8 9
@@ -256,10 +352,10 @@ public:
// This topology can fork, which is why we are using it for this // This topology can fork, which is why we are using it for this
// test. // test.
BEAST_EXPECT(tg.canFork(minimumConsensusPercentage / 100.)); BEAST_EXPECT(tg.canFork(parms.minCONSENSUS_PCT / 100.));
auto netDelay = round<milliseconds>(0.2 * LEDGER_GRANULARITY); auto netDelay = round<milliseconds>(0.2 * parms.ledgerGRANULARITY);
Sim sim(tg, topology(tg, fixed{netDelay})); Sim sim(parms, tg, topology(tg, fixed{netDelay}));
// initial round to set prior state // initial round to set prior state
sim.run(1); sim.run(1);
@@ -283,7 +379,7 @@ public:
// wrong ones) and recover within that round since wrong LCL // wrong ones) and recover within that round since wrong LCL
// is detected before we close // is detected before we close
// //
// With a validation delay of LEDGER_MIN_CLOSE, we need 3 more // With a validation delay of ledgerMIN_CLOSE, we need 3 more
// rounds. // rounds.
// 1. Round to generate different ledgers // 1. Round to generate different ledgers
// 2. Round to detect different prior ledgers (but still generate // 2. Round to detect different prior ledgers (but still generate
@@ -324,9 +420,6 @@ public:
// switchLCL switched from establish to open phase, but still processed // switchLCL switched from establish to open phase, but still processed
// the establish phase logic. // the establish phase logic.
{ {
using namespace csf;
using namespace std::chrono;
// A mostly disjoint topology // A mostly disjoint topology
std::vector<UNL> unls; std::vector<UNL> unls;
unls.push_back({0, 1}); unls.push_back({0, 1});
@@ -337,14 +430,14 @@ public:
TrustGraph tg{unls, membership}; TrustGraph tg{unls, membership};
Sim sim(tg, topology(tg, fixed{round<milliseconds>( Sim sim(parms, tg, topology(tg, fixed{round<milliseconds>(
0.2 * LEDGER_GRANULARITY)})); 0.2 * parms.ledgerGRANULARITY)}));
// initial ground to set prior state // initial ground to set prior state
sim.run(1); sim.run(1);
for (auto &p : sim.peers) { for (auto &p : sim.peers) {
// A long delay to acquire a missing ledger from the network // A long delay to acquire a missing ledger from the network
p.missingLedgerDelay = 2 * LEDGER_MIN_CLOSE; p.missingLedgerDelay = 2 * parms.ledgerMIN_CLOSE;
// Everyone sees only their own LCL // Everyone sees only their own LCL
p.openTxs.insert(Tx(p.id)); p.openTxs.insert(Tx(p.id));
@@ -367,11 +460,15 @@ public:
int numPeers = 10; int numPeers = 10;
for (int overlap = 0; overlap <= numPeers; ++overlap) for (int overlap = 0; overlap <= numPeers; ++overlap)
{ {
ConsensusParms parms;
auto tg = TrustGraph::makeClique(numPeers, overlap); auto tg = TrustGraph::makeClique(numPeers, overlap);
Sim sim( Sim sim(
parms,
tg, tg,
topology( topology(
tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)})); tg,
fixed{
round<milliseconds>(0.2 * parms.ledgerGRANULARITY)}));
// Initial round to set prior state // Initial round to set prior state
sim.run(1); sim.run(1);
@@ -406,7 +503,7 @@ public:
simClockSkew() simClockSkew()
{ {
using namespace csf; using namespace csf;
using namespace std::chrono_literals;
// Attempting to test what happens if peers enter consensus well // Attempting to test what happens if peers enter consensus well
// separated in time. Initial round (in which peers are not staggered) // separated in time. Initial round (in which peers are not staggered)
// is used to get the network going, then transactions are submitted // is used to get the network going, then transactions are submitted
@@ -422,8 +519,9 @@ public:
for (auto stagger : {800ms, 1600ms, 3200ms, 30000ms, 45000ms, 300000ms}) for (auto stagger : {800ms, 1600ms, 3200ms, 30000ms, 45000ms, 300000ms})
{ {
ConsensusParms parms;
auto tg = TrustGraph::makeComplete(5); auto tg = TrustGraph::makeComplete(5);
Sim sim(tg, topology(tg, [](PeerID i, PeerID) { Sim sim(parms, tg, topology(tg, [](PeerID i, PeerID) {
return 200ms * (i + 1); return 200ms * (i + 1);
})); }));
@@ -474,6 +572,7 @@ public:
double transProb = 0.5; double transProb = 0.5;
std::mt19937_64 rng; std::mt19937_64 rng;
ConsensusParms parms;
auto tg = TrustGraph::makeRandomRanked( auto tg = TrustGraph::makeRandomRanked(
N, N,
@@ -483,8 +582,11 @@ public:
rng); rng);
Sim sim{ Sim sim{
parms,
tg, tg,
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)})}; topology(
tg,
fixed{round<milliseconds>(0.2 * parms.ledgerGRANULARITY)})};
// Initial round to set prior state // Initial round to set prior state
sim.run(1); sim.run(1);
@@ -511,6 +613,9 @@ public:
void void
run() override run() override
{ {
testShouldCloseLedger();
testCheckConsensus();
testStandalone(); testStandalone();
testPeersAgree(); testPeersAgree();
testSlowPeer(); testSlowPeer();

View File

@@ -77,6 +77,7 @@ class LedgerTiming_test : public beast::unit_test::suite
void testRoundCloseTime() void testRoundCloseTime()
{ {
using namespace std::chrono_literals;
// A closeTime equal to the epoch is not modified // A closeTime equal to the epoch is not modified
using tp = NetClock::time_point; using tp = NetClock::time_point;
tp def; tp def;
@@ -94,67 +95,11 @@ class LedgerTiming_test : public beast::unit_test::suite
} }
void testShouldCloseLedger()
{
// Bizarre times forcibly close
BEAST_EXPECT(shouldCloseLedger(true, 10, 10, 10, -10s, 10s, 1s, 1s, j));
BEAST_EXPECT(shouldCloseLedger(true, 10, 10, 10, 100h, 10s, 1s, 1s, j));
BEAST_EXPECT(shouldCloseLedger(true, 10, 10, 10, 10s, 100h, 1s, 1s, j));
// Rest of network has closed
BEAST_EXPECT(shouldCloseLedger(true, 10, 3, 5, 10s, 10s, 10s, 10s, j));
// No transactions means wait until end of internval
BEAST_EXPECT(!shouldCloseLedger(false, 10, 0, 0, 1s, 1s, 1s, 10s, j));
BEAST_EXPECT(shouldCloseLedger(false, 10, 0, 0, 1s, 10s, 1s, 10s, j));
// Enforce minimum ledger open time
BEAST_EXPECT(!shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 1s, 10s, j));
// Don't go too much faster than last time
BEAST_EXPECT(!shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 3s, 10s, j));
BEAST_EXPECT(shouldCloseLedger(true, 10, 0, 0, 10s, 10s, 10s, 10s, j));
}
void testCheckConsensus()
{
// Not enough time has elapsed
BEAST_EXPECT( ConsensusState::No
== checkConsensus(10, 2, 2, 0, 3s, 2s, true, j));
// If not enough peers have propsed, ensure
// more time for proposals
BEAST_EXPECT( ConsensusState::No
== checkConsensus(10, 2, 2, 0, 3s, 4s, true, j));
// Enough time has elapsed and we all agree
BEAST_EXPECT( ConsensusState::Yes
== checkConsensus(10, 2, 2, 0, 3s, 10s, true, j));
// Enough time has elapsed and we don't yet agree
BEAST_EXPECT( ConsensusState::No
== checkConsensus(10, 2, 1, 0, 3s, 10s, true, j));
// Our peers have moved on
// Enough time has elapsed and we all agree
BEAST_EXPECT( ConsensusState::MovedOn
== checkConsensus(10, 2, 1, 8, 3s, 10s, true, j));
// No peers makes it easy to agree
BEAST_EXPECT( ConsensusState::Yes
== checkConsensus(0, 0, 0, 0, 3s, 10s, true, j));
}
void void
run() override run() override
{ {
testGetNextLedgerTimeResolution(); testGetNextLedgerTimeResolution();
testRoundCloseTime(); testRoundCloseTime();
testShouldCloseLedger();
testCheckConsensus();
} }
}; };

View File

@@ -20,6 +20,7 @@
#define RIPPLE_TEST_CSF_LEDGER_H_INCLUDED #define RIPPLE_TEST_CSF_LEDGER_H_INCLUDED
#include <ripple/basics/chrono.h> #include <ripple/basics/chrono.h>
#include <ripple/consensus/LedgerTiming.h>
#include <test/csf/Tx.h> #include <test/csf/Tx.h>
namespace ripple { namespace ripple {

View File

@@ -173,8 +173,8 @@ struct Peer : public Consensus<Peer, Traits>
bool proposing_ = true; bool proposing_ = true;
//! 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) Peer(PeerID i, BasicNetwork<Peer*>& n, UNL const& u, ConsensusParms p)
: Consensus<Peer, Traits>(n.clock(), beast::Journal{}) : Consensus<Peer, Traits>(n.clock(), p, beast::Journal{})
, id{i} , id{i}
, net{n} , net{n}
, unl(u) , unl(u)
@@ -395,12 +395,12 @@ struct Peer : public Consensus<Peer, Traits>
Base::timerEntry(now()); Base::timerEntry(now());
// only reschedule if not completed // only reschedule if not completed
if (completedLedgers < targetLedgers) if (completedLedgers < targetLedgers)
net.timer(LEDGER_GRANULARITY, [&]() { timerEntry(); }); net.timer(parms().ledgerGRANULARITY, [&]() { timerEntry(); });
} }
void void
start() start()
{ {
net.timer(LEDGER_GRANULARITY, [&]() { timerEntry(); }); net.timer(parms().ledgerGRANULARITY, [&]() { timerEntry(); });
// The ID is the one we have seen the most validations for // The ID is the one we have seen the most validations for
// In practice, we might not actually have that ledger itself yet, // In practice, we might not actually have that ledger itself yet,
// so there is no gaurantee that bestLCL == lastClosedLedger.id() // so there is no gaurantee that bestLCL == lastClosedLedger.id()
@@ -415,8 +415,9 @@ struct Peer : public Consensus<Peer, Traits>
// We don't care about the actual epochs, but do want the // We don't care about the actual epochs, but do want the
// generated NetClock time to be well past its epoch to ensure // generated NetClock time to be well past its epoch to ensure
// any subtractions of two NetClock::time_point in the consensu // any subtractions of two NetClock::time_point in the consensu
// code are positive. (e.g. PROPOSE_FRESHNESS) // code are positive. (e.g. proposeFRESHNESS)
using namespace std::chrono; using namespace std::chrono;
using namespace std::chrono_literals;
return NetClock::time_point(duration_cast<NetClock::duration>( return NetClock::time_point(duration_cast<NetClock::duration>(
net.now().time_since_epoch() + 86400s + clockSkew)); net.now().time_since_epoch() + 86400s + clockSkew));
} }
@@ -427,6 +428,8 @@ struct Peer : public Consensus<Peer, Traits>
void void
schedule(std::chrono::nanoseconds when, T&& what) schedule(std::chrono::nanoseconds when, T&& what)
{ {
using namespace std::chrono_literals;
if (when == 0ns) if (when == 0ns)
what(); what();
else else

View File

@@ -47,14 +47,15 @@ public:
@param g The trust graph between peers. @param g The trust graph between peers.
@param top The network topology between peers. @param top The network topology between peers.
@param parms Consensus parameters to use in the simulation
*/ */
template <class Topology> template <class Topology>
Sim(TrustGraph const& g, Topology const& top) Sim(ConsensusParms parms, TrustGraph const& g, Topology const& top)
{ {
peers.reserve(g.numPeers()); peers.reserve(g.numPeers());
for (int i = 0; i < g.numPeers(); ++i) for (int i = 0; i < g.numPeers(); ++i)
peers.emplace_back(i, net, g.unl(i)); peers.emplace_back(i, net, g.unl(i), parms);
for (int i = 0; i < peers.size(); ++i) for (int i = 0; i < peers.size(); ++i)
{ {