mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
Expose consensus parameters for simulation (RIPD-1355)
This commit is contained in:
@@ -1859,16 +1859,18 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
|
||||
</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>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\ConsensusParms.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
|
||||
</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>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\Validations.h">
|
||||
|
||||
@@ -2502,18 +2502,21 @@
|
||||
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
|
||||
<Filter>ripple\conditions\impl</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\consensus\Consensus.cpp">
|
||||
<Filter>ripple\consensus</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\Consensus.h">
|
||||
<Filter>ripple\consensus</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\ConsensusParms.h">
|
||||
<Filter>ripple\consensus</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\ConsensusProposal.h">
|
||||
<Filter>ripple\consensus</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\DisputedTx.h">
|
||||
<Filter>ripple\consensus</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\consensus\LedgerTiming.cpp">
|
||||
<Filter>ripple\consensus</Filter>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
|
||||
<Filter>ripple\consensus</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -114,6 +114,7 @@ INPUT = \
|
||||
../src/ripple/consensus/DisputedTx.h \
|
||||
../src/ripple/consensus/LedgerTiming.h \
|
||||
../src/ripple/consensus/Validations.h \
|
||||
../src/ripple/consensus/ConsensusParms.h \
|
||||
../src/ripple/app/consensus/RCLCxTx.h \
|
||||
../src/ripple/app/consensus/RCLCxLedger.h \
|
||||
../src/ripple/app/consensus/RCLConsensus.h \
|
||||
|
||||
@@ -50,7 +50,7 @@ RCLConsensus::RCLConsensus(
|
||||
InboundTransactions& inboundTransactions,
|
||||
typename Base::clock_type const& clock,
|
||||
beast::Journal journal)
|
||||
: Base(clock, journal)
|
||||
: Base(clock, ConsensusParms{}, journal)
|
||||
, app_(app)
|
||||
, feeVote_(std::move(feeVote))
|
||||
, ledgerMaster_(ledgerMaster)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <ripple/app/ledger/AcceptedLedger.h>
|
||||
#include <ripple/app/ledger/InboundLedgers.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/LocalTxs.h>
|
||||
#include <ripple/app/ledger/OpenLedger.h>
|
||||
@@ -639,7 +639,7 @@ void NetworkOPsImp::setStateTimer ()
|
||||
|
||||
void NetworkOPsImp::setHeartbeatTimer ()
|
||||
{
|
||||
m_heartbeatTimer.setExpiration (LEDGER_GRANULARITY);
|
||||
m_heartbeatTimer.setExpiration (mConsensus->parms().ledgerGRANULARITY);
|
||||
}
|
||||
|
||||
void NetworkOPsImp::setClusterTimer ()
|
||||
|
||||
@@ -19,9 +19,7 @@
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <ripple/consensus/Consensus.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -35,11 +33,13 @@ shouldCloseLedger(
|
||||
std::chrono::milliseconds
|
||||
timeSincePrevClose, // Time since last ledger's close time
|
||||
std::chrono::milliseconds openTime, // Time waiting to close this ledger
|
||||
std::chrono::seconds idleInterval,
|
||||
std::chrono::milliseconds idleInterval,
|
||||
ConsensusParms const& parms,
|
||||
beast::Journal j)
|
||||
{
|
||||
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
|
||||
JLOG(j.warn()) << "shouldCloseLedger Trans="
|
||||
@@ -64,7 +64,7 @@ shouldCloseLedger(
|
||||
}
|
||||
|
||||
// Preserve minimum ledger open time
|
||||
if (openTime < LEDGER_MIN_CLOSE)
|
||||
if (openTime < parms.ledgerMIN_CLOSE)
|
||||
{
|
||||
JLOG(j.debug()) << "Must wait minimum time before closing";
|
||||
return false;
|
||||
@@ -84,7 +84,11 @@ shouldCloseLedger(
|
||||
}
|
||||
|
||||
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 (total == 0)
|
||||
@@ -96,9 +100,9 @@ checkConsensusReached(std::size_t agreeing, std::size_t total, bool count_self)
|
||||
++total;
|
||||
}
|
||||
|
||||
int currentPercentage = (agreeing * 100) / total;
|
||||
std::size_t currentPercentage = (agreeing * 100) / total;
|
||||
|
||||
return currentPercentage > minimumConsensusPercentage;
|
||||
return currentPercentage > minConsensusPct;
|
||||
}
|
||||
|
||||
ConsensusState
|
||||
@@ -109,6 +113,7 @@ checkConsensus(
|
||||
std::size_t currentFinished,
|
||||
std::chrono::milliseconds previousAgreeTime,
|
||||
std::chrono::milliseconds currentAgreeTime,
|
||||
ConsensusParms const& parms,
|
||||
bool proposing,
|
||||
beast::Journal j)
|
||||
{
|
||||
@@ -118,14 +123,14 @@ checkConsensus(
|
||||
<< " time=" << currentAgreeTime.count() << "/"
|
||||
<< previousAgreeTime.count();
|
||||
|
||||
if (currentAgreeTime <= LEDGER_MIN_CONSENSUS)
|
||||
if (currentAgreeTime <= parms.ledgerMIN_CONSENSUS)
|
||||
return ConsensusState::No;
|
||||
|
||||
if (currentProposers < (prevProposers * 3 / 4))
|
||||
{
|
||||
// Less than 3/4 of the last ledger's proposers are present; don't
|
||||
// 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";
|
||||
return ConsensusState::No;
|
||||
@@ -134,7 +139,8 @@ checkConsensus(
|
||||
|
||||
// Have we, together with the nodes on our UNL list, reached the threshold
|
||||
// to declare consensus?
|
||||
if (checkConsensusReached(currentAgree, currentProposers, proposing))
|
||||
if (checkConsensusReached(
|
||||
currentAgree, currentProposers, proposing, parms.minCONSENSUS_PCT))
|
||||
{
|
||||
JLOG(j.debug()) << "normal consensus";
|
||||
return ConsensusState::Yes;
|
||||
@@ -142,7 +148,8 @@ checkConsensus(
|
||||
|
||||
// Have sufficient nodes on our UNL list moved on and reached the threshold
|
||||
// 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";
|
||||
return ConsensusState::MovedOn;
|
||||
@@ -153,4 +160,4 @@ checkConsensus(
|
||||
return ConsensusState::No;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
} // namespace ripple
|
||||
@@ -24,11 +24,79 @@
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/consensus/ConsensusProposal.h>
|
||||
#include <ripple/consensus/ConsensusParms.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/consensus/DisputedTx.h>
|
||||
#include <ripple/json/json_writer.h>
|
||||
|
||||
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.
|
||||
|
||||
Achieves consensus on the next ledger.
|
||||
@@ -351,9 +419,10 @@ public:
|
||||
/** Constructor.
|
||||
|
||||
@param clock The clock used to internally sample consensus progress
|
||||
@param p Consensus parameters to use
|
||||
@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.
|
||||
|
||||
@@ -474,6 +543,13 @@ public:
|
||||
Json::Value
|
||||
getJson(bool full) const;
|
||||
|
||||
/** Get the consensus parameters
|
||||
*/
|
||||
ConsensusParms const &
|
||||
parms() const
|
||||
{
|
||||
return parms_;
|
||||
}
|
||||
protected:
|
||||
|
||||
// Prevent deleting derived instance through base pointer
|
||||
@@ -584,7 +660,7 @@ private:
|
||||
NetClock::duration closeResolution_ = ledgerDefaultTimeResolution;
|
||||
|
||||
// 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
|
||||
@@ -618,6 +694,9 @@ private:
|
||||
// nodes that have bowed out of this consensus process
|
||||
hash_set<NodeID_t> deadNodes_;
|
||||
|
||||
// Parameters that control consensus algorithm
|
||||
ConsensusParms const parms_;
|
||||
|
||||
// Journal for debugging
|
||||
beast::Journal j_;
|
||||
};
|
||||
@@ -625,9 +704,12 @@ private:
|
||||
template <class Derived, class Traits>
|
||||
Consensus<Derived, Traits>::Consensus(
|
||||
clock_type const& clock,
|
||||
ConsensusParms const & p,
|
||||
beast::Journal journal)
|
||||
: lock_(std::make_unique<std::recursive_mutex>())
|
||||
, clock_(clock)
|
||||
: lock_{std::make_unique<std::recursive_mutex>()}
|
||||
, clock_{clock}
|
||||
, prevRoundTime_{p.ledgerIDLE_INTERVAL}
|
||||
, parms_(p)
|
||||
, j_{journal}
|
||||
{
|
||||
JLOG(j_.debug()) << "Creating consensus object";
|
||||
@@ -1112,9 +1194,9 @@ Consensus<Derived, Traits>::phaseOpen()
|
||||
sinceClose = -duration_cast<milliseconds>(lastCloseTime - now_);
|
||||
}
|
||||
|
||||
auto const idleInterval = std::max<seconds>(
|
||||
LEDGER_IDLE_INTERVAL,
|
||||
duration_cast<seconds>(2 * previousLedger_.closeTimeResolution()));
|
||||
auto const idleInterval = std::max<milliseconds>(
|
||||
parms_.ledgerIDLE_INTERVAL,
|
||||
2 * previousLedger_.closeTimeResolution());
|
||||
|
||||
// Decide if we should close the ledger
|
||||
if (shouldCloseLedger(
|
||||
@@ -1126,6 +1208,7 @@ Consensus<Derived, Traits>::phaseOpen()
|
||||
sinceClose,
|
||||
openTime_.read(),
|
||||
idleInterval,
|
||||
parms_,
|
||||
j_))
|
||||
{
|
||||
closeLedger();
|
||||
@@ -1143,10 +1226,10 @@ Consensus<Derived, Traits>::phaseEstablish()
|
||||
result_->roundTime.tick(clock_.now());
|
||||
|
||||
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
|
||||
if (result_->roundTime.read() < LEDGER_MIN_CONSENSUS)
|
||||
if (result_->roundTime.read() < parms_.ledgerMIN_CONSENSUS)
|
||||
return;
|
||||
|
||||
updateOurPositions();
|
||||
@@ -1230,8 +1313,8 @@ Consensus<Derived, Traits>::updateOurPositions()
|
||||
assert(result_);
|
||||
|
||||
// Compute a cutoff time
|
||||
auto const peerCutoff = now_ - PROPOSE_FRESHNESS;
|
||||
auto const ourCutoff = now_ - PROPOSE_INTERVAL;
|
||||
auto const peerCutoff = now_ - parms_.proposeFRESHNESS;
|
||||
auto const ourCutoff = now_ - parms_.proposeINTERVAL;
|
||||
|
||||
// Verify freshness of peer positions and compute close times
|
||||
std::map<NetClock::time_point, int> effCloseTimes;
|
||||
@@ -1271,7 +1354,7 @@ Consensus<Derived, Traits>::updateOurPositions()
|
||||
// Because the threshold for inclusion increases,
|
||||
// time can change our position on a dispute
|
||||
if (it.second.updateVote(
|
||||
convergePercent_, (mode_ == Mode::proposing)))
|
||||
convergePercent_, (mode_ == Mode::proposing), parms_))
|
||||
{
|
||||
if (!mutableSet)
|
||||
mutableSet.emplace(result_->set);
|
||||
@@ -1309,14 +1392,14 @@ Consensus<Derived, Traits>::updateOurPositions()
|
||||
{
|
||||
int neededWeight;
|
||||
|
||||
if (convergePercent_ < AV_MID_CONSENSUS_TIME)
|
||||
neededWeight = AV_INIT_CONSENSUS_PCT;
|
||||
else if (convergePercent_ < AV_LATE_CONSENSUS_TIME)
|
||||
neededWeight = AV_MID_CONSENSUS_PCT;
|
||||
else if (convergePercent_ < AV_STUCK_CONSENSUS_TIME)
|
||||
neededWeight = AV_LATE_CONSENSUS_PCT;
|
||||
if (convergePercent_ < parms_.avMID_CONSENSUS_TIME)
|
||||
neededWeight = parms_.avINIT_CONSENSUS_PCT;
|
||||
else if (convergePercent_ < parms_.avLATE_CONSENSUS_TIME)
|
||||
neededWeight = parms_.avMID_CONSENSUS_PCT;
|
||||
else if (convergePercent_ < parms_.avSTUCK_CONSENSUS_TIME)
|
||||
neededWeight = parms_.avLATE_CONSENSUS_PCT;
|
||||
else
|
||||
neededWeight = AV_STUCK_CONSENSUS_PCT;
|
||||
neededWeight = parms_.avSTUCK_CONSENSUS_PCT;
|
||||
|
||||
int participants = peerProposals_.size();
|
||||
if (mode_ == Mode::proposing)
|
||||
@@ -1333,7 +1416,7 @@ Consensus<Derived, Traits>::updateOurPositions()
|
||||
|
||||
// Threshold to declare consensus
|
||||
int const threshConsensus =
|
||||
participantsNeeded(participants, AV_CT_CONSENSUS_PCT);
|
||||
participantsNeeded(participants, parms_.avCT_CONSENSUS_PCT);
|
||||
|
||||
JLOG(j_.info()) << "Proposers:" << peerProposals_.size()
|
||||
<< " nw:" << neededWeight << " thrV:" << threshVote
|
||||
@@ -1454,6 +1537,7 @@ Consensus<Derived, Traits>::haveConsensus()
|
||||
currentFinished,
|
||||
prevRoundTime_,
|
||||
result_->roundTime.read(),
|
||||
parms_,
|
||||
mode_ == Mode::proposing,
|
||||
j_);
|
||||
|
||||
|
||||
135
src/ripple/consensus/ConsensusParms.h
Normal file
135
src/ripple/consensus/ConsensusParms.h
Normal 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
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/base_uint.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/UintTypes.h>
|
||||
#include <memory>
|
||||
@@ -112,10 +112,11 @@ public:
|
||||
@param percentTime Percentage progress through consensus, e.g. 50%
|
||||
through or 90%.
|
||||
@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
|
||||
*/
|
||||
bool
|
||||
updateVote(int percentTime, bool proposing);
|
||||
updateVote(int percentTime, bool proposing, ConsensusParms const& p);
|
||||
|
||||
//! JSON representation of dispute, used for debugging
|
||||
Json::Value
|
||||
@@ -190,7 +191,10 @@ DisputedTx<Tx_t, NodeID_t>::unVote(NodeID_t const& peer)
|
||||
|
||||
template <class Tx_t, class NodeID_t>
|
||||
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))
|
||||
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
|
||||
// over time.
|
||||
if (percentTime < AV_MID_CONSENSUS_TIME)
|
||||
newPosition = weight > AV_INIT_CONSENSUS_PCT;
|
||||
else if (percentTime < AV_LATE_CONSENSUS_TIME)
|
||||
newPosition = weight > AV_MID_CONSENSUS_PCT;
|
||||
else if (percentTime < AV_STUCK_CONSENSUS_TIME)
|
||||
newPosition = weight > AV_LATE_CONSENSUS_PCT;
|
||||
if (percentTime < p.avMID_CONSENSUS_TIME)
|
||||
newPosition = weight > p.avINIT_CONSENSUS_PCT;
|
||||
else if (percentTime < p.avLATE_CONSENSUS_TIME)
|
||||
newPosition = weight > p.avMID_CONSENSUS_PCT;
|
||||
else if (percentTime < p.avSTUCK_CONSENSUS_TIME)
|
||||
newPosition = weight > p.avLATE_CONSENSUS_PCT;
|
||||
else
|
||||
newPosition = weight > AV_STUCK_CONSENSUS_PCT;
|
||||
newPosition = weight > p.avSTUCK_CONSENSUS_PCT;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -27,15 +27,9 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// These are protocol parameters used to control the behavior of the system and
|
||||
// they should not be changed arbitrarily.
|
||||
|
||||
//! The percentage threshold above which we can declare consensus.
|
||||
auto constexpr minimumConsensusPercentage = 80;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
/** Possible close time resolutions.
|
||||
|
||||
/** Possible ledger close time resolutions.
|
||||
|
||||
Values should not be duplicated.
|
||||
@see getNextLedgerTimeResolution
|
||||
@@ -52,61 +46,6 @@ auto constexpr increaseLedgerTimeResolutionEvery = 8;
|
||||
//! How often we decrease the close time resolution (in numbers of ledgers)
|
||||
auto constexpr decreaseLedgerTimeResolutionEvery = 1;
|
||||
|
||||
//! The number of seconds a ledger may remain idle before closing
|
||||
auto constexpr LEDGER_IDLE_INTERVAL = 15s;
|
||||
|
||||
//! The number of seconds 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.
|
||||
|
||||
@@ -200,6 +139,8 @@ effCloseTime(
|
||||
typename time_point::duration const resolution,
|
||||
time_point priorCloseTime)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
if (closeTime == time_point{})
|
||||
return closeTime;
|
||||
|
||||
@@ -207,78 +148,5 @@ effCloseTime(
|
||||
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
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
//==============================================================================
|
||||
#include <BeastConfig.h>
|
||||
|
||||
#include <ripple/consensus/LedgerTiming.cpp>
|
||||
#include <ripple/consensus/Consensus.cpp>
|
||||
|
||||
@@ -31,13 +31,97 @@ namespace test {
|
||||
class Consensus_test : public beast::unit_test::suite
|
||||
{
|
||||
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
|
||||
testStandalone()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace csf;
|
||||
|
||||
ConsensusParms parms;
|
||||
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];
|
||||
|
||||
@@ -63,10 +147,14 @@ public:
|
||||
using namespace csf;
|
||||
using namespace std::chrono;
|
||||
|
||||
ConsensusParms parms;
|
||||
auto tg = TrustGraph::makeComplete(5);
|
||||
Sim sim(
|
||||
parms,
|
||||
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
|
||||
for (auto& p : sim.peers)
|
||||
@@ -98,12 +186,13 @@ public:
|
||||
|
||||
for (auto isParticipant : {true, false})
|
||||
{
|
||||
ConsensusParms parms;
|
||||
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;
|
||||
return round<milliseconds>(
|
||||
delayFactor * LEDGER_GRANULARITY);
|
||||
delayFactor * parms.ledgerGRANULARITY);
|
||||
}));
|
||||
|
||||
sim.peers[0].proposing_ = sim.peers[0].validating_ = isParticipant;
|
||||
@@ -183,7 +272,7 @@ public:
|
||||
// the skews need to be at least 10 seconds.
|
||||
|
||||
// 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
|
||||
// (0s,10s,20s).
|
||||
|
||||
@@ -191,22 +280,26 @@ public:
|
||||
// skew. Then no majority (1/3 < 1/2) of nodes will agree on an
|
||||
// actual close time.
|
||||
|
||||
ConsensusParms parms;
|
||||
auto tg = TrustGraph::makeComplete(6);
|
||||
Sim sim(
|
||||
parms,
|
||||
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
|
||||
// resolution
|
||||
while (sim.peers.front().lastClosedLedger.closeTimeResolution() >=
|
||||
PROPOSE_FRESHNESS)
|
||||
parms.proposeFRESHNESS)
|
||||
sim.run(1);
|
||||
|
||||
// Introduce a shift on the time of half the peers
|
||||
sim.peers[0].clockSkew = PROPOSE_FRESHNESS / 2;
|
||||
sim.peers[1].clockSkew = PROPOSE_FRESHNESS / 2;
|
||||
sim.peers[2].clockSkew = PROPOSE_FRESHNESS;
|
||||
sim.peers[3].clockSkew = PROPOSE_FRESHNESS;
|
||||
sim.peers[0].clockSkew = parms.proposeFRESHNESS / 2;
|
||||
sim.peers[1].clockSkew = parms.proposeFRESHNESS / 2;
|
||||
sim.peers[2].clockSkew = parms.proposeFRESHNESS;
|
||||
sim.peers[3].clockSkew = parms.proposeFRESHNESS;
|
||||
|
||||
// Verify all peers have the same LCL and it has all the Txs
|
||||
sim.run(1);
|
||||
@@ -224,9 +317,12 @@ public:
|
||||
// Specialized test to exercise a temporary fork in which some peers
|
||||
// are working on an incorrect prior ledger.
|
||||
|
||||
ConsensusParms parms;
|
||||
|
||||
|
||||
// Vary the time it takes to process validations to exercise detecting
|
||||
// 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:
|
||||
// 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
|
||||
// test.
|
||||
BEAST_EXPECT(tg.canFork(minimumConsensusPercentage / 100.));
|
||||
BEAST_EXPECT(tg.canFork(parms.minCONSENSUS_PCT / 100.));
|
||||
|
||||
auto netDelay = round<milliseconds>(0.2 * LEDGER_GRANULARITY);
|
||||
Sim sim(tg, topology(tg, fixed{netDelay}));
|
||||
auto netDelay = round<milliseconds>(0.2 * parms.ledgerGRANULARITY);
|
||||
Sim sim(parms, tg, topology(tg, fixed{netDelay}));
|
||||
|
||||
// initial round to set prior state
|
||||
sim.run(1);
|
||||
@@ -283,7 +379,7 @@ public:
|
||||
// wrong ones) and recover within that round since wrong LCL
|
||||
// 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.
|
||||
// 1. Round to generate different ledgers
|
||||
// 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
|
||||
// the establish phase logic.
|
||||
{
|
||||
using namespace csf;
|
||||
using namespace std::chrono;
|
||||
|
||||
// A mostly disjoint topology
|
||||
std::vector<UNL> unls;
|
||||
unls.push_back({0, 1});
|
||||
@@ -337,14 +430,14 @@ public:
|
||||
|
||||
TrustGraph tg{unls, membership};
|
||||
|
||||
Sim sim(tg, topology(tg, fixed{round<milliseconds>(
|
||||
0.2 * LEDGER_GRANULARITY)}));
|
||||
Sim sim(parms, tg, topology(tg, fixed{round<milliseconds>(
|
||||
0.2 * parms.ledgerGRANULARITY)}));
|
||||
|
||||
// initial ground to set prior state
|
||||
sim.run(1);
|
||||
for (auto &p : sim.peers) {
|
||||
// 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
|
||||
p.openTxs.insert(Tx(p.id));
|
||||
@@ -367,11 +460,15 @@ public:
|
||||
int numPeers = 10;
|
||||
for (int overlap = 0; overlap <= numPeers; ++overlap)
|
||||
{
|
||||
ConsensusParms parms;
|
||||
auto tg = TrustGraph::makeClique(numPeers, overlap);
|
||||
Sim sim(
|
||||
parms,
|
||||
tg,
|
||||
topology(
|
||||
tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
|
||||
tg,
|
||||
fixed{
|
||||
round<milliseconds>(0.2 * parms.ledgerGRANULARITY)}));
|
||||
|
||||
// Initial round to set prior state
|
||||
sim.run(1);
|
||||
@@ -406,7 +503,7 @@ public:
|
||||
simClockSkew()
|
||||
{
|
||||
using namespace csf;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
// Attempting to test what happens if peers enter consensus well
|
||||
// separated in time. Initial round (in which peers are not staggered)
|
||||
// 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})
|
||||
{
|
||||
ConsensusParms parms;
|
||||
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);
|
||||
}));
|
||||
|
||||
@@ -474,6 +572,7 @@ public:
|
||||
double transProb = 0.5;
|
||||
|
||||
std::mt19937_64 rng;
|
||||
ConsensusParms parms;
|
||||
|
||||
auto tg = TrustGraph::makeRandomRanked(
|
||||
N,
|
||||
@@ -483,8 +582,11 @@ public:
|
||||
rng);
|
||||
|
||||
Sim sim{
|
||||
parms,
|
||||
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
|
||||
sim.run(1);
|
||||
@@ -511,6 +613,9 @@ public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testShouldCloseLedger();
|
||||
testCheckConsensus();
|
||||
|
||||
testStandalone();
|
||||
testPeersAgree();
|
||||
testSlowPeer();
|
||||
|
||||
@@ -77,6 +77,7 @@ class LedgerTiming_test : public beast::unit_test::suite
|
||||
|
||||
void testRoundCloseTime()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
// A closeTime equal to the epoch is not modified
|
||||
using tp = NetClock::time_point;
|
||||
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
|
||||
run() override
|
||||
{
|
||||
testGetNextLedgerTimeResolution();
|
||||
testRoundCloseTime();
|
||||
testShouldCloseLedger();
|
||||
testCheckConsensus();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#define RIPPLE_TEST_CSF_LEDGER_H_INCLUDED
|
||||
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <test/csf/Tx.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -173,8 +173,8 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
bool proposing_ = true;
|
||||
|
||||
//! All peers start from the default constructed ledger
|
||||
Peer(PeerID i, BasicNetwork<Peer*>& n, UNL const& u)
|
||||
: Consensus<Peer, Traits>(n.clock(), beast::Journal{})
|
||||
Peer(PeerID i, BasicNetwork<Peer*>& n, UNL const& u, ConsensusParms p)
|
||||
: Consensus<Peer, Traits>(n.clock(), p, beast::Journal{})
|
||||
, id{i}
|
||||
, net{n}
|
||||
, unl(u)
|
||||
@@ -395,12 +395,12 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
Base::timerEntry(now());
|
||||
// only reschedule if not completed
|
||||
if (completedLedgers < targetLedgers)
|
||||
net.timer(LEDGER_GRANULARITY, [&]() { timerEntry(); });
|
||||
net.timer(parms().ledgerGRANULARITY, [&]() { timerEntry(); });
|
||||
}
|
||||
void
|
||||
start()
|
||||
{
|
||||
net.timer(LEDGER_GRANULARITY, [&]() { timerEntry(); });
|
||||
net.timer(parms().ledgerGRANULARITY, [&]() { timerEntry(); });
|
||||
// The ID is the one we have seen the most validations for
|
||||
// In practice, we might not actually have that ledger itself yet,
|
||||
// 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
|
||||
// generated NetClock time to be well past its epoch to ensure
|
||||
// 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_literals;
|
||||
return NetClock::time_point(duration_cast<NetClock::duration>(
|
||||
net.now().time_since_epoch() + 86400s + clockSkew));
|
||||
}
|
||||
@@ -427,6 +428,8 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
void
|
||||
schedule(std::chrono::nanoseconds when, T&& what)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
if (when == 0ns)
|
||||
what();
|
||||
else
|
||||
|
||||
@@ -47,14 +47,15 @@ public:
|
||||
|
||||
@param g The trust graph between peers.
|
||||
@param top The network topology between peers.
|
||||
@param parms Consensus parameters to use in the simulation
|
||||
|
||||
*/
|
||||
template <class Topology>
|
||||
Sim(TrustGraph const& g, Topology const& top)
|
||||
Sim(ConsensusParms parms, TrustGraph const& g, Topology const& top)
|
||||
{
|
||||
peers.reserve(g.numPeers());
|
||||
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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user