diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 44bbbfd43a..4a416a3540 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1859,16 +1859,18 @@
+
+ True
+ True
+
+
+
-
- True
- True
-
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index efaff19785..cf74448ecd 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -2502,18 +2502,21 @@
ripple\conditions\impl
+
+ ripple\consensus
+
ripple\consensus
+
+ ripple\consensus
+
ripple\consensus
ripple\consensus
-
- ripple\consensus
-
ripple\consensus
diff --git a/docs/source.dox b/docs/source.dox
index 1fb7de3f06..5754d9b1ab 100644
--- a/docs/source.dox
+++ b/docs/source.dox
@@ -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 \
diff --git a/src/ripple/app/consensus/RCLConsensus.cpp b/src/ripple/app/consensus/RCLConsensus.cpp
index 2d95cdb99c..b26451201d 100644
--- a/src/ripple/app/consensus/RCLConsensus.cpp
+++ b/src/ripple/app/consensus/RCLConsensus.cpp
@@ -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)
diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp
index 4c9690ebf7..53f8201f34 100644
--- a/src/ripple/app/misc/NetworkOPs.cpp
+++ b/src/ripple/app/misc/NetworkOPs.cpp
@@ -25,7 +25,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -639,7 +639,7 @@ void NetworkOPsImp::setStateTimer ()
void NetworkOPsImp::setHeartbeatTimer ()
{
- m_heartbeatTimer.setExpiration (LEDGER_GRANULARITY);
+ m_heartbeatTimer.setExpiration (mConsensus->parms().ledgerGRANULARITY);
}
void NetworkOPsImp::setClusterTimer ()
diff --git a/src/ripple/consensus/LedgerTiming.cpp b/src/ripple/consensus/Consensus.cpp
similarity index 81%
rename from src/ripple/consensus/LedgerTiming.cpp
rename to src/ripple/consensus/Consensus.cpp
index fe069a2a53..ac40e039e8 100644
--- a/src/ripple/consensus/LedgerTiming.cpp
+++ b/src/ripple/consensus/Consensus.cpp
@@ -19,9 +19,7 @@
#include
#include
-#include
-#include
-#include
+#include
namespace ripple {
@@ -33,13 +31,15 @@ shouldCloseLedger(
std::size_t proposersValidated,
std::chrono::milliseconds prevRoundTime,
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::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
diff --git a/src/ripple/consensus/Consensus.h b/src/ripple/consensus/Consensus.h
index 31c01bb5d4..f41431cfe4 100644
--- a/src/ripple/consensus/Consensus.h
+++ b/src/ripple/consensus/Consensus.h
@@ -24,11 +24,79 @@
#include
#include
#include
+#include
+#include
#include
#include
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 deadNodes_;
+ // Parameters that control consensus algorithm
+ ConsensusParms const parms_;
+
// Journal for debugging
beast::Journal j_;
};
@@ -625,9 +704,12 @@ private:
template
Consensus::Consensus(
clock_type const& clock,
+ ConsensusParms const & p,
beast::Journal journal)
- : lock_(std::make_unique())
- , clock_(clock)
+ : lock_{std::make_unique()}
+ , clock_{clock}
+ , prevRoundTime_{p.ledgerIDLE_INTERVAL}
+ , parms_(p)
, j_{journal}
{
JLOG(j_.debug()) << "Creating consensus object";
@@ -1112,9 +1194,9 @@ Consensus::phaseOpen()
sinceClose = -duration_cast(lastCloseTime - now_);
}
- auto const idleInterval = std::max(
- LEDGER_IDLE_INTERVAL,
- duration_cast(2 * previousLedger_.closeTimeResolution()));
+ auto const idleInterval = std::max(
+ parms_.ledgerIDLE_INTERVAL,
+ 2 * previousLedger_.closeTimeResolution());
// Decide if we should close the ledger
if (shouldCloseLedger(
@@ -1126,6 +1208,7 @@ Consensus::phaseOpen()
sinceClose,
openTime_.read(),
idleInterval,
+ parms_,
j_))
{
closeLedger();
@@ -1143,10 +1226,10 @@ Consensus::phaseEstablish()
result_->roundTime.tick(clock_.now());
convergePercent_ = result_->roundTime.read() * 100 /
- std::max(prevRoundTime_, AV_MIN_CONSENSUS_TIME);
+ std::max(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::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 effCloseTimes;
@@ -1271,7 +1354,7 @@ Consensus::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::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::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::haveConsensus()
currentFinished,
prevRoundTime_,
result_->roundTime.read(),
+ parms_,
mode_ == Mode::proposing,
j_);
diff --git a/src/ripple/consensus/ConsensusParms.h b/src/ripple/consensus/ConsensusParms.h
new file mode 100644
index 0000000000..02ed12a61f
--- /dev/null
+++ b/src/ripple/consensus/ConsensusParms.h
@@ -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
+#include
+
+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
diff --git a/src/ripple/consensus/DisputedTx.h b/src/ripple/consensus/DisputedTx.h
index 41e3d8ebe0..c0ef35f956 100644
--- a/src/ripple/consensus/DisputedTx.h
+++ b/src/ripple/consensus/DisputedTx.h
@@ -23,7 +23,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -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::unVote(NodeID_t const& peer)
template
bool
-DisputedTx::updateVote(int percentTime, bool proposing)
+DisputedTx::updateVote(
+ int percentTime,
+ bool proposing,
+ ConsensusParms const& p)
{
if (ourVote_ && (nays_ == 0))
return false;
@@ -212,14 +216,14 @@ DisputedTx::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
{
diff --git a/src/ripple/consensus/LedgerTiming.h b/src/ripple/consensus/LedgerTiming.h
index 19f2914d03..08552c347d 100644
--- a/src/ripple/consensus/LedgerTiming.h
+++ b/src/ripple/consensus/LedgerTiming.h
@@ -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
diff --git a/src/ripple/unity/consensus.cpp b/src/ripple/unity/consensus.cpp
index 74a8b8aafe..8554a327ce 100644
--- a/src/ripple/unity/consensus.cpp
+++ b/src/ripple/unity/consensus.cpp
@@ -18,4 +18,4 @@
//==============================================================================
#include
-#include
+#include
diff --git a/src/test/consensus/Consensus_test.cpp b/src/test/consensus/Consensus_test.cpp
index 0bffc4bdaa..ce3d58466b 100644
--- a/src/test/consensus/Consensus_test.cpp
+++ b/src/test/consensus/Consensus_test.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(0.2 * LEDGER_GRANULARITY)}));
+ topology(
+ tg,
+ fixed{round(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(
- 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(0.2 * LEDGER_GRANULARITY)}));
+ topology(
+ tg,
+ fixed{round(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(0.2 * LEDGER_GRANULARITY);
- Sim sim(tg, topology(tg, fixed{netDelay}));
+ auto netDelay = round(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 unls;
unls.push_back({0, 1});
@@ -337,14 +430,14 @@ public:
TrustGraph tg{unls, membership};
- Sim sim(tg, topology(tg, fixed{round(
- 0.2 * LEDGER_GRANULARITY)}));
+ Sim sim(parms, tg, topology(tg, fixed{round(
+ 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(0.2 * LEDGER_GRANULARITY)}));
+ tg,
+ fixed{
+ round(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(0.2 * LEDGER_GRANULARITY)})};
+ topology(
+ tg,
+ fixed{round(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();
diff --git a/src/test/consensus/LedgerTiming_test.cpp b/src/test/consensus/LedgerTiming_test.cpp
index 1c9894ab86..b3ce73733d 100644
--- a/src/test/consensus/LedgerTiming_test.cpp
+++ b/src/test/consensus/LedgerTiming_test.cpp
@@ -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();
}
};
diff --git a/src/test/csf/Ledger.h b/src/test/csf/Ledger.h
index 1787ce33c4..addc064273 100644
--- a/src/test/csf/Ledger.h
+++ b/src/test/csf/Ledger.h
@@ -20,6 +20,7 @@
#define RIPPLE_TEST_CSF_LEDGER_H_INCLUDED
#include
+#include
#include
namespace ripple {
diff --git a/src/test/csf/Peer.h b/src/test/csf/Peer.h
index f713f487b1..86220d633e 100644
--- a/src/test/csf/Peer.h
+++ b/src/test/csf/Peer.h
@@ -173,8 +173,8 @@ struct Peer : public Consensus
bool proposing_ = true;
//! All peers start from the default constructed ledger
- Peer(PeerID i, BasicNetwork& n, UNL const& u)
- : Consensus(n.clock(), beast::Journal{})
+ Peer(PeerID i, BasicNetwork& n, UNL const& u, ConsensusParms p)
+ : Consensus(n.clock(), p, beast::Journal{})
, id{i}
, net{n}
, unl(u)
@@ -395,12 +395,12 @@ struct Peer : public Consensus
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
// 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(
net.now().time_since_epoch() + 86400s + clockSkew));
}
@@ -427,6 +428,8 @@ struct Peer : public Consensus
void
schedule(std::chrono::nanoseconds when, T&& what)
{
+ using namespace std::chrono_literals;
+
if (when == 0ns)
what();
else
diff --git a/src/test/csf/Sim.h b/src/test/csf/Sim.h
index 4515deddf7..c88ef5e7c0 100644
--- a/src/test/csf/Sim.h
+++ b/src/test/csf/Sim.h
@@ -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
- 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)
{