mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Improve Consensus interface and documentation (RIPD-1340):
- Add Consensus::Result, which represents the result of the establish state and includes the consensus transaction set, final proposed position and disputes. - Add Consensus::Mode to track how we are participating in consensus and ensures the onAccept callback can distinguish when we entered the round with consensus versus when we recovered from a wrong ledger during a round. - Rename Consensus::Phase to Consensus::State and eliminate the processing phase. Instead, accept is a terminal phase which notifies RCLConsensus via onAccept callbacks. Even if clients dispatch accepting to another thread, all future calls except to startRound will not change the state of consensus. - Move validate_ status from Consensus to RCLConsensus, since generic implementation does not directly reference whether a node is validating or not. - Eliminate gotTxSetInternal and handle externally received TxSets distinct from locally generated positions. - Change ConsensusProposal::changePosition to always update the internal close time and position even if we have bowed out. This enforces the invariant that our proposal's position always matches our transaction set.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
@@ -21,19 +21,19 @@
|
||||
#define RIPPLE_APP_CONSENSUS_RCLCONSENSUS_H_INCLUDED
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/consensus/RCLCxLedger.h>
|
||||
#include <ripple/app/consensus/RCLCxPeerPos.h>
|
||||
#include <ripple/app/consensus/RCLCxTx.h>
|
||||
#include <ripple/app/misc/FeeVote.h>
|
||||
#include <ripple/basics/CountedObject.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/consensus/Consensus.h>
|
||||
#include <ripple/core/JobQueue.h>
|
||||
#include <ripple/overlay/Message.h>
|
||||
#include <ripple/protocol/RippleLedgerHash.h>
|
||||
#include <ripple/protocol/STValidation.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/app/misc/FeeVote.h>
|
||||
#include <ripple/protocol/RippleLedgerHash.h>
|
||||
#include <ripple/app/consensus/RCLCxLedger.h>
|
||||
#include <ripple/app/consensus/RCLCxTx.h>
|
||||
#include <ripple/app/consensus/RCLCxPeerPos.h>
|
||||
#include <ripple/core/JobQueue.h>
|
||||
#include <ripple/consensus/Consensus.h>
|
||||
#include <ripple/basics/CountedObject.h>
|
||||
#include <ripple/overlay/Message.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -50,37 +50,40 @@ struct RCLCxTraits
|
||||
using NodeID_t = NodeID;
|
||||
//! TxSet type presented to Consensus
|
||||
using TxSet_t = RCLTxSet;
|
||||
//! MissingTxException type neede by Consensus
|
||||
using MissingTxException_t = SHAMapMissingNode;
|
||||
};
|
||||
|
||||
|
||||
/** Adapts the generic Consensus algorithm for use by RCL.
|
||||
|
||||
@note The enabled_shared_from_this base allows the application to properly
|
||||
create a shared instance of RCLConsensus for use in the accept logic..
|
||||
*/
|
||||
class RCLConsensus : public Consensus<RCLConsensus, RCLCxTraits>
|
||||
, public std::enable_shared_from_this <RCLConsensus>
|
||||
, public CountedObject <RCLConsensus>
|
||||
class RCLConsensus final : public Consensus<RCLConsensus, RCLCxTraits>,
|
||||
public std::enable_shared_from_this<RCLConsensus>,
|
||||
public CountedObject<RCLConsensus>
|
||||
{
|
||||
using Base = Consensus<RCLConsensus, RCLCxTraits>;
|
||||
using Base::accept;
|
||||
public:
|
||||
|
||||
public:
|
||||
//! Constructor
|
||||
RCLConsensus(
|
||||
Application& app,
|
||||
std::unique_ptr<FeeVote> && feeVote,
|
||||
std::unique_ptr<FeeVote>&& feeVote,
|
||||
LedgerMaster& ledgerMaster,
|
||||
LocalTxs& localTxs,
|
||||
InboundTransactions& inboundTransactions,
|
||||
typename Base::clock_type const & clock,
|
||||
typename Base::clock_type const& clock,
|
||||
beast::Journal journal);
|
||||
RCLConsensus(RCLConsensus const&) = delete;
|
||||
RCLConsensus& operator=(RCLConsensus const&) = delete;
|
||||
|
||||
static char const* getCountedObjectName() { return "Consensus"; }
|
||||
RCLConsensus(RCLConsensus const&) = delete;
|
||||
|
||||
RCLConsensus&
|
||||
operator=(RCLConsensus const&) = delete;
|
||||
|
||||
static char const*
|
||||
getCountedObjectName()
|
||||
{
|
||||
return "Consensus";
|
||||
}
|
||||
|
||||
/** Save the given consensus proposed by a peer with nodeID for later
|
||||
use in consensus.
|
||||
@@ -89,15 +92,59 @@ public:
|
||||
@param nodeID ID of peer
|
||||
*/
|
||||
void
|
||||
storeProposal( RCLCxPeerPos::ref peerPos, NodeID const& nodeID);
|
||||
storeProposal(RCLCxPeerPos::ref peerPos, NodeID const& nodeID);
|
||||
|
||||
//! Whether we are validating consensus ledgers.
|
||||
bool
|
||||
validating() const
|
||||
{
|
||||
return validating_;
|
||||
}
|
||||
|
||||
bool
|
||||
haveCorrectLCL() const
|
||||
{
|
||||
return mode() != Mode::wrongLedger;
|
||||
}
|
||||
|
||||
bool
|
||||
proposing() const
|
||||
{
|
||||
return mode() == Mode::proposing;
|
||||
}
|
||||
|
||||
/** Get the Json state of the consensus process.
|
||||
|
||||
Called by the consensus_info RPC.
|
||||
|
||||
@param full True if verbose response desired.
|
||||
@return The Json state.
|
||||
*/
|
||||
Json::Value
|
||||
getJson(bool full) const;
|
||||
|
||||
//! See Consensus::startRound
|
||||
void
|
||||
startRound(
|
||||
NetClock::time_point const& now,
|
||||
RCLCxLedger::ID const& prevLgrId,
|
||||
RCLCxLedger const& prevLgr);
|
||||
|
||||
//! See Consensus::timerEntry
|
||||
void
|
||||
timerEntry(NetClock::time_point const& now);
|
||||
|
||||
//! See Consensus::gotTxSet
|
||||
void
|
||||
gotTxSet(NetClock::time_point const& now, RCLTxSet const& txSet);
|
||||
|
||||
/** Returns validation public key */
|
||||
PublicKey const&
|
||||
getValidationPublicKey () const;
|
||||
getValidationPublicKey() const;
|
||||
|
||||
/** Set validation private and public key pair. */
|
||||
void
|
||||
setValidationKeys (SecretKey const& valSecret, PublicKey const& valPublic);
|
||||
setValidationKeys(SecretKey const& valSecret, PublicKey const& valPublic);
|
||||
|
||||
private:
|
||||
friend class Consensus<RCLConsensus, RCLCxTraits>;
|
||||
@@ -105,17 +152,6 @@ private:
|
||||
//-------------------------------------------------------------------------
|
||||
// Consensus type requirements.
|
||||
|
||||
/** Notification that a new consensus round has begun.
|
||||
|
||||
@param ledger The ledger we are building consensus on
|
||||
*/
|
||||
void
|
||||
onStartRound(RCLCxLedger const & ledger);
|
||||
|
||||
//! @return Whether consensus should be (proposing, validating)
|
||||
std::pair <bool, bool>
|
||||
getMode ();
|
||||
|
||||
/** Attempt to acquire a specific ledger.
|
||||
|
||||
If not available, asynchronously acquires from the network.
|
||||
@@ -124,44 +160,44 @@ private:
|
||||
@return Optional ledger, will be seated if we locally had the ledger
|
||||
*/
|
||||
boost::optional<RCLCxLedger>
|
||||
acquireLedger(LedgerHash const & ledger);
|
||||
acquireLedger(LedgerHash const& ledger);
|
||||
|
||||
/** Get peers' proposed positions.
|
||||
@param prevLedger The base ledger which proposals are based on
|
||||
@return The set of proposals
|
||||
*/
|
||||
std::vector<RCLCxPeerPos>
|
||||
proposals (LedgerHash const& prevLedger);
|
||||
proposals(LedgerHash const& prevLedger);
|
||||
|
||||
/** Relay the given proposal to all peers
|
||||
|
||||
@param peerPos The peer position to relay.
|
||||
*/
|
||||
void
|
||||
relay(RCLCxPeerPos const & peerPos);
|
||||
relay(RCLCxPeerPos const& peerPos);
|
||||
|
||||
/** Relay disputed transacction to peers.
|
||||
|
||||
Only relay if the provided transaction hasn't been shared recently.
|
||||
|
||||
@param dispute The disputed transaction to relay.
|
||||
@param tx The disputed transaction to relay.
|
||||
*/
|
||||
void
|
||||
relay(DisputedTx <RCLCxTx, NodeID> const & dispute);
|
||||
relay(RCLCxTx const& tx);
|
||||
|
||||
/** Acquire the transaction set associated with a proposal.
|
||||
/** Acquire the transaction set associated with a proposal.
|
||||
|
||||
If the transaction set is not available locally, will attempt acquire it
|
||||
from the network.
|
||||
If the transaction set is not available locally, will attempt acquire it
|
||||
from the network.
|
||||
|
||||
@param setId The transaction set ID associated with the proposal
|
||||
@return Optional set of transactions, seated if available.
|
||||
*/
|
||||
@param setId The transaction set ID associated with the proposal
|
||||
@return Optional set of transactions, seated if available.
|
||||
*/
|
||||
boost::optional<RCLTxSet>
|
||||
acquireTxSet(RCLTxSet::ID const & setId);
|
||||
acquireTxSet(RCLTxSet::ID const& setId);
|
||||
|
||||
/** Whether the open ledger has any transactions
|
||||
*/
|
||||
*/
|
||||
bool
|
||||
hasOpenTransactions() const;
|
||||
|
||||
@@ -171,133 +207,93 @@ private:
|
||||
@return the number of proposers that validated a ledger
|
||||
*/
|
||||
std::size_t
|
||||
proposersValidated(LedgerHash const & h) const;
|
||||
proposersValidated(LedgerHash const& h) const;
|
||||
|
||||
/** Number of proposers that have validated a ledger descended from requested ledger.
|
||||
/** Number of proposers that have validated a ledger descended from
|
||||
requested ledger.
|
||||
|
||||
@param h The hash of the ledger of interest.
|
||||
@return The number of validating peers that have validated a ledger
|
||||
succeeding the one provided.
|
||||
*/
|
||||
std::size_t
|
||||
proposersFinished(LedgerHash const & h) const;
|
||||
proposersFinished(LedgerHash const& h) const;
|
||||
|
||||
/** Propose the given position to my peers.
|
||||
|
||||
@param proposal Our proposed position
|
||||
*/
|
||||
void
|
||||
propose (RCLCxPeerPos::Proposal const& proposal);
|
||||
propose(RCLCxPeerPos::Proposal const& proposal);
|
||||
|
||||
/** Share the given tx set with peers.
|
||||
/** Relay the given tx set to peers.
|
||||
|
||||
@param set The TxSet to share.
|
||||
*/
|
||||
void
|
||||
share (RCLTxSet const& set);
|
||||
relay(RCLTxSet const& set);
|
||||
|
||||
/** Get the last closed ledger (LCL) seen on the network
|
||||
/** Get the ID of the previous ledger/last closed ledger(LCL) on the network
|
||||
|
||||
@param currentLedger Current ledger used in consensus
|
||||
@param priorLedger Prior ledger used in consensus
|
||||
@param believedCorrect Whether consensus believes currentLedger is LCL
|
||||
@param ledgerID ID of previous ledger used by consensus
|
||||
@param ledger Previous ledger consensus has available
|
||||
@param mode Current consensus mode
|
||||
@return The id of the last closed network
|
||||
|
||||
@return The hash of the last closed network
|
||||
@note ledgerID may not match ledger.id() if we haven't acquired
|
||||
the ledger matching ledgerID from the network
|
||||
*/
|
||||
uint256
|
||||
getLCL (
|
||||
uint256 const& currentLedger,
|
||||
uint256 const& priorLedger,
|
||||
bool believedCorrect);
|
||||
getPrevLedger(
|
||||
uint256 ledgerID,
|
||||
RCLCxLedger const& ledger,
|
||||
Mode mode);
|
||||
|
||||
|
||||
/** Notification that the ledger has closed.
|
||||
/** Close the open ledger and return initial consensus position.
|
||||
|
||||
@param ledger the ledger we are changing to
|
||||
@param haveCorrectLCL whether we believe this is the correct LCL
|
||||
@param closeTime When consensus closed the ledger
|
||||
@param mode Current consensus mode
|
||||
@return Tentative consensus result
|
||||
*/
|
||||
void
|
||||
onClose(RCLCxLedger const & ledger, bool haveCorrectLCL);
|
||||
Result
|
||||
onClose(
|
||||
RCLCxLedger const& ledger,
|
||||
NetClock::time_point const& closeTime,
|
||||
Mode mode);
|
||||
|
||||
/** Create our initial position of transactions to accept in this round
|
||||
of consensus.
|
||||
|
||||
@param prevLedger The ledger the transactions apply to
|
||||
@param isProposing Whether we are currently proposing
|
||||
@param isCorrectLCL Whether we have the correct LCL
|
||||
@param closeTime When we believe the ledger closed
|
||||
@param now The current network adjusted time
|
||||
|
||||
@return Pair of (i) transactions we believe are in the ledger
|
||||
(ii) the corresponding proposal of those transactions
|
||||
to send to peers
|
||||
*/
|
||||
std::pair <RCLTxSet, typename RCLCxPeerPos::Proposal>
|
||||
makeInitialPosition (
|
||||
RCLCxLedger const & prevLedger,
|
||||
bool isProposing,
|
||||
bool isCorrectLCL,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now);
|
||||
|
||||
|
||||
/** Dispatch a call to Consensus::accept
|
||||
/** Process the accepted ledger.
|
||||
|
||||
Accepting a ledger may be expensive, so this function can dispatch
|
||||
that call to another thread if desired and must call the accept
|
||||
method of the generic consensus algorithm.
|
||||
that call to another thread if desired.
|
||||
|
||||
@param txSet The transactions to accept.
|
||||
@param result The result of consensus
|
||||
@param prevLedger The closed ledger consensus worked from
|
||||
@param closeResolution The resolution used in agreeing on an effective
|
||||
closeTiem
|
||||
@param rawCloseTimes The unrounded closetimes of ourself and our peers
|
||||
@param mode Our participating mode at the time consensus was declared
|
||||
*/
|
||||
void
|
||||
dispatchAccept(RCLTxSet const & txSet);
|
||||
onAccept(
|
||||
Result const& result,
|
||||
RCLCxLedger const& prevLedger,
|
||||
NetClock::duration const & closeResolution,
|
||||
CloseTimes const& rawCloseTimes,
|
||||
Mode const& mode);
|
||||
|
||||
/** Process the accepted ledger that was a result of simulation/force
|
||||
accept.
|
||||
|
||||
/** Accept a new ledger based on the given transactions.
|
||||
|
||||
TODO: Too many arguments, need to group related types.
|
||||
|
||||
@param set The set of accepted transactions
|
||||
@param consensusCloseTime Consensus agreed upon close time
|
||||
@param proposing_ Whether we are proposing
|
||||
@param validating_ Whether we are validating
|
||||
@param haveCorrectLCL_ Whether we had the correct last closed ledger
|
||||
@param consensusFail_ Whether consensus failed
|
||||
@param prevLedgerHash_ The hash/id of the previous ledger
|
||||
@param previousLedger_ The previous ledger
|
||||
@param closeResolution_ The close time resolution used this round
|
||||
@param now Current network adjsuted time
|
||||
@param roundTime_ Duration of this consensus round
|
||||
@param disputes_ Disputed trarnsactions from this round
|
||||
@param closeTimes_ Histogram of peers close times
|
||||
@param closeTime Our close time
|
||||
@return Whether we should continue validating
|
||||
*/
|
||||
bool
|
||||
accept(
|
||||
RCLTxSet const& set,
|
||||
NetClock::time_point consensusCloseTime,
|
||||
bool proposing_,
|
||||
bool validating_,
|
||||
bool haveCorrectLCL_,
|
||||
bool consensusFail_,
|
||||
LedgerHash const &prevLedgerHash_,
|
||||
RCLCxLedger const & previousLedger_,
|
||||
NetClock::duration closeResolution_,
|
||||
NetClock::time_point const & now,
|
||||
std::chrono::milliseconds const & roundTime_,
|
||||
hash_map<RCLCxTx::ID, DisputedTx <RCLCxTx, NodeID>> const & disputes_,
|
||||
std::map <NetClock::time_point, int> closeTimes_,
|
||||
NetClock::time_point const & closeTime
|
||||
);
|
||||
|
||||
/** Signal the end of consensus to the application, which will start the
|
||||
next round.
|
||||
|
||||
@param correctLCL Whether we believe we have the correct LCL
|
||||
@ref onAccept
|
||||
*/
|
||||
void
|
||||
endConsensus(bool correctLCL);
|
||||
onForceAccept(
|
||||
Result const& result,
|
||||
RCLCxLedger const& prevLedger,
|
||||
NetClock::duration const &closeResolution,
|
||||
CloseTimes const& rawCloseTimes,
|
||||
Mode const& mode);
|
||||
|
||||
//!-------------------------------------------------------------------------
|
||||
// Additional members (not directly required by Consensus interface)
|
||||
@@ -308,42 +304,53 @@ private:
|
||||
@param haveCorrectLCL Whether we believ we have the correct LCL.
|
||||
*/
|
||||
void
|
||||
notify(protocol::NodeEvent ne, RCLCxLedger const & ledger, bool haveCorrectLCL);
|
||||
notify(
|
||||
protocol::NodeEvent ne,
|
||||
RCLCxLedger const& ledger,
|
||||
bool haveCorrectLCL);
|
||||
|
||||
/** Build the new last closed ledger.
|
||||
/** Accept a new ledger based on the given transactions.
|
||||
|
||||
Accept the given the provided set of consensus transactions and build
|
||||
the last closed ledger. Since consensus just agrees on which
|
||||
transactions to apply, but not whether they make it into the closed
|
||||
ledger, this function also populates retriableTxs with those that can
|
||||
be retried in the next round.
|
||||
@ref onAccept
|
||||
*/
|
||||
void
|
||||
doAccept(
|
||||
Result const& result,
|
||||
RCLCxLedger const& prevLedger,
|
||||
NetClock::duration closeResolution,
|
||||
CloseTimes const& rawCloseTimes,
|
||||
Mode const& mode);
|
||||
|
||||
@param previousLedger Prior ledger building upon
|
||||
@param set The set of transactions to apply to the ledger
|
||||
@param closeTime The the ledger closed
|
||||
@param closeTimeCorrect Whether consensus agreed on close time
|
||||
@param closeResolution Resolution used to determine consensus close time
|
||||
@param now Current network adjusted time
|
||||
@param roundTime Duration of this consensus rorund
|
||||
@param retriableTxs Populate with transactions to retry in next round
|
||||
@return The newly built ledger
|
||||
*/
|
||||
/** Build the new last closed ledger.
|
||||
|
||||
Accept the given the provided set of consensus transactions and build
|
||||
the last closed ledger. Since consensus just agrees on which
|
||||
transactions to apply, but not whether they make it into the closed
|
||||
ledger, this function also populates retriableTxs with those that can
|
||||
be retried in the next round.
|
||||
|
||||
@param previousLedger Prior ledger building upon
|
||||
@param set The set of transactions to apply to the ledger
|
||||
@param closeTime The the ledger closed
|
||||
@param closeTimeCorrect Whether consensus agreed on close time
|
||||
@param closeResolution Resolution used to determine consensus close time
|
||||
@param roundTime Duration of this consensus rorund
|
||||
@param retriableTxs Populate with transactions to retry in next round
|
||||
@return The newly built ledger
|
||||
*/
|
||||
RCLCxLedger
|
||||
buildLCL(
|
||||
RCLCxLedger const & previousLedger,
|
||||
RCLTxSet const & set,
|
||||
RCLCxLedger const& previousLedger,
|
||||
RCLTxSet const& set,
|
||||
NetClock::time_point closeTime,
|
||||
bool closeTimeCorrect,
|
||||
NetClock::duration closeResolution,
|
||||
NetClock::time_point now,
|
||||
std::chrono::milliseconds roundTime,
|
||||
CanonicalTXSet & retriableTxs
|
||||
);
|
||||
CanonicalTXSet& retriableTxs);
|
||||
|
||||
/** Validate the given ledger and share with peers as necessary
|
||||
|
||||
@param ledger The ledger to validate
|
||||
@param now Current network adjusted time
|
||||
@param proposing Whether we were proposing transactions while generating
|
||||
this ledger. If we are not proposing, a validation
|
||||
can still be sent to inform peers that we know we
|
||||
@@ -351,16 +358,13 @@ private:
|
||||
around and trying to catch up.
|
||||
*/
|
||||
void
|
||||
validate(
|
||||
RCLCxLedger const & ledger,
|
||||
NetClock::time_point now,
|
||||
bool proposing);
|
||||
validate(RCLCxLedger const& ledger, bool proposing);
|
||||
|
||||
//!-------------------------------------------------------------------------
|
||||
Application& app_;
|
||||
std::unique_ptr <FeeVote> feeVote_;
|
||||
LedgerMaster & ledgerMaster_;
|
||||
LocalTxs & localTxs_;
|
||||
std::unique_ptr<FeeVote> feeVote_;
|
||||
LedgerMaster& ledgerMaster_;
|
||||
LocalTxs& localTxs_;
|
||||
InboundTransactions& inboundTransactions_;
|
||||
beast::Journal j_;
|
||||
|
||||
@@ -373,11 +377,13 @@ private:
|
||||
// only used for our own validations.
|
||||
NetClock::time_point lastValidationTime_;
|
||||
|
||||
using PeerPositions = hash_map <NodeID, std::deque<RCLCxPeerPos::pointer>>;
|
||||
using PeerPositions = hash_map<NodeID, std::deque<RCLCxPeerPos::pointer>>;
|
||||
PeerPositions peerPositions_;
|
||||
std::mutex peerPositionsLock_;
|
||||
};
|
||||
|
||||
bool validating_ = false;
|
||||
bool simulating_ = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#define RIPPLE_APP_CONSENSUS_RCLCXLEDGER_H_INCLUDED
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <ripple/app/ledger/LedgerToJson.h>
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <ripple/protocol/RippleLedgerHash.h>
|
||||
#include <memory>
|
||||
|
||||
@@ -50,24 +50,26 @@ public:
|
||||
|
||||
@param l The ledger to wrap.
|
||||
*/
|
||||
RCLCxLedger(std::shared_ptr<Ledger const> const & l) : ledger_{ l } {}
|
||||
RCLCxLedger(std::shared_ptr<Ledger const> const& l) : ledger_{l}
|
||||
{
|
||||
}
|
||||
|
||||
//! Sequence number of the ledger.
|
||||
auto const &
|
||||
auto const&
|
||||
seq() const
|
||||
{
|
||||
return ledger_->info().seq;
|
||||
}
|
||||
|
||||
//! Unique identifier (hash) of this ledger.
|
||||
auto const &
|
||||
auto const&
|
||||
id() const
|
||||
{
|
||||
return ledger_->info().hash;
|
||||
}
|
||||
|
||||
//! Unique identifier (hash) of this ledger's parent.
|
||||
auto const &
|
||||
auto const&
|
||||
parentID() const
|
||||
{
|
||||
return ledger_->info().parentHash;
|
||||
@@ -114,8 +116,6 @@ public:
|
||||
a new ledger from a readView?
|
||||
*/
|
||||
std::shared_ptr<Ledger const> ledger_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -19,28 +19,28 @@
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/consensus/RCLCxPeerPos.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/HashPrefix.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/digest.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Used to construct received proposals
|
||||
RCLCxPeerPos::RCLCxPeerPos (
|
||||
PublicKey const& publicKey,
|
||||
Slice const& signature,
|
||||
uint256 const& suppression,
|
||||
Proposal && proposal)
|
||||
: proposal_{ std::move(proposal)}
|
||||
, mSuppression {suppression}
|
||||
RCLCxPeerPos::RCLCxPeerPos(
|
||||
PublicKey const& publicKey,
|
||||
Slice const& signature,
|
||||
uint256 const& suppression,
|
||||
Proposal&& proposal)
|
||||
: proposal_{std::move(proposal)}
|
||||
, mSuppression{suppression}
|
||||
, publicKey_{publicKey}
|
||||
, signature_{signature}
|
||||
{
|
||||
|
||||
}
|
||||
uint256 RCLCxPeerPos::getSigningHash () const
|
||||
uint256
|
||||
RCLCxPeerPos::getSigningHash() const
|
||||
{
|
||||
return sha512Half(
|
||||
HashPrefix::proposal,
|
||||
@@ -50,28 +50,25 @@ uint256 RCLCxPeerPos::getSigningHash () const
|
||||
proposal().position());
|
||||
}
|
||||
|
||||
bool RCLCxPeerPos::checkSign () const
|
||||
bool
|
||||
RCLCxPeerPos::checkSign() const
|
||||
{
|
||||
return verifyDigest (
|
||||
publicKey_,
|
||||
getSigningHash(),
|
||||
signature_,
|
||||
false);
|
||||
return verifyDigest(publicKey_, getSigningHash(), signature_, false);
|
||||
}
|
||||
|
||||
Json::Value RCLCxPeerPos::getJson () const
|
||||
Json::Value
|
||||
RCLCxPeerPos::getJson() const
|
||||
{
|
||||
auto ret = proposal().getJson();
|
||||
|
||||
if (publicKey_.size())
|
||||
ret[jss::peer_id] = toBase58 (
|
||||
TokenType::TOKEN_NODE_PUBLIC,
|
||||
publicKey_);
|
||||
ret[jss::peer_id] = toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey_);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint256 proposalUniqueId (
|
||||
uint256
|
||||
proposalUniqueId(
|
||||
uint256 const& proposeHash,
|
||||
uint256 const& previousLedger,
|
||||
std::uint32_t proposeSeq,
|
||||
@@ -79,15 +76,15 @@ uint256 proposalUniqueId (
|
||||
Slice const& publicKey,
|
||||
Slice const& signature)
|
||||
{
|
||||
Serializer s (512);
|
||||
s.add256 (proposeHash);
|
||||
s.add256 (previousLedger);
|
||||
s.add32 (proposeSeq);
|
||||
s.add32 (closeTime.time_since_epoch().count());
|
||||
s.addVL (publicKey);
|
||||
s.addVL (signature);
|
||||
Serializer s(512);
|
||||
s.add256(proposeHash);
|
||||
s.add256(previousLedger);
|
||||
s.add32(proposeSeq);
|
||||
s.add32(closeTime.time_since_epoch().count());
|
||||
s.addVL(publicKey);
|
||||
s.addVL(signature);
|
||||
|
||||
return s.getSHA512Half ();
|
||||
return s.getSHA512Half();
|
||||
}
|
||||
|
||||
} // ripple
|
||||
} // ripple
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
|
||||
#include <ripple/basics/CountedObject.h>
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/beast/hash/hash_append.h>
|
||||
#include <ripple/consensus/ConsensusProposal.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/HashPrefix.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/SecretKey.h>
|
||||
#include <ripple/beast/hash/hash_append.h>
|
||||
#include <ripple/consensus/ConsensusProposal.h>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -38,18 +38,20 @@ namespace ripple {
|
||||
|
||||
Carries a ConsensusProposal signed by a peer.
|
||||
*/
|
||||
class RCLCxPeerPos
|
||||
: public CountedObject <RCLCxPeerPos>
|
||||
class RCLCxPeerPos : public CountedObject<RCLCxPeerPos>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "RCLCxPeerPos"; }
|
||||
static char const*
|
||||
getCountedObjectName()
|
||||
{
|
||||
return "RCLCxPeerPos";
|
||||
}
|
||||
using pointer = std::shared_ptr<RCLCxPeerPos>;
|
||||
using ref = const pointer&;
|
||||
|
||||
//< The type of the proposed position
|
||||
using Proposal = ConsensusProposal<NodeID, uint256, uint256>;
|
||||
|
||||
|
||||
/** Constructor
|
||||
|
||||
Constructs a signed peer position.
|
||||
@@ -60,57 +62,64 @@ public:
|
||||
@param proposal The consensus proposal
|
||||
*/
|
||||
|
||||
RCLCxPeerPos (
|
||||
RCLCxPeerPos(
|
||||
PublicKey const& publicKey,
|
||||
Slice const& signature,
|
||||
uint256 const& suppress,
|
||||
Proposal && proposal);
|
||||
Proposal&& proposal);
|
||||
|
||||
//! Create the signing hash for the proposal
|
||||
uint256 getSigningHash () const;
|
||||
uint256
|
||||
getSigningHash() const;
|
||||
|
||||
//! Verify the signing hash of the proposal
|
||||
bool checkSign () const;
|
||||
bool
|
||||
checkSign() const;
|
||||
|
||||
//! Signature of the proposal (not necessarily verified)
|
||||
Slice getSignature () const
|
||||
Slice
|
||||
getSignature() const
|
||||
{
|
||||
return signature_;
|
||||
}
|
||||
|
||||
//! Public key of peer that sent the proposal
|
||||
PublicKey const& getPublicKey () const
|
||||
PublicKey const&
|
||||
getPublicKey() const
|
||||
{
|
||||
return publicKey_;
|
||||
}
|
||||
|
||||
//! ?????
|
||||
uint256 const& getSuppressionID () const
|
||||
uint256 const&
|
||||
getSuppressionID() const
|
||||
{
|
||||
return mSuppression;
|
||||
}
|
||||
|
||||
//! The consensus proposal
|
||||
Proposal const & proposal() const
|
||||
Proposal const&
|
||||
proposal() const
|
||||
{
|
||||
return proposal_;
|
||||
}
|
||||
|
||||
/// @cond Ignore
|
||||
//! Add a conversion operator to conform to the Consensus interface
|
||||
operator Proposal const &() const
|
||||
operator Proposal const&() const
|
||||
{
|
||||
return proposal_;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
//! JSON representation of proposal
|
||||
Json::Value getJson () const;
|
||||
Json::Value
|
||||
getJson() const;
|
||||
|
||||
private:
|
||||
template <class Hasher>
|
||||
void
|
||||
hash_append (Hasher& h) const
|
||||
hash_append(Hasher& h) const
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, HashPrefix::proposal);
|
||||
@@ -142,14 +151,15 @@ private:
|
||||
@param publicKey Signer's public key
|
||||
@param signature Proposal signature
|
||||
*/
|
||||
uint256 proposalUniqueId (
|
||||
uint256 const& proposeHash,
|
||||
uint256 const& previousLedger,
|
||||
std::uint32_t proposeSeq,
|
||||
NetClock::time_point closeTime,
|
||||
Slice const& publicKey,
|
||||
Slice const& signature);
|
||||
uint256
|
||||
proposalUniqueId(
|
||||
uint256 const& proposeHash,
|
||||
uint256 const& previousLedger,
|
||||
std::uint32_t proposeSeq,
|
||||
NetClock::time_point closeTime,
|
||||
Slice const& publicKey,
|
||||
Slice const& signature);
|
||||
|
||||
} // ripple
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
#ifndef RIPPLE_APP_CONSENSUS_RCLCXTX_H_INCLUDED
|
||||
#define RIPPLE_APP_CONSENSUS_RCLCXTX_H_INCLUDED
|
||||
|
||||
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/shamap/SHAMap.h>
|
||||
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -42,14 +42,15 @@ public:
|
||||
|
||||
@param txn The transaction to wrap
|
||||
*/
|
||||
RCLCxTx(SHAMapItem const& txn) : tx_{ txn }
|
||||
{ }
|
||||
RCLCxTx(SHAMapItem const& txn) : tx_{txn}
|
||||
{
|
||||
}
|
||||
|
||||
//! The unique identifier/hash of the transaction
|
||||
ID const&
|
||||
id() const
|
||||
{
|
||||
return tx_.key ();
|
||||
return tx_.key();
|
||||
}
|
||||
|
||||
//! The SHAMapItem that represents the transaction.
|
||||
@@ -74,13 +75,11 @@ public:
|
||||
{
|
||||
friend class RCLTxSet;
|
||||
//! The SHAMap representing the transactions.
|
||||
std::shared_ptr <SHAMap> map_;
|
||||
std::shared_ptr<SHAMap> map_;
|
||||
|
||||
public:
|
||||
MutableTxSet(RCLTxSet const & src)
|
||||
: map_{ src.map_->snapShot(true) }
|
||||
MutableTxSet(RCLTxSet const& src) : map_{src.map_->snapShot(true)}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/** Insert a new transaction into the set.
|
||||
@@ -92,8 +91,7 @@ public:
|
||||
insert(Tx const& t)
|
||||
{
|
||||
return map_->addItem(
|
||||
SHAMapItem{ t.id(), t.tx_.peekData() },
|
||||
true, false);
|
||||
SHAMapItem{t.id(), t.tx_.peekData()}, true, false);
|
||||
}
|
||||
|
||||
/** Remove a transaction from the set.
|
||||
@@ -112,8 +110,7 @@ public:
|
||||
|
||||
@param m SHAMap to wrap
|
||||
*/
|
||||
RCLTxSet (std::shared_ptr<SHAMap> m)
|
||||
: map_{ std::move(m) }
|
||||
RCLTxSet(std::shared_ptr<SHAMap> m) : map_{std::move(m)}
|
||||
{
|
||||
assert(map_);
|
||||
}
|
||||
@@ -122,10 +119,8 @@ public:
|
||||
|
||||
@param m MutableTxSet that will become fixed
|
||||
*/
|
||||
RCLTxSet(MutableTxSet const & m)
|
||||
: map_{m.map_->snapShot(false)}
|
||||
RCLTxSet(MutableTxSet const& m) : map_{m.map_->snapShot(false)}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/** Test if a transaction is in the set.
|
||||
@@ -136,7 +131,7 @@ public:
|
||||
bool
|
||||
exists(Tx::ID const& entry) const
|
||||
{
|
||||
return map_->hasItem (entry);
|
||||
return map_->hasItem(entry);
|
||||
}
|
||||
|
||||
/** Lookup a transaction.
|
||||
@@ -150,10 +145,10 @@ public:
|
||||
code use the shared_ptr semantics to know whether the find
|
||||
was succesfully and properly creates a Tx as needed.
|
||||
*/
|
||||
std::shared_ptr<const SHAMapItem> const &
|
||||
std::shared_ptr<const SHAMapItem> const&
|
||||
find(Tx::ID const& entry) const
|
||||
{
|
||||
return map_->peekItem (entry);
|
||||
return map_->peekItem(entry);
|
||||
}
|
||||
|
||||
//! The unique ID/hash of the transaction set
|
||||
@@ -163,7 +158,8 @@ public:
|
||||
return map_->getHash().as_uint256();
|
||||
}
|
||||
|
||||
/** Find transactions not in common between this and another transaction set.
|
||||
/** Find transactions not in common between this and another transaction
|
||||
set.
|
||||
|
||||
@param j The set to compare with
|
||||
@return Map of transactions in this set and `j` but not both. The key
|
||||
@@ -171,28 +167,28 @@ public:
|
||||
exists in this set.
|
||||
*/
|
||||
std::map<Tx::ID, bool>
|
||||
compare (RCLTxSet const& j) const
|
||||
compare(RCLTxSet const& j) const
|
||||
{
|
||||
SHAMap::Delta delta;
|
||||
|
||||
// Bound the work we do in case of a malicious
|
||||
// map_ from a trusted validator
|
||||
map_->compare (*(j.map_), delta, 65536);
|
||||
map_->compare(*(j.map_), delta, 65536);
|
||||
|
||||
std::map <uint256, bool> ret;
|
||||
std::map<uint256, bool> ret;
|
||||
for (auto const& item : delta)
|
||||
{
|
||||
assert ( (item.second.first && ! item.second.second) ||
|
||||
(item.second.second && ! item.second.first) );
|
||||
assert(
|
||||
(item.second.first && !item.second.second) ||
|
||||
(item.second.second && !item.second.first));
|
||||
|
||||
ret[item.first] = static_cast<bool> (item.second.first);
|
||||
ret[item.first] = static_cast<bool>(item.second.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! The SHAMap representing the transactions.
|
||||
std::shared_ptr <SHAMap> map_;
|
||||
std::shared_ptr<SHAMap> map_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -322,7 +322,7 @@ private:
|
||||
|
||||
public:
|
||||
bool beginConsensus (uint256 const& networkClosed) override;
|
||||
void endConsensus (bool correctLCL) override;
|
||||
void endConsensus () override;
|
||||
void setStandAlone () override
|
||||
{
|
||||
setMode (omFULL);
|
||||
@@ -1525,7 +1525,7 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
|
||||
|
||||
uint256 NetworkOPsImp::getConsensusLCL ()
|
||||
{
|
||||
return mConsensus->LCL ();
|
||||
return mConsensus->prevLedgerID ();
|
||||
}
|
||||
|
||||
void NetworkOPsImp::processTrustedProposal (
|
||||
@@ -1565,7 +1565,7 @@ NetworkOPsImp::mapComplete (
|
||||
RCLTxSet{map});
|
||||
}
|
||||
|
||||
void NetworkOPsImp::endConsensus (bool correctLCL)
|
||||
void NetworkOPsImp::endConsensus ()
|
||||
{
|
||||
uint256 deadLedger = m_ledgerMaster.getClosedLedger ()->info().parentHash;
|
||||
|
||||
@@ -2164,18 +2164,18 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
|
||||
info[jss::peers] = Json::UInt (app_.overlay ().size ());
|
||||
|
||||
Json::Value lastClose = Json::objectValue;
|
||||
lastClose[jss::proposers] = mConsensus->getLastCloseProposers();
|
||||
lastClose[jss::proposers] = Json::UInt(mConsensus->prevProposers());
|
||||
|
||||
if (human)
|
||||
{
|
||||
lastClose[jss::converge_time_s] =
|
||||
std::chrono::duration<double>{
|
||||
mConsensus->getLastConvergeDuration()}.count();
|
||||
mConsensus->prevRoundTime()}.count();
|
||||
}
|
||||
else
|
||||
{
|
||||
lastClose[jss::converge_time] =
|
||||
Json::Int (mConsensus->getLastConvergeDuration().count());
|
||||
Json::Int (mConsensus->prevRoundTime().count());
|
||||
}
|
||||
|
||||
info[jss::last_close] = lastClose;
|
||||
@@ -2754,9 +2754,7 @@ std::uint32_t NetworkOPsImp::acceptLedger (
|
||||
// FIXME Could we improve on this and remove the need for a specialized
|
||||
// API in Consensus?
|
||||
beginConsensus (m_ledgerMaster.getClosedLedger()->info().hash);
|
||||
mConsensus->simulate (
|
||||
app_.timeKeeper().closeTime(),
|
||||
consensusDelay);
|
||||
mConsensus->simulate (app_.timeKeeper().closeTime(), consensusDelay);
|
||||
return m_ledgerMaster.getCurrentLedger ()->info().seq;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
|
||||
// network state machine
|
||||
virtual bool beginConsensus (uint256 const& netLCL) = 0;
|
||||
virtual void endConsensus (bool correctLCL) = 0;
|
||||
virtual void endConsensus () = 0;
|
||||
virtual void setStandAlone () = 0;
|
||||
virtual void setStateTimer () = 0;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 Ripple Labs Inc.
|
||||
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
|
||||
@@ -19,13 +19,12 @@
|
||||
#ifndef RIPPLE_CONSENSUS_ConsensusProposal_H_INCLUDED
|
||||
#define RIPPLE_CONSENSUS_ConsensusProposal_H_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ripple
|
||||
{
|
||||
namespace ripple {
|
||||
/** Represents a proposed position taken during a round of consensus.
|
||||
|
||||
During consensus, peers seek agreement on a set of transactions to
|
||||
@@ -49,10 +48,7 @@ namespace ripple
|
||||
@tparam Position_t Type used to represent the position taken on transactions
|
||||
under consideration during this round of consensus
|
||||
*/
|
||||
template <
|
||||
class NodeID_t,
|
||||
class LedgerID_t,
|
||||
class Position_t>
|
||||
template <class NodeID_t, class LedgerID_t, class Position_t>
|
||||
class ConsensusProposal
|
||||
{
|
||||
public:
|
||||
@@ -64,7 +60,6 @@ public:
|
||||
//< Sequence number when a peer wants to bow out and leave consensus
|
||||
static std::uint32_t const seqLeave = 0xffffffff;
|
||||
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param prevLedger The previous ledger this proposal is building on.
|
||||
@@ -81,33 +76,32 @@ public:
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now,
|
||||
NodeID_t const& nodeID)
|
||||
: previousLedger_(prevLedger)
|
||||
, position_(position)
|
||||
, closeTime_(closeTime)
|
||||
, time_(now)
|
||||
, proposeSeq_(seq)
|
||||
, nodeID_(nodeID)
|
||||
: previousLedger_(prevLedger)
|
||||
, position_(position)
|
||||
, closeTime_(closeTime)
|
||||
, time_(now)
|
||||
, proposeSeq_(seq)
|
||||
, nodeID_(nodeID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//! Identifying which peer took this position.
|
||||
NodeID_t const&
|
||||
nodeID () const
|
||||
nodeID() const
|
||||
{
|
||||
return nodeID_;
|
||||
}
|
||||
|
||||
//! Get the proposed position.
|
||||
Position_t const&
|
||||
position () const
|
||||
position() const
|
||||
{
|
||||
return position_;
|
||||
}
|
||||
|
||||
//! Get the prior accepted ledger this position is based on.
|
||||
LedgerID_t const&
|
||||
prevLedger () const
|
||||
prevLedger() const
|
||||
{
|
||||
return previousLedger_;
|
||||
}
|
||||
@@ -120,21 +114,21 @@ public:
|
||||
@return the sequence number
|
||||
*/
|
||||
std::uint32_t
|
||||
proposeSeq () const
|
||||
proposeSeq() const
|
||||
{
|
||||
return proposeSeq_;
|
||||
}
|
||||
|
||||
//! The current position on the consensus close time.
|
||||
NetClock::time_point const &
|
||||
closeTime () const
|
||||
NetClock::time_point const&
|
||||
closeTime() const
|
||||
{
|
||||
return closeTime_;
|
||||
}
|
||||
|
||||
//! Get when this position was taken.
|
||||
NetClock::time_point const &
|
||||
seenTime () const
|
||||
NetClock::time_point const&
|
||||
seenTime() const
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
@@ -143,49 +137,43 @@ public:
|
||||
consensus round.
|
||||
*/
|
||||
bool
|
||||
isInitial () const
|
||||
isInitial() const
|
||||
{
|
||||
return proposeSeq_ == seqJoin;
|
||||
}
|
||||
|
||||
//! Get whether this node left the consensus process
|
||||
bool
|
||||
isBowOut () const
|
||||
isBowOut() const
|
||||
{
|
||||
return proposeSeq_ == seqLeave;
|
||||
}
|
||||
|
||||
//! Get whether this position is stale relative to the provided cutoff
|
||||
bool
|
||||
isStale (NetClock::time_point cutoff) const
|
||||
isStale(NetClock::time_point cutoff) const
|
||||
{
|
||||
return time_ <= cutoff;
|
||||
}
|
||||
|
||||
/** Update the position during the consensus process. This will increment
|
||||
the proposal's sequence number.
|
||||
the proposal's sequence number if it has not already bowed out.
|
||||
|
||||
@param newPosition The new position taken.
|
||||
@param newCloseTime The new close time.
|
||||
@param now the time The new position was taken.
|
||||
|
||||
@return `true` if the position was updated or `false` if this node has
|
||||
already left this consensus round.
|
||||
@param now the time The new position was taken
|
||||
*/
|
||||
bool
|
||||
void
|
||||
changePosition(
|
||||
Position_t const& newPosition,
|
||||
NetClock::time_point newCloseTime,
|
||||
NetClock::time_point now)
|
||||
{
|
||||
if (proposeSeq_ == seqLeave)
|
||||
return false;
|
||||
|
||||
position_ = newPosition;
|
||||
closeTime_ = newCloseTime;
|
||||
time_ = now;
|
||||
++proposeSeq_;
|
||||
return true;
|
||||
position_ = newPosition;
|
||||
closeTime_ = newCloseTime;
|
||||
time_ = now;
|
||||
if (proposeSeq_ != seqLeave)
|
||||
++proposeSeq_;
|
||||
}
|
||||
|
||||
/** Leave consensus
|
||||
@@ -197,32 +185,32 @@ public:
|
||||
void
|
||||
bowOut(NetClock::time_point now)
|
||||
{
|
||||
time_ = now;
|
||||
proposeSeq_ = seqLeave;
|
||||
time_ = now;
|
||||
proposeSeq_ = seqLeave;
|
||||
}
|
||||
|
||||
//! Get JSON representation for debugging
|
||||
Json::Value
|
||||
getJson () const
|
||||
getJson() const
|
||||
{
|
||||
using std::to_string;
|
||||
|
||||
Json::Value ret = Json::objectValue;
|
||||
ret[jss::previous_ledger] = to_string (prevLedger());
|
||||
ret[jss::previous_ledger] = to_string(prevLedger());
|
||||
|
||||
if (!isBowOut())
|
||||
{
|
||||
ret[jss::transaction_hash] = to_string (position());
|
||||
ret[jss::transaction_hash] = to_string(position());
|
||||
ret[jss::propose_seq] = proposeSeq();
|
||||
}
|
||||
|
||||
ret[jss::close_time] = to_string(closeTime().time_since_epoch().count());
|
||||
ret[jss::close_time] =
|
||||
to_string(closeTime().time_since_epoch().count());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//! Unique identifier of prior ledger this proposal is based on
|
||||
LedgerID_t previousLedger_;
|
||||
|
||||
@@ -240,23 +228,17 @@ private:
|
||||
|
||||
//! The identifier of the node taking this position
|
||||
NodeID_t nodeID_;
|
||||
|
||||
};
|
||||
|
||||
template <class NodeID_t,
|
||||
class LedgerID_t,
|
||||
class Position_t>
|
||||
template <class NodeID_t, class LedgerID_t, class Position_t>
|
||||
bool
|
||||
operator==(ConsensusProposal<NodeID_t, LedgerID_t, Position_t> const & a,
|
||||
ConsensusProposal<NodeID_t, LedgerID_t, Position_t> const & b)
|
||||
operator==(
|
||||
ConsensusProposal<NodeID_t, LedgerID_t, Position_t> const& a,
|
||||
ConsensusProposal<NodeID_t, LedgerID_t, Position_t> const& b)
|
||||
{
|
||||
return a.nodeID() == b.nodeID() &&
|
||||
a.proposeSeq() == b.proposeSeq() &&
|
||||
a.prevLedger() == b.prevLedger() &&
|
||||
a.position() == b.position() &&
|
||||
a.closeTime() == b.closeTime() &&
|
||||
a.seenTime() == b.seenTime();
|
||||
return a.nodeID() == b.nodeID() && a.proposeSeq() == b.proposeSeq() &&
|
||||
a.prevLedger() == b.prevLedger() && a.position() == b.position() &&
|
||||
a.closeTime() == b.closeTime() && a.seenTime() == b.seenTime();
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_CONSENSUS_DISPUTEDTX_H_INCLUDED
|
||||
#define RIPPLE_APP_CONSENSUS_DISPUTEDTX_H_INCLUDED
|
||||
#ifndef RIPPLE_APP_CONSENSUS_IMPL_DISPUTEDTX_H_INCLUDED
|
||||
#define RIPPLE_APP_CONSENSUS_IMPL_DISPUTEDTX_H_INCLUDED
|
||||
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/protocol/Serializer.h>
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
@@ -47,7 +47,8 @@ namespace ripple {
|
||||
template <class Tx_t, class NodeID_t>
|
||||
class DisputedTx
|
||||
{
|
||||
using TxID_t = typename Tx_t::ID;
|
||||
using TxID_t = typename Tx_t::ID;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
@@ -55,41 +56,35 @@ public:
|
||||
@param ourVote Our vote on whether tx should be included
|
||||
@param j Journal for debugging
|
||||
*/
|
||||
DisputedTx (Tx_t const& tx,
|
||||
bool ourVote,
|
||||
beast::Journal j)
|
||||
: yays_ (0)
|
||||
, nays_ (0)
|
||||
, ourVote_ (ourVote)
|
||||
, tx_ (tx)
|
||||
, j_ (j)
|
||||
DisputedTx(Tx_t const& tx, bool ourVote, beast::Journal j)
|
||||
: yays_(0), nays_(0), ourVote_(ourVote), tx_(tx), j_(j)
|
||||
{
|
||||
}
|
||||
|
||||
//! The unique id/hash of the disputed transaction.
|
||||
TxID_t
|
||||
const& ID () const
|
||||
TxID_t const&
|
||||
ID() const
|
||||
{
|
||||
return tx_.id();
|
||||
}
|
||||
|
||||
//! Our vote on whether the transaction should be included.
|
||||
bool
|
||||
getOurVote () const
|
||||
getOurVote() const
|
||||
{
|
||||
return ourVote_;
|
||||
}
|
||||
|
||||
//! The disputed transaction.
|
||||
Tx_t
|
||||
const& tx () const
|
||||
Tx_t const&
|
||||
tx() const
|
||||
{
|
||||
return tx_;
|
||||
}
|
||||
|
||||
//! Change our vote
|
||||
void
|
||||
setOurVote (bool o)
|
||||
setOurVote(bool o)
|
||||
{
|
||||
ourVote_ = o;
|
||||
}
|
||||
@@ -100,14 +95,14 @@ public:
|
||||
@param votesYes Whether peer votes to include the disputed transaction.
|
||||
*/
|
||||
void
|
||||
setVote (NodeID_t const& peer, bool votesYes);
|
||||
setVote(NodeID_t const& peer, bool votesYes);
|
||||
|
||||
/** Remove a peer's vote
|
||||
|
||||
@param peer Identifier of peer.
|
||||
*/
|
||||
void
|
||||
unVote (NodeID_t const& peer);
|
||||
unVote(NodeID_t const& peer);
|
||||
|
||||
/** Update our vote given progression of consensus.
|
||||
|
||||
@@ -120,49 +115,47 @@ public:
|
||||
@return Whether our vote changed
|
||||
*/
|
||||
bool
|
||||
updateVote (int percentTime, bool proposing);
|
||||
updateVote(int percentTime, bool proposing);
|
||||
|
||||
//! JSON representation of dispute, used for debugging
|
||||
Json::Value
|
||||
getJson () const;
|
||||
getJson() const;
|
||||
|
||||
private:
|
||||
int yays_; //< Number of yes votes
|
||||
int nays_; //< Number of no votes
|
||||
int yays_; //< Number of yes votes
|
||||
int nays_; //< Number of no votes
|
||||
bool ourVote_; //< Our vote (true is yes)
|
||||
Tx_t tx_; //< Transaction under dispute
|
||||
Tx_t tx_; //< Transaction under dispute
|
||||
|
||||
hash_map <NodeID_t, bool> votes_; //< Votes of our peers
|
||||
beast::Journal j_; //< Debug journal
|
||||
hash_map<NodeID_t, bool> votes_; //< Votes of our peers
|
||||
beast::Journal j_; //< Debug journal
|
||||
};
|
||||
|
||||
// Track a peer's yes/no vote on a particular disputed tx_
|
||||
template <class Tx_t, class NodeID_t>
|
||||
void DisputedTx<Tx_t, NodeID_t>::setVote (NodeID_t const& peer, bool votesYes)
|
||||
void
|
||||
DisputedTx<Tx_t, NodeID_t>::setVote(NodeID_t const& peer, bool votesYes)
|
||||
{
|
||||
auto res = votes_.insert (std::make_pair (peer, votesYes));
|
||||
auto res = votes_.insert(std::make_pair(peer, votesYes));
|
||||
|
||||
// new vote
|
||||
if (res.second)
|
||||
{
|
||||
if (votesYes)
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Peer " << peer << " votes YES on " << tx_.id();
|
||||
JLOG(j_.debug()) << "Peer " << peer << " votes YES on " << tx_.id();
|
||||
++yays_;
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Peer " << peer << " votes NO on " << tx_.id();
|
||||
JLOG(j_.debug()) << "Peer " << peer << " votes NO on " << tx_.id();
|
||||
++nays_;
|
||||
}
|
||||
}
|
||||
// changes vote to yes
|
||||
else if (votesYes && !res.first->second)
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Peer " << peer << " now votes YES on " << tx_.id();
|
||||
JLOG(j_.debug()) << "Peer " << peer << " now votes YES on " << tx_.id();
|
||||
--nays_;
|
||||
++yays_;
|
||||
res.first->second = true;
|
||||
@@ -170,8 +163,7 @@ void DisputedTx<Tx_t, NodeID_t>::setVote (NodeID_t const& peer, bool votesYes)
|
||||
// changes vote to no
|
||||
else if (!votesYes && res.first->second)
|
||||
{
|
||||
JLOG (j_.debug())
|
||||
<< "Peer " << peer << " now votes NO on " << tx_.id();
|
||||
JLOG(j_.debug()) << "Peer " << peer << " now votes NO on " << tx_.id();
|
||||
++nays_;
|
||||
--yays_;
|
||||
res.first->second = false;
|
||||
@@ -180,23 +172,25 @@ void DisputedTx<Tx_t, NodeID_t>::setVote (NodeID_t const& peer, bool votesYes)
|
||||
|
||||
// Remove a peer's vote on this disputed transasction
|
||||
template <class Tx_t, class NodeID_t>
|
||||
void DisputedTx<Tx_t, NodeID_t>::unVote (NodeID_t const& peer)
|
||||
void
|
||||
DisputedTx<Tx_t, NodeID_t>::unVote(NodeID_t const& peer)
|
||||
{
|
||||
auto it = votes_.find (peer);
|
||||
auto it = votes_.find(peer);
|
||||
|
||||
if (it != votes_.end ())
|
||||
if (it != votes_.end())
|
||||
{
|
||||
if (it->second)
|
||||
--yays_;
|
||||
else
|
||||
--nays_;
|
||||
|
||||
votes_.erase (it);
|
||||
votes_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Tx_t, class NodeID_t>
|
||||
bool DisputedTx<Tx_t, NodeID_t>::updateVote (int percentTime, bool proposing)
|
||||
bool
|
||||
DisputedTx<Tx_t, NodeID_t>::updateVote(int percentTime, bool proposing)
|
||||
{
|
||||
if (ourVote_ && (nays_ == 0))
|
||||
return false;
|
||||
@@ -207,7 +201,7 @@ bool DisputedTx<Tx_t, NodeID_t>::updateVote (int percentTime, bool proposing)
|
||||
bool newPosition;
|
||||
int weight;
|
||||
|
||||
if (proposing) // give ourselves full weight
|
||||
if (proposing) // give ourselves full weight
|
||||
{
|
||||
// This is basically the percentage of nodes voting 'yes' (including us)
|
||||
weight = (yays_ * 100 + (ourVote_ ? 100 : 0)) / (nays_ + yays_ + 1);
|
||||
@@ -219,7 +213,7 @@ bool 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;
|
||||
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)
|
||||
@@ -236,43 +230,43 @@ bool DisputedTx<Tx_t, NodeID_t>::updateVote (int percentTime, bool proposing)
|
||||
|
||||
if (newPosition == ourVote_)
|
||||
{
|
||||
JLOG (j_.info())
|
||||
<< "No change (" << (ourVote_ ? "YES" : "NO") << ") : weight "
|
||||
<< weight << ", percent " << percentTime;
|
||||
JLOG (j_.debug()) << getJson ();
|
||||
JLOG(j_.info()) << "No change (" << (ourVote_ ? "YES" : "NO")
|
||||
<< ") : weight " << weight << ", percent "
|
||||
<< percentTime;
|
||||
JLOG(j_.debug()) << getJson();
|
||||
return false;
|
||||
}
|
||||
|
||||
ourVote_ = newPosition;
|
||||
JLOG (j_.debug())
|
||||
<< "We now vote " << (ourVote_ ? "YES" : "NO")
|
||||
<< " on " << tx_.id();
|
||||
JLOG (j_.debug()) << getJson ();
|
||||
JLOG(j_.debug()) << "We now vote " << (ourVote_ ? "YES" : "NO") << " on "
|
||||
<< tx_.id();
|
||||
JLOG(j_.debug()) << getJson();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Tx_t, class NodeID_t>
|
||||
Json::Value DisputedTx<Tx_t, NodeID_t>::getJson () const
|
||||
Json::Value
|
||||
DisputedTx<Tx_t, NodeID_t>::getJson() const
|
||||
{
|
||||
using std::to_string;
|
||||
|
||||
Json::Value ret (Json::objectValue);
|
||||
Json::Value ret(Json::objectValue);
|
||||
|
||||
ret["yays"] = yays_;
|
||||
ret["nays"] = nays_;
|
||||
ret["our_vote"] = ourVote_;
|
||||
|
||||
if (!votes_.empty ())
|
||||
if (!votes_.empty())
|
||||
{
|
||||
Json::Value votesj (Json::objectValue);
|
||||
Json::Value votesj(Json::objectValue);
|
||||
for (auto& vote : votes_)
|
||||
votesj[to_string (vote.first)] = vote.second;
|
||||
ret["votes"] = std::move (votesj);
|
||||
votesj[to_string(vote.first)] = vote.second;
|
||||
ret["votes"] = std::move(votesj);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,66 +18,64 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
bool
|
||||
shouldCloseLedger (
|
||||
shouldCloseLedger(
|
||||
bool anyTransactions,
|
||||
std::size_t previousProposers,
|
||||
std::size_t prevProposers,
|
||||
std::size_t proposersClosed,
|
||||
std::size_t proposersValidated,
|
||||
std::chrono::milliseconds previousTime,
|
||||
std::chrono::milliseconds currentTime, // Time since last ledger's close time
|
||||
std::chrono::milliseconds openTime, // Time waiting to close this ledger
|
||||
std::chrono::milliseconds prevRoundTime,
|
||||
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,
|
||||
beast::Journal j)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
if ((previousTime < -1s) || (previousTime > 10min) ||
|
||||
(currentTime > 10min))
|
||||
if ((prevRoundTime < -1s) || (prevRoundTime > 10min) || (timeSincePrevClose > 10min))
|
||||
{
|
||||
// These are unexpected cases, we just close the ledger
|
||||
JLOG (j.warn()) <<
|
||||
"shouldCloseLedger Trans=" << (anyTransactions ? "yes" : "no") <<
|
||||
" Prop: " << previousProposers << "/" << proposersClosed <<
|
||||
" Secs: " << currentTime.count() << " (last: " <<
|
||||
previousTime.count() << ")";
|
||||
JLOG(j.warn()) << "shouldCloseLedger Trans="
|
||||
<< (anyTransactions ? "yes" : "no")
|
||||
<< " Prop: " << prevProposers << "/" << proposersClosed
|
||||
<< " Secs: " << timeSincePrevClose.count()
|
||||
<< " (last: " << prevRoundTime.count() << ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((proposersClosed + proposersValidated) > (previousProposers / 2))
|
||||
if ((proposersClosed + proposersValidated) > (prevProposers / 2))
|
||||
{
|
||||
// If more than half of the network has closed, we close
|
||||
JLOG (j.trace()) << "Others have closed";
|
||||
JLOG(j.trace()) << "Others have closed";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!anyTransactions)
|
||||
{
|
||||
// Only close at the end of the idle interval
|
||||
return currentTime >= idleInterval; // normal idle
|
||||
return timeSincePrevClose >= idleInterval; // normal idle
|
||||
}
|
||||
|
||||
// Preserve minimum ledger open time
|
||||
if (openTime < LEDGER_MIN_CLOSE)
|
||||
{
|
||||
JLOG (j.debug()) <<
|
||||
"Must wait minimum time before closing";
|
||||
JLOG(j.debug()) << "Must wait minimum time before closing";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let this ledger close more than twice as fast as the previous
|
||||
// ledger reached consensus so that slower validators can slow down
|
||||
// the network
|
||||
if (openTime < (previousTime / 2))
|
||||
if (openTime < (prevRoundTime / 2))
|
||||
{
|
||||
JLOG (j.debug()) <<
|
||||
"Ledger has not been open long enough";
|
||||
JLOG(j.debug()) << "Ledger has not been open long enough";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -86,10 +84,7 @@ 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)
|
||||
{
|
||||
// If we are alone, we have a consensus
|
||||
if (total == 0)
|
||||
@@ -107,8 +102,8 @@ checkConsensusReached (
|
||||
}
|
||||
|
||||
ConsensusState
|
||||
checkConsensus (
|
||||
std::size_t previousProposers,
|
||||
checkConsensus(
|
||||
std::size_t prevProposers,
|
||||
std::size_t currentProposers,
|
||||
std::size_t currentAgree,
|
||||
std::size_t currentFinished,
|
||||
@@ -117,47 +112,45 @@ checkConsensus (
|
||||
bool proposing,
|
||||
beast::Journal j)
|
||||
{
|
||||
JLOG (j.trace()) <<
|
||||
"checkConsensus: prop=" << currentProposers <<
|
||||
"/" << previousProposers <<
|
||||
" agree=" << currentAgree << " validated=" << currentFinished <<
|
||||
" time=" << currentAgreeTime.count() << "/" << previousAgreeTime.count();
|
||||
JLOG(j.trace()) << "checkConsensus: prop=" << currentProposers << "/"
|
||||
<< prevProposers << " agree=" << currentAgree
|
||||
<< " validated=" << currentFinished
|
||||
<< " time=" << currentAgreeTime.count() << "/"
|
||||
<< previousAgreeTime.count();
|
||||
|
||||
if (currentAgreeTime <= LEDGER_MIN_CONSENSUS)
|
||||
return ConsensusState::No;
|
||||
|
||||
if (currentProposers < (previousProposers * 3 / 4))
|
||||
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))
|
||||
{
|
||||
JLOG (j.trace()) <<
|
||||
"too fast, not enough proposers";
|
||||
JLOG(j.trace()) << "too fast, not enough proposers";
|
||||
return ConsensusState::No;
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
JLOG (j.debug()) << "normal consensus";
|
||||
JLOG(j.debug()) << "normal consensus";
|
||||
return ConsensusState::Yes;
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// no consensus yet
|
||||
JLOG (j.trace()) << "no consensus";
|
||||
JLOG(j.trace()) << "no consensus";
|
||||
return ConsensusState::No;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
} // ripple
|
||||
|
||||
@@ -20,14 +20,13 @@
|
||||
#ifndef RIPPLE_APP_LEDGER_LEDGERTIMING_H_INCLUDED
|
||||
#define RIPPLE_APP_LEDGER_LEDGERTIMING_H_INCLUDED
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// These are protocol parameters used to control the behavior of the system and
|
||||
// they should not be changed arbitrarily.
|
||||
@@ -42,16 +41,10 @@ using namespace std::chrono_literals;
|
||||
@see getNextLedgerTimeResolution
|
||||
*/
|
||||
std::chrono::seconds constexpr ledgerPossibleTimeResolutions[] =
|
||||
{ 10s, 20s, 30s, 60s, 90s, 120s };
|
||||
{10s, 20s, 30s, 60s, 90s, 120s};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
//! Initial resolution of ledger close time.
|
||||
auto constexpr ledgerDefaultTimeResolution = ledgerPossibleTimeResolutions[2];
|
||||
#else
|
||||
// HH Remove this workaround of a VS bug when possible
|
||||
//! Initial resolution of ledger close time.
|
||||
auto constexpr ledgerDefaultTimeResolution = 30s;
|
||||
#endif
|
||||
|
||||
//! How often we increase the close time resolution (in numbers of ledgers)
|
||||
auto constexpr increaseLedgerTimeResolutionEvery = 8;
|
||||
@@ -161,23 +154,25 @@ getNextLedgerTimeResolution(
|
||||
bool previousAgree,
|
||||
std::uint32_t ledgerSeq)
|
||||
{
|
||||
assert (ledgerSeq);
|
||||
assert(ledgerSeq);
|
||||
|
||||
using namespace std::chrono;
|
||||
// Find the current resolution:
|
||||
auto iter = std::find (std::begin (ledgerPossibleTimeResolutions),
|
||||
std::end (ledgerPossibleTimeResolutions), previousResolution);
|
||||
assert (iter != std::end (ledgerPossibleTimeResolutions));
|
||||
auto iter = std::find(
|
||||
std::begin(ledgerPossibleTimeResolutions),
|
||||
std::end(ledgerPossibleTimeResolutions),
|
||||
previousResolution);
|
||||
assert(iter != std::end(ledgerPossibleTimeResolutions));
|
||||
|
||||
// This should never happen, but just as a precaution
|
||||
if (iter == std::end (ledgerPossibleTimeResolutions))
|
||||
if (iter == std::end(ledgerPossibleTimeResolutions))
|
||||
return previousResolution;
|
||||
|
||||
// If we did not previously agree, we try to decrease the resolution to
|
||||
// improve the chance that we will agree now.
|
||||
if (!previousAgree && ledgerSeq % decreaseLedgerTimeResolutionEvery == 0)
|
||||
{
|
||||
if (++iter != std::end (ledgerPossibleTimeResolutions))
|
||||
if (++iter != std::end(ledgerPossibleTimeResolutions))
|
||||
return *iter;
|
||||
}
|
||||
|
||||
@@ -185,7 +180,7 @@ getNextLedgerTimeResolution(
|
||||
// if we can continue to agree.
|
||||
if (previousAgree && ledgerSeq % increaseLedgerTimeResolutionEvery == 0)
|
||||
{
|
||||
if (iter-- != std::begin (ledgerPossibleTimeResolutions))
|
||||
if (iter-- != std::begin(ledgerPossibleTimeResolutions))
|
||||
return *iter;
|
||||
}
|
||||
|
||||
@@ -212,7 +207,6 @@ roundCloseTime(
|
||||
return closeTime - (closeTime.time_since_epoch() % closeResolution);
|
||||
}
|
||||
|
||||
|
||||
/** Calculate the effective ledger close time
|
||||
|
||||
After adjusting the ledger close time based on the current resolution, also
|
||||
@@ -223,16 +217,17 @@ roundCloseTime(
|
||||
@param priorCloseTime The close time of the prior ledger
|
||||
*/
|
||||
template <class time_point>
|
||||
time_point effectiveCloseTime(time_point closeTime,
|
||||
typename time_point::duration const resolution,
|
||||
time_point
|
||||
effCloseTime(
|
||||
time_point closeTime,
|
||||
typename time_point::duration const resolution,
|
||||
time_point priorCloseTime)
|
||||
{
|
||||
if (closeTime == time_point{})
|
||||
return closeTime;
|
||||
|
||||
return std::max<time_point>(
|
||||
roundCloseTime (closeTime, resolution),
|
||||
(priorCloseTime + 1s));
|
||||
roundCloseTime(closeTime, resolution), (priorCloseTime + 1s));
|
||||
}
|
||||
|
||||
/** Determines whether the current ledger should close at this time.
|
||||
@@ -241,30 +236,29 @@ time_point effectiveCloseTime(time_point closeTime,
|
||||
in progress, or when a transaction is received and no close is in progress.
|
||||
|
||||
@param anyTransactions indicates whether any transactions have been received
|
||||
@param previousProposers proposers in the last closing
|
||||
@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 previousTime time for the previous ledger to reach consensus
|
||||
@param currentTime time since the previous ledger's
|
||||
(possibly rounded) close time
|
||||
@param openTime time waiting to close this 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 (
|
||||
shouldCloseLedger(
|
||||
bool anyTransactions,
|
||||
std::size_t previousProposers,
|
||||
std::size_t prevProposers,
|
||||
std::size_t proposersClosed,
|
||||
std::size_t proposersValidated,
|
||||
std::chrono::milliseconds previousTime,
|
||||
std::chrono::milliseconds currentTime, // Time since last ledger's close time
|
||||
std::chrono::milliseconds openTime, // Time waiting to close this ledger
|
||||
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
|
||||
@@ -275,22 +269,18 @@ shouldCloseLedger (
|
||||
@return True if a consensus has been reached
|
||||
*/
|
||||
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);
|
||||
|
||||
/** 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
|
||||
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 previousProposers proposers in the last closing (not including us)
|
||||
@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
|
||||
@@ -302,8 +292,8 @@ enum class ConsensusState
|
||||
@param j journal for logging
|
||||
*/
|
||||
ConsensusState
|
||||
checkConsensus (
|
||||
std::size_t previousProposers,
|
||||
checkConsensus(
|
||||
std::size_t prevProposers,
|
||||
std::size_t currentProposers,
|
||||
std::size_t currentAgree,
|
||||
std::size_t currentFinished,
|
||||
@@ -312,6 +302,6 @@ checkConsensus (
|
||||
bool proposing,
|
||||
beast::Journal j);
|
||||
|
||||
} // ripple
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,22 +17,20 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/beast/clock/manual_clock.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/consensus/Consensus.h>
|
||||
#include <ripple/consensus/ConsensusProposal.h>
|
||||
#include <ripple/beast/clock/manual_clock.h>
|
||||
#include <boost/function_output_iterator.hpp>
|
||||
#include <test/csf.h>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Consensus_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
|
||||
void
|
||||
testStandalone()
|
||||
{
|
||||
@@ -41,21 +39,22 @@ public:
|
||||
auto tg = TrustGraph::makeComplete(1);
|
||||
Sim s(tg, topology(tg, fixed{LEDGER_GRANULARITY}));
|
||||
|
||||
auto & p = s.peers[0];
|
||||
auto& p = s.peers[0];
|
||||
|
||||
p.targetLedgers = 1;
|
||||
p.start();
|
||||
p.submit(Tx{ 1 });
|
||||
p.submit(Tx{1});
|
||||
|
||||
s.net.step();
|
||||
|
||||
// Inspect that the proper ledger was created
|
||||
BEAST_EXPECT(p.LCL().seq == 1);
|
||||
BEAST_EXPECT(p.LCL() == p.lastClosedLedger.id());
|
||||
BEAST_EXPECT(p.prevLedgerID().seq == 1);
|
||||
BEAST_EXPECT(p.prevLedgerID() == p.lastClosedLedger.id());
|
||||
BEAST_EXPECT(p.lastClosedLedger.id().txs.size() == 1);
|
||||
BEAST_EXPECT(p.lastClosedLedger.id().txs.find(Tx{ 1 })
|
||||
!= p.lastClosedLedger.id().txs.end());
|
||||
BEAST_EXPECT(p.getLastCloseProposers() == 0);
|
||||
BEAST_EXPECT(
|
||||
p.lastClosedLedger.id().txs.find(Tx{1}) !=
|
||||
p.lastClosedLedger.id().txs.end());
|
||||
BEAST_EXPECT(p.prevProposers() == 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -65,24 +64,25 @@ public:
|
||||
using namespace std::chrono;
|
||||
|
||||
auto tg = TrustGraph::makeComplete(5);
|
||||
Sim sim(tg,
|
||||
Sim sim(
|
||||
tg,
|
||||
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
|
||||
|
||||
// everyone submits their own ID as a TX and relay it to peers
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
p.submit(Tx(p.id));
|
||||
|
||||
// Verify all peers have the same LCL and it has all the Txs
|
||||
sim.run(1);
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
auto const &lgrID = p.LCL();
|
||||
auto const& lgrID = p.prevLedgerID();
|
||||
BEAST_EXPECT(lgrID.seq == 1);
|
||||
BEAST_EXPECT(p.getLastCloseProposers() == sim.peers.size() - 1);
|
||||
for(std::uint32_t i = 0; i < sim.peers.size(); ++i)
|
||||
BEAST_EXPECT(lgrID.txs.find(Tx{ i }) != lgrID.txs.end());
|
||||
BEAST_EXPECT(p.prevProposers() == sim.peers.size() - 1);
|
||||
for (std::uint32_t i = 0; i < sim.peers.size(); ++i)
|
||||
BEAST_EXPECT(lgrID.txs.find(Tx{i}) != lgrID.txs.end());
|
||||
// Matches peer 0 ledger
|
||||
BEAST_EXPECT(lgrID.txs == sim.peers[0].LCL().txs);
|
||||
BEAST_EXPECT(lgrID.txs == sim.peers[0].prevLedgerID().txs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,70 +96,69 @@ public:
|
||||
// 1. The slow peer is participating in consensus
|
||||
// 2. The slow peer is just observing
|
||||
|
||||
for(auto isParticipant : {true, false})
|
||||
for (auto isParticipant : {true, false})
|
||||
{
|
||||
auto tg = TrustGraph::makeComplete(5);
|
||||
|
||||
Sim sim(tg, topology(tg,[](PeerID i, PeerID j)
|
||||
{
|
||||
auto delayFactor = (i == 0 || j == 0) ? 1.1 : 0.2;
|
||||
return round<milliseconds>(delayFactor* LEDGER_GRANULARITY);
|
||||
}));
|
||||
Sim sim(tg, topology(tg, [](PeerID i, PeerID j) {
|
||||
auto delayFactor = (i == 0 || j == 0) ? 1.1 : 0.2;
|
||||
return round<milliseconds>(
|
||||
delayFactor * LEDGER_GRANULARITY);
|
||||
}));
|
||||
|
||||
sim.peers[0].proposing = sim.peers[0].validating = isParticipant;
|
||||
sim.peers[0].proposing_ = sim.peers[0].validating_ = isParticipant;
|
||||
|
||||
// All peers submit their own ID as a transaction and relay it to peers
|
||||
for (auto & p : sim.peers)
|
||||
// All peers submit their own ID as a transaction and relay it to
|
||||
// peers
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
p.submit(Tx{ p.id });
|
||||
p.submit(Tx{p.id});
|
||||
}
|
||||
|
||||
sim.run(1);
|
||||
|
||||
// Verify all peers have same LCL but are missing transaction 0 which
|
||||
// was not received by all peers before the ledger closed
|
||||
for (auto & p : sim.peers)
|
||||
// Verify all peers have same LCL but are missing transaction 0
|
||||
// which was not received by all peers before the ledger closed
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
auto const &lgrID = p.LCL();
|
||||
auto const& lgrID = p.prevLedgerID();
|
||||
BEAST_EXPECT(lgrID.seq == 1);
|
||||
|
||||
|
||||
// If peer 0 is participating
|
||||
if(isParticipant)
|
||||
if (isParticipant)
|
||||
{
|
||||
BEAST_EXPECT(p.getLastCloseProposers()
|
||||
== sim.peers.size() - 1);
|
||||
// Peer 0 closes first because it sees a quorum of agreeing positions
|
||||
// from all other peers in one hop (1->0, 2->0, ..)
|
||||
// The other peers take an extra timer period before they find that
|
||||
// Peer 0 agrees with them ( 1->0->1, 2->0->2, ...)
|
||||
if(p.id != 0)
|
||||
BEAST_EXPECT(p.getLastConvergeDuration()
|
||||
> sim.peers[0].getLastConvergeDuration());
|
||||
BEAST_EXPECT(p.prevProposers() == sim.peers.size() - 1);
|
||||
// Peer 0 closes first because it sees a quorum of agreeing
|
||||
// positions from all other peers in one hop (1->0, 2->0,
|
||||
// ..) The other peers take an extra timer period before
|
||||
// they find that Peer 0 agrees with them ( 1->0->1,
|
||||
// 2->0->2, ...)
|
||||
if (p.id != 0)
|
||||
BEAST_EXPECT(
|
||||
p.prevRoundTime() > sim.peers[0].prevRoundTime());
|
||||
}
|
||||
else // peer 0 is not participating
|
||||
else // peer 0 is not participating
|
||||
{
|
||||
auto const proposers = p.getLastCloseProposers();
|
||||
if(p.id == 0)
|
||||
auto const proposers = p.prevProposers();
|
||||
if (p.id == 0)
|
||||
BEAST_EXPECT(proposers == sim.peers.size() - 1);
|
||||
else
|
||||
BEAST_EXPECT(proposers == sim.peers.size() - 2);
|
||||
|
||||
// so all peers should have closed together
|
||||
BEAST_EXPECT(p.getLastConvergeDuration()
|
||||
== sim.peers[0].getLastConvergeDuration());
|
||||
BEAST_EXPECT(
|
||||
p.prevRoundTime() == sim.peers[0].prevRoundTime());
|
||||
}
|
||||
|
||||
|
||||
BEAST_EXPECT(lgrID.txs.find(Tx{ 0 }) == lgrID.txs.end());
|
||||
for(std::uint32_t i = 1; i < sim.peers.size(); ++i)
|
||||
BEAST_EXPECT(lgrID.txs.find(Tx{ i }) != lgrID.txs.end());
|
||||
BEAST_EXPECT(lgrID.txs.find(Tx{0}) == lgrID.txs.end());
|
||||
for (std::uint32_t i = 1; i < sim.peers.size(); ++i)
|
||||
BEAST_EXPECT(lgrID.txs.find(Tx{i}) != lgrID.txs.end());
|
||||
// Matches peer 0 ledger
|
||||
BEAST_EXPECT(lgrID.txs == sim.peers[0].LCL().txs);
|
||||
BEAST_EXPECT(lgrID.txs == sim.peers[0].prevLedgerID().txs);
|
||||
}
|
||||
BEAST_EXPECT(sim.peers[0].openTxs.find(Tx{ 0 })
|
||||
!= sim.peers[0].openTxs.end());
|
||||
}
|
||||
BEAST_EXPECT(
|
||||
sim.peers[0].openTxs.find(Tx{0}) != sim.peers[0].openTxs.end());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -185,32 +184,35 @@ public:
|
||||
|
||||
// Complicating this matter is that nodes will ignore proposals
|
||||
// with times more than PROPOSE_FRESHNESS =20s in the past. So at
|
||||
// the minimum granularity, we have at most 3 types of skews (0s,10s,20s).
|
||||
// the minimum granularity, we have at most 3 types of skews
|
||||
// (0s,10s,20s).
|
||||
|
||||
// This test therefore has 6 nodes, with 2 nodes having each type of
|
||||
// skew. Then no majority (1/3 < 1/2) of nodes will agree on an
|
||||
// actual close time.
|
||||
|
||||
auto tg = TrustGraph::makeComplete(6);
|
||||
Sim sim(tg,
|
||||
Sim sim(
|
||||
tg,
|
||||
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
|
||||
|
||||
// Run consensus without skew until we have a short close time resolution
|
||||
while(sim.peers.front().lastClosedLedger.closeTimeResolution() >=
|
||||
PROPOSE_FRESHNESS)
|
||||
// Run consensus without skew until we have a short close time
|
||||
// resolution
|
||||
while (sim.peers.front().lastClosedLedger.closeTimeResolution() >=
|
||||
PROPOSE_FRESHNESS)
|
||||
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[0].clockSkew = PROPOSE_FRESHNESS / 2;
|
||||
sim.peers[1].clockSkew = PROPOSE_FRESHNESS / 2;
|
||||
sim.peers[2].clockSkew = PROPOSE_FRESHNESS;
|
||||
sim.peers[3].clockSkew = PROPOSE_FRESHNESS;
|
||||
|
||||
// Verify all peers have the same LCL and it has all the Txs
|
||||
sim.run(1);
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
BEAST_EXPECT(! p.lastClosedLedger.closeAgree());
|
||||
BEAST_EXPECT(!p.lastClosedLedger.closeAgree());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,9 +226,8 @@ public:
|
||||
|
||||
// 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 : {0s, LEDGER_MIN_CLOSE})
|
||||
{
|
||||
|
||||
// Consider 10 peers:
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
//
|
||||
@@ -241,19 +242,21 @@ public:
|
||||
// since nodes 2-4 will validate a different ledger.
|
||||
|
||||
// Nodes 0-1 will acquire the proper ledger from the network and
|
||||
// resume consensus and eventually generate the dominant network ledger
|
||||
// resume consensus and eventually generate the dominant network
|
||||
// ledger
|
||||
|
||||
std::vector<UNL> unls;
|
||||
unls.push_back({2,3,4,5,6,7,8,9});
|
||||
unls.push_back({0,1,2,3,4});
|
||||
std::vector<int> membership(10,0);
|
||||
unls.push_back({2, 3, 4, 5, 6, 7, 8, 9});
|
||||
unls.push_back({0, 1, 2, 3, 4});
|
||||
std::vector<int> membership(10, 0);
|
||||
membership[0] = 1;
|
||||
membership[1] = 1;
|
||||
|
||||
TrustGraph tg{unls, membership};
|
||||
|
||||
// This topology can fork, which is why we are using it for this test.
|
||||
BEAST_EXPECT(tg.canFork(minimumConsensusPercentage/100.));
|
||||
// This topology can fork, which is why we are using it for this
|
||||
// test.
|
||||
BEAST_EXPECT(tg.canFork(minimumConsensusPercentage / 100.));
|
||||
|
||||
auto netDelay = round<milliseconds>(0.2 * LEDGER_GRANULARITY);
|
||||
Sim sim(tg, topology(tg, fixed{netDelay}));
|
||||
@@ -261,8 +264,9 @@ public:
|
||||
// initial round to set prior state
|
||||
sim.run(1);
|
||||
|
||||
// Nodes in smaller UNL have seen tx 0, nodes in other unl have seen tx 1
|
||||
for (auto & p : sim.peers)
|
||||
// Nodes in smaller UNL have seen tx 0, nodes in other unl have seen
|
||||
// tx 1
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
p.validationDelay = validationDelay;
|
||||
p.missingLedgerDelay = netDelay;
|
||||
@@ -272,16 +276,29 @@ public:
|
||||
p.openTxs.insert(Tx{1});
|
||||
}
|
||||
|
||||
// Run for 2 additional rounds
|
||||
// - One round to generate different ledgers
|
||||
// - One round to detect different prior ledgers (but still generate
|
||||
// wrong ones) and recover
|
||||
sim.run(2);
|
||||
// Run for additional rounds
|
||||
// With no validation delay, only 2 more rounds are needed.
|
||||
// 1. Round to generate different ledgers
|
||||
// 2. Round to detect different prior ledgers (but still generate
|
||||
// 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
|
||||
// rounds.
|
||||
// 1. Round to generate different ledgers
|
||||
// 2. Round to detect different prior ledgers (but still generate
|
||||
// wrong ones) but end up declaring consensus on wrong LCL (but
|
||||
// with the right transaction set!). This is because we detect
|
||||
// the wrong LCL after we have closed the ledger, so we declare
|
||||
// consensus based solely on our peer proposals. But we haven't
|
||||
// had time to acquire the right LCL
|
||||
// 3. Round to correct
|
||||
sim.run(3);
|
||||
|
||||
bc::flat_map<int, bc::flat_set<Ledger::ID>> ledgers;
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
for (auto const & l : p.ledgers)
|
||||
for (auto const& l : p.ledgers)
|
||||
{
|
||||
ledgers[l.first.seq].insert(l.first);
|
||||
}
|
||||
@@ -289,12 +306,19 @@ public:
|
||||
|
||||
BEAST_EXPECT(ledgers[0].size() == 1);
|
||||
BEAST_EXPECT(ledgers[1].size() == 1);
|
||||
BEAST_EXPECT(ledgers[2].size() == 2);
|
||||
BEAST_EXPECT(ledgers[3].size() == 1);
|
||||
|
||||
|
||||
if (validationDelay == 0s)
|
||||
{
|
||||
BEAST_EXPECT(ledgers[2].size() == 2);
|
||||
BEAST_EXPECT(ledgers[3].size() == 1);
|
||||
BEAST_EXPECT(ledgers[4].size() == 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(ledgers[2].size() == 2);
|
||||
BEAST_EXPECT(ledgers[3].size() == 2);
|
||||
BEAST_EXPECT(ledgers[4].size() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Additional test engineered to switch LCL during the establish phase.
|
||||
// This was added to trigger a scenario that previously crashed, in which
|
||||
// switchLCL switched from establish to open phase, but still processed
|
||||
@@ -330,7 +354,7 @@ public:
|
||||
|
||||
// Check all peers recovered
|
||||
for (auto &p : sim.peers)
|
||||
BEAST_EXPECT(p.LCL() == sim.peers[0].LCL());
|
||||
BEAST_EXPECT(p.prevLedgerID() == sim.peers[0].prevLedgerID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,43 +365,43 @@ public:
|
||||
using namespace std::chrono;
|
||||
|
||||
int numPeers = 10;
|
||||
for(int overlap = 0; overlap <= numPeers; ++overlap)
|
||||
for (int overlap = 0; overlap <= numPeers; ++overlap)
|
||||
{
|
||||
auto tg = TrustGraph::makeClique(numPeers, overlap);
|
||||
Sim sim(tg, topology(tg,
|
||||
fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
|
||||
Sim sim(
|
||||
tg,
|
||||
topology(
|
||||
tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)}));
|
||||
|
||||
// Initial round to set prior state
|
||||
sim.run(1);
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
// Nodes have only seen transactions from their neighbors
|
||||
p.openTxs.insert(Tx{p.id});
|
||||
for(auto const link : sim.net.links(&p))
|
||||
for (auto const link : sim.net.links(&p))
|
||||
p.openTxs.insert(Tx{link.to->id});
|
||||
}
|
||||
sim.run(1);
|
||||
|
||||
|
||||
// See if the network forked
|
||||
bc::flat_set<Ledger::ID> ledgers;
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
ledgers.insert(p.LCL());
|
||||
ledgers.insert(p.prevLedgerID());
|
||||
}
|
||||
|
||||
// Fork should not happen for 40% or greater overlap
|
||||
// Since the overlapped nodes have a UNL that is the union of the
|
||||
// two cliques, the maximum sized UNL list is the number of peers
|
||||
if(overlap > 0.4 * numPeers)
|
||||
if (overlap > 0.4 * numPeers)
|
||||
BEAST_EXPECT(ledgers.size() == 1);
|
||||
else // Even if we do fork, there shouldn't be more than 3 ledgers
|
||||
// One for cliqueA, one for cliqueB and one for nodes in both
|
||||
BEAST_EXPECT(ledgers.size() <= 3);
|
||||
else // Even if we do fork, there shouldn't be more than 3 ledgers
|
||||
// One for cliqueA, one for cliqueB and one for nodes in both
|
||||
BEAST_EXPECT(ledgers.size() <= 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
simClockSkew()
|
||||
{
|
||||
@@ -396,52 +420,44 @@ public:
|
||||
|
||||
// Disabled while continuing to understand testt.
|
||||
|
||||
|
||||
for(auto stagger : {800ms, 1600ms, 3200ms, 30000ms, 45000ms, 300000ms})
|
||||
for (auto stagger : {800ms, 1600ms, 3200ms, 30000ms, 45000ms, 300000ms})
|
||||
{
|
||||
|
||||
auto tg = TrustGraph::makeComplete(5);
|
||||
Sim sim(tg, topology(tg, [](PeerID i, PeerID)
|
||||
{
|
||||
return 200ms * (i + 1);
|
||||
}));
|
||||
|
||||
Sim sim(tg, topology(tg, [](PeerID i, PeerID) {
|
||||
return 200ms * (i + 1);
|
||||
}));
|
||||
|
||||
// all transactions submitted before starting
|
||||
// Initial round to set prior state
|
||||
sim.run(1);
|
||||
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
p.openTxs.insert(Tx{ 0 });
|
||||
p.openTxs.insert(Tx{0});
|
||||
p.targetLedgers = p.completedLedgers + 1;
|
||||
|
||||
}
|
||||
|
||||
// stagger start of consensus
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
p.start();
|
||||
sim.net.step_for(stagger);
|
||||
}
|
||||
|
||||
// run until all peers have accepted all transactions
|
||||
sim.net.step_while([&]()
|
||||
{
|
||||
for(auto & p : sim.peers)
|
||||
{
|
||||
if(p.LCL().txs.size() != 1)
|
||||
sim.net.step_while([&]() {
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
if (p.prevLedgerID().txs.size() != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
simScaleFree()
|
||||
{
|
||||
@@ -450,45 +466,46 @@ public:
|
||||
// Generate a quasi-random scale free network and simulate consensus
|
||||
// for a single transaction
|
||||
|
||||
int N = 100; // Peers
|
||||
int N = 100; // Peers
|
||||
|
||||
int numUNLs = 15; // UNL lists
|
||||
int minUNLSize = N/4, maxUNLSize = N / 2;
|
||||
int minUNLSize = N / 4, maxUNLSize = N / 2;
|
||||
|
||||
double transProb = 0.5;
|
||||
|
||||
std::mt19937_64 rng;
|
||||
|
||||
auto tg = TrustGraph::makeRandomRanked(N, numUNLs,
|
||||
PowerLawDistribution{1,3},
|
||||
std::uniform_int_distribution<>{minUNLSize, maxUNLSize},
|
||||
rng);
|
||||
auto tg = TrustGraph::makeRandomRanked(
|
||||
N,
|
||||
numUNLs,
|
||||
PowerLawDistribution{1, 3},
|
||||
std::uniform_int_distribution<>{minUNLSize, maxUNLSize},
|
||||
rng);
|
||||
|
||||
Sim sim{tg, topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)})};
|
||||
Sim sim{
|
||||
tg,
|
||||
topology(tg, fixed{round<milliseconds>(0.2 * LEDGER_GRANULARITY)})};
|
||||
|
||||
// Initial round to set prior state
|
||||
sim.run(1);
|
||||
|
||||
std::uniform_real_distribution<> u{};
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
// 50-50 chance to have seen a transaction
|
||||
if(u(rng) >= transProb)
|
||||
if (u(rng) >= transProb)
|
||||
p.openTxs.insert(Tx{0});
|
||||
|
||||
}
|
||||
sim.run(1);
|
||||
|
||||
|
||||
// See if the network forked
|
||||
bc::flat_set<Ledger::ID> ledgers;
|
||||
for (auto & p : sim.peers)
|
||||
for (auto& p : sim.peers)
|
||||
{
|
||||
ledgers.insert(p.LCL());
|
||||
ledgers.insert(p.prevLedgerID());
|
||||
}
|
||||
|
||||
BEAST_EXPECT(ledgers.size() == 1);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
@@ -507,5 +524,5 @@ public:
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Consensus, consensus, ripple);
|
||||
} // test
|
||||
} // ripple
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@@ -98,58 +98,54 @@ class BasicNetwork
|
||||
public:
|
||||
using peer_type = Peer;
|
||||
|
||||
using clock_type =
|
||||
beast::manual_clock<
|
||||
std::chrono::steady_clock>;
|
||||
using clock_type = beast::manual_clock<std::chrono::steady_clock>;
|
||||
|
||||
using duration =
|
||||
typename clock_type::duration;
|
||||
using duration = typename clock_type::duration;
|
||||
|
||||
using time_point =
|
||||
typename clock_type::time_point;
|
||||
using time_point = typename clock_type::time_point;
|
||||
|
||||
private:
|
||||
struct by_to_tag {};
|
||||
struct by_from_tag {};
|
||||
struct by_when_tag {};
|
||||
struct by_to_tag
|
||||
{
|
||||
};
|
||||
struct by_from_tag
|
||||
{
|
||||
};
|
||||
struct by_when_tag
|
||||
{
|
||||
};
|
||||
|
||||
using by_to_hook =
|
||||
boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>,
|
||||
boost::intrusive::tag<by_to_tag>>;
|
||||
using by_to_hook = boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>,
|
||||
boost::intrusive::tag<by_to_tag>>;
|
||||
|
||||
using by_from_hook =
|
||||
boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>,
|
||||
boost::intrusive::tag<by_from_tag>>;
|
||||
using by_from_hook = boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>,
|
||||
boost::intrusive::tag<by_from_tag>>;
|
||||
|
||||
using by_when_hook =
|
||||
boost::intrusive::set_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>;
|
||||
using by_when_hook = boost::intrusive::set_base_hook<
|
||||
boost::intrusive::link_mode<boost::intrusive::normal_link>>;
|
||||
|
||||
struct msg
|
||||
: by_to_hook, by_from_hook, by_when_hook
|
||||
struct msg : by_to_hook, by_from_hook, by_when_hook
|
||||
{
|
||||
Peer to;
|
||||
Peer from;
|
||||
time_point when;
|
||||
|
||||
msg (msg const&) = delete;
|
||||
msg& operator= (msg const&) = delete;
|
||||
msg(msg const&) = delete;
|
||||
msg&
|
||||
operator=(msg const&) = delete;
|
||||
virtual ~msg() = default;
|
||||
virtual void operator()() const = 0;
|
||||
virtual void
|
||||
operator()() const = 0;
|
||||
|
||||
msg (Peer const& from_, Peer const& to_,
|
||||
time_point when_)
|
||||
msg(Peer const& from_, Peer const& to_, time_point when_)
|
||||
: to(to_), from(from_), when(when_)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
operator< (msg const& other) const
|
||||
operator<(msg const& other) const
|
||||
{
|
||||
return when < other.when;
|
||||
}
|
||||
@@ -162,24 +158,30 @@ private:
|
||||
Handler const h_;
|
||||
|
||||
public:
|
||||
msg_impl (msg_impl const&) = delete;
|
||||
msg_impl& operator= (msg_impl const&) = delete;
|
||||
msg_impl(msg_impl const&) = delete;
|
||||
msg_impl&
|
||||
operator=(msg_impl const&) = delete;
|
||||
|
||||
msg_impl (Peer const& from_, Peer const& to_,
|
||||
time_point when_, Handler&& h)
|
||||
: msg (from_, to_, when_)
|
||||
, h_ (std::move(h))
|
||||
msg_impl(
|
||||
Peer const& from_,
|
||||
Peer const& to_,
|
||||
time_point when_,
|
||||
Handler&& h)
|
||||
: msg(from_, to_, when_), h_(std::move(h))
|
||||
{
|
||||
}
|
||||
|
||||
msg_impl (Peer const& from_, Peer const& to_,
|
||||
time_point when_, Handler const& h)
|
||||
: msg (from_, to_, when_)
|
||||
, h_ (h)
|
||||
msg_impl(
|
||||
Peer const& from_,
|
||||
Peer const& to_,
|
||||
time_point when_,
|
||||
Handler const& h)
|
||||
: msg(from_, to_, when_), h_(h)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()() const override
|
||||
void
|
||||
operator()() const override
|
||||
{
|
||||
h_();
|
||||
}
|
||||
@@ -188,19 +190,19 @@ private:
|
||||
class queue_type
|
||||
{
|
||||
private:
|
||||
using by_to_list = typename
|
||||
boost::intrusive::make_list<msg,
|
||||
boost::intrusive::base_hook<by_to_hook>,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
using by_to_list = typename boost::intrusive::make_list<
|
||||
msg,
|
||||
boost::intrusive::base_hook<by_to_hook>,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using by_from_list = typename
|
||||
boost::intrusive::make_list<msg,
|
||||
boost::intrusive::base_hook<by_from_hook>,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
using by_from_list = typename boost::intrusive::make_list<
|
||||
msg,
|
||||
boost::intrusive::base_hook<by_from_hook>,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using by_when_set = typename
|
||||
boost::intrusive::make_multiset<msg,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
using by_when_set = typename boost::intrusive::make_multiset<
|
||||
msg,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
qalloc alloc_;
|
||||
by_when_set by_when_;
|
||||
@@ -208,14 +210,13 @@ private:
|
||||
std::unordered_map<Peer, by_from_list> by_from_;
|
||||
|
||||
public:
|
||||
using iterator =
|
||||
typename by_when_set::iterator;
|
||||
using iterator = typename by_when_set::iterator;
|
||||
|
||||
queue_type (queue_type const&) = delete;
|
||||
queue_type& operator= (queue_type const&) = delete;
|
||||
queue_type(queue_type const&) = delete;
|
||||
queue_type&
|
||||
operator=(queue_type const&) = delete;
|
||||
|
||||
explicit
|
||||
queue_type (qalloc const& alloc);
|
||||
explicit queue_type(qalloc const& alloc);
|
||||
|
||||
~queue_type();
|
||||
|
||||
@@ -230,14 +231,13 @@ private:
|
||||
|
||||
template <class Handler>
|
||||
typename by_when_set::iterator
|
||||
emplace (Peer const& from, Peer const& to,
|
||||
time_point when, Handler&& h);
|
||||
emplace(Peer const& from, Peer const& to, time_point when, Handler&& h);
|
||||
|
||||
void
|
||||
erase (iterator iter);
|
||||
erase(iterator iter);
|
||||
|
||||
void
|
||||
remove (Peer const& from, Peer const& to);
|
||||
remove(Peer const& from, Peer const& to);
|
||||
};
|
||||
|
||||
struct link_type
|
||||
@@ -245,15 +245,13 @@ private:
|
||||
bool inbound;
|
||||
duration delay;
|
||||
|
||||
link_type (bool inbound_, duration delay_)
|
||||
: inbound (inbound_)
|
||||
, delay (delay_)
|
||||
link_type(bool inbound_, duration delay_)
|
||||
: inbound(inbound_), delay(delay_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using links_type =
|
||||
boost::container::flat_map<Peer, link_type>;
|
||||
using links_type = boost::container::flat_map<Peer, link_type>;
|
||||
|
||||
class link_transform;
|
||||
|
||||
@@ -265,8 +263,9 @@ private:
|
||||
std::unordered_map<Peer, links_type> links_;
|
||||
|
||||
public:
|
||||
BasicNetwork (BasicNetwork const&) = delete;
|
||||
BasicNetwork& operator= (BasicNetwork const&) = delete;
|
||||
BasicNetwork(BasicNetwork const&) = delete;
|
||||
BasicNetwork&
|
||||
operator=(BasicNetwork const&) = delete;
|
||||
|
||||
BasicNetwork();
|
||||
|
||||
@@ -308,7 +307,9 @@ public:
|
||||
@return `true` if a new connection was established
|
||||
*/
|
||||
bool
|
||||
connect (Peer const& from, Peer const& to,
|
||||
connect(
|
||||
Peer const& from,
|
||||
Peer const& to,
|
||||
duration const& delay = std::chrono::seconds{0});
|
||||
|
||||
/** Break a link.
|
||||
@@ -324,15 +325,14 @@ public:
|
||||
@return `true` if a connection was broken.
|
||||
*/
|
||||
bool
|
||||
disconnect (Peer const& peer1, Peer const& peer2);
|
||||
disconnect(Peer const& peer1, Peer const& peer2);
|
||||
|
||||
/** Return the range of active links.
|
||||
|
||||
@return A random access range.
|
||||
*/
|
||||
boost::transformed_range<
|
||||
link_transform, links_type>
|
||||
links (Peer const& from);
|
||||
boost::transformed_range<link_transform, links_type>
|
||||
links(Peer const& from);
|
||||
|
||||
/** Send a message to a peer.
|
||||
|
||||
@@ -354,8 +354,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
void
|
||||
send (Peer const& from, Peer const& to,
|
||||
Function&& f);
|
||||
send(Peer const& from, Peer const& to, Function&& f);
|
||||
|
||||
// Used to cancel timers
|
||||
struct cancel_token;
|
||||
@@ -370,8 +369,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
cancel_token
|
||||
timer (time_point const& when,
|
||||
Function&& f);
|
||||
timer(time_point const& when, Function&& f);
|
||||
|
||||
/** Deliver a timer notification.
|
||||
|
||||
@@ -383,8 +381,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
cancel_token
|
||||
timer (duration const& delay,
|
||||
Function&& f);
|
||||
timer(duration const& delay, Function&& f);
|
||||
|
||||
/** Cancel a timer.
|
||||
|
||||
@@ -394,7 +391,7 @@ public:
|
||||
timer() which has not yet been invoked.
|
||||
*/
|
||||
void
|
||||
cancel (cancel_token const& token);
|
||||
cancel(cancel_token const& token);
|
||||
|
||||
/** Perform breadth-first search.
|
||||
|
||||
@@ -407,7 +404,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
void
|
||||
bfs (Peer const& start, Function&& f);
|
||||
bfs(Peer const& start, Function&& f);
|
||||
|
||||
/** Run the network for up to one message.
|
||||
|
||||
@@ -448,7 +445,7 @@ public:
|
||||
*/
|
||||
template <class Function>
|
||||
bool
|
||||
step_while(Function && func);
|
||||
step_while(Function&& func);
|
||||
|
||||
/** Run the network until the specified time.
|
||||
|
||||
@@ -460,7 +457,7 @@ public:
|
||||
@return `true` if any messages remain.
|
||||
*/
|
||||
bool
|
||||
step_until (time_point const& until);
|
||||
step_until(time_point const& until);
|
||||
|
||||
/** Run the network until time has elapsed.
|
||||
|
||||
@@ -473,24 +470,21 @@ public:
|
||||
*/
|
||||
template <class Period, class Rep>
|
||||
bool
|
||||
step_for (std::chrono::duration<
|
||||
Period, Rep> const& amount);
|
||||
step_for(std::chrono::duration<Period, Rep> const& amount);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class Peer>
|
||||
BasicNetwork<Peer>::queue_type::queue_type(
|
||||
qalloc const& alloc)
|
||||
: alloc_ (alloc)
|
||||
BasicNetwork<Peer>::queue_type::queue_type(qalloc const& alloc)
|
||||
: alloc_(alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
BasicNetwork<Peer>::queue_type::~queue_type()
|
||||
{
|
||||
for(auto iter = by_when_.begin();
|
||||
iter != by_when_.end();)
|
||||
for (auto iter = by_when_.begin(); iter != by_when_.end();)
|
||||
{
|
||||
auto m = &*iter;
|
||||
++iter;
|
||||
@@ -500,27 +494,22 @@ BasicNetwork<Peer>::queue_type::~queue_type()
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
bool
|
||||
inline bool
|
||||
BasicNetwork<Peer>::queue_type::empty() const
|
||||
{
|
||||
return by_when_.empty();
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::queue_type::begin() ->
|
||||
iterator
|
||||
inline auto
|
||||
BasicNetwork<Peer>::queue_type::begin() -> iterator
|
||||
{
|
||||
return by_when_.begin();
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::queue_type::end() ->
|
||||
iterator
|
||||
inline auto
|
||||
BasicNetwork<Peer>::queue_type::end() -> iterator
|
||||
{
|
||||
return by_when_.end();
|
||||
}
|
||||
@@ -529,15 +518,14 @@ template <class Peer>
|
||||
template <class Handler>
|
||||
auto
|
||||
BasicNetwork<Peer>::queue_type::emplace(
|
||||
Peer const& from, Peer const& to, time_point when,
|
||||
Handler&& h) ->
|
||||
typename by_when_set::iterator
|
||||
Peer const& from,
|
||||
Peer const& to,
|
||||
time_point when,
|
||||
Handler&& h) -> typename by_when_set::iterator
|
||||
{
|
||||
using msg_type = msg_impl<
|
||||
std::decay_t<Handler>>;
|
||||
using msg_type = msg_impl<std::decay_t<Handler>>;
|
||||
auto const p = alloc_.alloc<msg_type>(1);
|
||||
auto& m = *new(p) msg_type(from, to,
|
||||
when, std::forward<Handler>(h));
|
||||
auto& m = *new (p) msg_type(from, to, when, std::forward<Handler>(h));
|
||||
if (to)
|
||||
by_to_[to].push_back(m);
|
||||
if (from)
|
||||
@@ -547,8 +535,7 @@ BasicNetwork<Peer>::queue_type::emplace(
|
||||
|
||||
template <class Peer>
|
||||
void
|
||||
BasicNetwork<Peer>::queue_type::erase(
|
||||
iterator iter)
|
||||
BasicNetwork<Peer>::queue_type::erase(iterator iter)
|
||||
{
|
||||
auto& m = *iter;
|
||||
if (iter->to)
|
||||
@@ -568,13 +555,11 @@ BasicNetwork<Peer>::queue_type::erase(
|
||||
|
||||
template <class Peer>
|
||||
void
|
||||
BasicNetwork<Peer>::queue_type::remove(
|
||||
Peer const& from, Peer const& to)
|
||||
BasicNetwork<Peer>::queue_type::remove(Peer const& from, Peer const& to)
|
||||
{
|
||||
{
|
||||
auto& list = by_to_[to];
|
||||
for(auto iter = list.begin();
|
||||
iter != list.end();)
|
||||
for (auto iter = list.begin(); iter != list.end();)
|
||||
{
|
||||
auto& m = *iter++;
|
||||
if (m.from == from)
|
||||
@@ -583,8 +568,7 @@ BasicNetwork<Peer>::queue_type::remove(
|
||||
}
|
||||
{
|
||||
auto& list = by_to_[from];
|
||||
for(auto iter = list.begin();
|
||||
iter != list.end();)
|
||||
for (auto iter = list.begin(); iter != list.end();)
|
||||
{
|
||||
auto& m = *iter++;
|
||||
if (m.from == to)
|
||||
@@ -603,8 +587,7 @@ private:
|
||||
Peer from_;
|
||||
|
||||
public:
|
||||
using argument_type =
|
||||
typename links_type::value_type;
|
||||
using argument_type = typename links_type::value_type;
|
||||
|
||||
class result_type
|
||||
{
|
||||
@@ -612,15 +595,14 @@ public:
|
||||
Peer to;
|
||||
bool inbound;
|
||||
|
||||
result_type (result_type const&) = default;
|
||||
result_type(result_type const&) = default;
|
||||
|
||||
result_type (BasicNetwork& net,
|
||||
Peer const& from, Peer const& to_,
|
||||
bool inbound_)
|
||||
: to(to_)
|
||||
, inbound(inbound_)
|
||||
, net_(net)
|
||||
, from_(from)
|
||||
result_type(
|
||||
BasicNetwork& net,
|
||||
Peer const& from,
|
||||
Peer const& to_,
|
||||
bool inbound_)
|
||||
: to(to_), inbound(inbound_), net_(net), from_(from)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -641,18 +623,14 @@ public:
|
||||
Peer from_;
|
||||
};
|
||||
|
||||
link_transform (BasicNetwork& net,
|
||||
Peer const& from)
|
||||
: net_(net)
|
||||
, from_(from)
|
||||
link_transform(BasicNetwork& net, Peer const& from) : net_(net), from_(from)
|
||||
{
|
||||
}
|
||||
|
||||
result_type const
|
||||
operator()(argument_type const& v) const
|
||||
{
|
||||
return result_type(net_, from_,
|
||||
v.first, v.second.inbound);
|
||||
return result_type(net_, from_, v.first, v.second.inbound);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -666,14 +644,13 @@ private:
|
||||
|
||||
public:
|
||||
cancel_token() = delete;
|
||||
cancel_token (cancel_token const&) = default;
|
||||
cancel_token& operator= (cancel_token const&) = default;
|
||||
cancel_token(cancel_token const&) = default;
|
||||
cancel_token&
|
||||
operator=(cancel_token const&) = default;
|
||||
|
||||
private:
|
||||
friend class BasicNetwork;
|
||||
cancel_token(typename
|
||||
queue_type::iterator iter)
|
||||
: iter_ (iter)
|
||||
cancel_token(typename queue_type::iterator iter) : iter_(iter)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -681,33 +658,27 @@ private:
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class Peer>
|
||||
BasicNetwork<Peer>::BasicNetwork()
|
||||
: queue_ (alloc_)
|
||||
BasicNetwork<Peer>::BasicNetwork() : queue_(alloc_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
qalloc const&
|
||||
inline qalloc const&
|
||||
BasicNetwork<Peer>::alloc() const
|
||||
{
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::clock() const ->
|
||||
clock_type&
|
||||
inline auto
|
||||
BasicNetwork<Peer>::clock() const -> clock_type&
|
||||
{
|
||||
return clock_;
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::now() const ->
|
||||
time_point
|
||||
inline auto
|
||||
BasicNetwork<Peer>::now() const -> time_point
|
||||
{
|
||||
return clock_.now();
|
||||
}
|
||||
@@ -715,17 +686,16 @@ BasicNetwork<Peer>::now() const ->
|
||||
template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::connect(
|
||||
Peer const& from, Peer const& to,
|
||||
duration const& delay)
|
||||
Peer const& from,
|
||||
Peer const& to,
|
||||
duration const& delay)
|
||||
{
|
||||
if (to == from)
|
||||
return false;
|
||||
using namespace std;
|
||||
if (! links_[from].emplace(to,
|
||||
link_type{ false, delay }).second)
|
||||
if (!links_[from].emplace(to, link_type{false, delay}).second)
|
||||
return false;
|
||||
auto const result = links_[to].emplace(
|
||||
from, link_type{ true, delay });
|
||||
auto const result = links_[to].emplace(from, link_type{true, delay});
|
||||
(void)result;
|
||||
assert(result.second);
|
||||
return true;
|
||||
@@ -733,13 +703,11 @@ BasicNetwork<Peer>::connect(
|
||||
|
||||
template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::disconnect(
|
||||
Peer const& peer1, Peer const& peer2)
|
||||
BasicNetwork<Peer>::disconnect(Peer const& peer1, Peer const& peer2)
|
||||
{
|
||||
if (links_[peer1].erase(peer2) == 0)
|
||||
return false;
|
||||
auto const n =
|
||||
links_[peer2].erase(peer1);
|
||||
auto const n = links_[peer2].erase(peer1);
|
||||
(void)n;
|
||||
assert(n);
|
||||
queue_.remove(peer1, peer2);
|
||||
@@ -747,64 +715,45 @@ BasicNetwork<Peer>::disconnect(
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::links(Peer const& from) ->
|
||||
boost::transformed_range<
|
||||
link_transform, links_type>
|
||||
inline auto
|
||||
BasicNetwork<Peer>::links(Peer const& from)
|
||||
-> boost::transformed_range<link_transform, links_type>
|
||||
{
|
||||
return boost::adaptors::transform(
|
||||
links_[from],
|
||||
link_transform{ *this, from });
|
||||
links_[from], link_transform{*this, from});
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
inline
|
||||
void
|
||||
BasicNetwork<Peer>::send(
|
||||
Peer const& from, Peer const& to,
|
||||
Function&& f)
|
||||
inline void
|
||||
BasicNetwork<Peer>::send(Peer const& from, Peer const& to, Function&& f)
|
||||
{
|
||||
using namespace std;
|
||||
auto const iter =
|
||||
links_[from].find(to);
|
||||
queue_.emplace(from, to,
|
||||
clock_.now() + iter->second.delay,
|
||||
forward<Function>(f));
|
||||
auto const iter = links_[from].find(to);
|
||||
queue_.emplace(
|
||||
from, to, clock_.now() + iter->second.delay, forward<Function>(f));
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::timer(
|
||||
time_point const& when, Function&& f) ->
|
||||
cancel_token
|
||||
inline auto
|
||||
BasicNetwork<Peer>::timer(time_point const& when, Function&& f) -> cancel_token
|
||||
{
|
||||
using namespace std;
|
||||
return queue_.emplace(
|
||||
nullptr, nullptr, when,
|
||||
forward<Function>(f));
|
||||
return queue_.emplace(nullptr, nullptr, when, forward<Function>(f));
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
inline
|
||||
auto
|
||||
BasicNetwork<Peer>::timer(
|
||||
duration const& delay, Function&& f) ->
|
||||
cancel_token
|
||||
inline auto
|
||||
BasicNetwork<Peer>::timer(duration const& delay, Function&& f) -> cancel_token
|
||||
{
|
||||
return timer(clock_.now() + delay,
|
||||
std::forward<Function>(f));
|
||||
return timer(clock_.now() + delay, std::forward<Function>(f));
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
inline
|
||||
void
|
||||
BasicNetwork<Peer>::cancel(
|
||||
cancel_token const& token)
|
||||
inline void
|
||||
BasicNetwork<Peer>::cancel(cancel_token const& token)
|
||||
{
|
||||
queue_.erase(token.iter_);
|
||||
}
|
||||
@@ -826,10 +775,10 @@ template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::step()
|
||||
{
|
||||
if (! step_one())
|
||||
if (!step_one())
|
||||
return false;
|
||||
for(;;)
|
||||
if (! step_one())
|
||||
for (;;)
|
||||
if (!step_one())
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
@@ -837,7 +786,7 @@ BasicNetwork<Peer>::step()
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
bool
|
||||
BasicNetwork<Peer>::step_while(Function && f)
|
||||
BasicNetwork<Peer>::step_while(Function&& f)
|
||||
{
|
||||
bool ran = false;
|
||||
while (f() && step_one())
|
||||
@@ -847,17 +796,16 @@ BasicNetwork<Peer>::step_while(Function && f)
|
||||
|
||||
template <class Peer>
|
||||
bool
|
||||
BasicNetwork<Peer>::step_until(
|
||||
time_point const& until)
|
||||
BasicNetwork<Peer>::step_until(time_point const& until)
|
||||
{
|
||||
// VFALCO This routine needs optimizing
|
||||
if(queue_.empty())
|
||||
if (queue_.empty())
|
||||
{
|
||||
clock_.set(until);
|
||||
return false;
|
||||
}
|
||||
auto iter = queue_.begin();
|
||||
if(iter->when > until)
|
||||
if (iter->when > until)
|
||||
{
|
||||
clock_.set(until);
|
||||
return true;
|
||||
@@ -866,19 +814,15 @@ BasicNetwork<Peer>::step_until(
|
||||
{
|
||||
step_one();
|
||||
iter = queue_.begin();
|
||||
}
|
||||
while(iter != queue_.end() &&
|
||||
iter->when <= until);
|
||||
} while (iter != queue_.end() && iter->when <= until);
|
||||
clock_.set(until);
|
||||
return iter != queue_.end();
|
||||
}
|
||||
|
||||
template <class Peer>
|
||||
template <class Period, class Rep>
|
||||
inline
|
||||
bool
|
||||
BasicNetwork<Peer>::step_for(
|
||||
std::chrono::duration<Period, Rep> const& amount)
|
||||
inline bool
|
||||
BasicNetwork<Peer>::step_for(std::chrono::duration<Period, Rep> const& amount)
|
||||
{
|
||||
return step_until(now() + amount);
|
||||
}
|
||||
@@ -886,19 +830,18 @@ BasicNetwork<Peer>::step_for(
|
||||
template <class Peer>
|
||||
template <class Function>
|
||||
void
|
||||
BasicNetwork<Peer>::bfs(
|
||||
Peer const& start, Function&& f)
|
||||
BasicNetwork<Peer>::bfs(Peer const& start, Function&& f)
|
||||
{
|
||||
std::deque<std::pair<Peer, std::size_t>> q;
|
||||
std::unordered_set<Peer> seen;
|
||||
q.emplace_back(start, 0);
|
||||
seen.insert(start);
|
||||
while(! q.empty())
|
||||
while (!q.empty())
|
||||
{
|
||||
auto v = q.front();
|
||||
q.pop_front();
|
||||
f(v.second, v.first);
|
||||
for(auto const& link : links_[v.first])
|
||||
for (auto const& link : links_[v.first])
|
||||
{
|
||||
auto const& w = link.first;
|
||||
if (seen.count(w) == 0)
|
||||
@@ -910,8 +853,8 @@ BasicNetwork<Peer>::bfs(
|
||||
}
|
||||
}
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,15 +18,14 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <test/csf/BasicNetwork.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <set>
|
||||
#include <test/csf/BasicNetwork.h>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
|
||||
class BasicNetwork_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
@@ -35,28 +34,25 @@ public:
|
||||
int id;
|
||||
std::set<int> set;
|
||||
|
||||
Peer (Peer const&) = default;
|
||||
Peer (Peer&&) = default;
|
||||
Peer(Peer const&) = default;
|
||||
Peer(Peer&&) = default;
|
||||
|
||||
explicit Peer(int id_)
|
||||
: id(id_)
|
||||
explicit Peer(int id_) : id(id_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
void start(Net& net)
|
||||
void
|
||||
start(Net& net)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
auto t = net.timer(1s,
|
||||
[&]{ set.insert(0); });
|
||||
auto t = net.timer(1s, [&] { set.insert(0); });
|
||||
if (id == 0)
|
||||
{
|
||||
for(auto const& link : net.links(this))
|
||||
net.send(this, link.to,
|
||||
[&, to = link.to]
|
||||
{
|
||||
to->receive(net, this, 1);
|
||||
});
|
||||
for (auto const& link : net.links(this))
|
||||
net.send(this, link.to, [&, to = link.to ] {
|
||||
to->receive(net, this, 1);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -65,23 +61,23 @@ public:
|
||||
}
|
||||
|
||||
template <class Net>
|
||||
void receive(Net& net, Peer* from, int m)
|
||||
void
|
||||
receive(Net& net, Peer* from, int m)
|
||||
{
|
||||
set.insert(m);
|
||||
++m;
|
||||
if (m < 5)
|
||||
{
|
||||
for(auto const& link : net.links(this))
|
||||
net.send(this, link.to,
|
||||
[&, mm = m, to = link.to]
|
||||
{
|
||||
to->receive(net, this, mm);
|
||||
});
|
||||
for (auto const& link : net.links(this))
|
||||
net.send(this, link.to, [&, mm = m, to = link.to ] {
|
||||
to->receive(net, this, mm);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void run() override
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
std::vector<Peer> pv;
|
||||
@@ -89,45 +85,40 @@ public:
|
||||
pv.emplace_back(1);
|
||||
pv.emplace_back(2);
|
||||
csf::BasicNetwork<Peer*> net;
|
||||
BEAST_EXPECT(! net.connect(&pv[0], &pv[0]));
|
||||
BEAST_EXPECT(!net.connect(&pv[0], &pv[0]));
|
||||
BEAST_EXPECT(net.connect(&pv[0], &pv[1], 1s));
|
||||
BEAST_EXPECT(net.connect(&pv[1], &pv[2], 1s));
|
||||
BEAST_EXPECT(! net.connect(&pv[0], &pv[1]));
|
||||
BEAST_EXPECT(!net.connect(&pv[0], &pv[1]));
|
||||
std::size_t diameter = 0;
|
||||
net.bfs(&pv[0],
|
||||
[&](auto d, Peer*)
|
||||
{ diameter = std::max(d, diameter); });
|
||||
net.bfs(
|
||||
&pv[0], [&](auto d, Peer*) { diameter = std::max(d, diameter); });
|
||||
BEAST_EXPECT(diameter == 2);
|
||||
for(auto& peer : pv)
|
||||
for (auto& peer : pv)
|
||||
peer.start(net);
|
||||
BEAST_EXPECT(net.step_for(0s));
|
||||
BEAST_EXPECT(net.step_for(1s));
|
||||
BEAST_EXPECT(net.step());
|
||||
BEAST_EXPECT(! net.step());
|
||||
BEAST_EXPECT(! net.step_for(1s));
|
||||
net.send(&pv[0], &pv[1], []{});
|
||||
net.send(&pv[1], &pv[0], []{});
|
||||
BEAST_EXPECT(!net.step());
|
||||
BEAST_EXPECT(!net.step_for(1s));
|
||||
net.send(&pv[0], &pv[1], [] {});
|
||||
net.send(&pv[1], &pv[0], [] {});
|
||||
BEAST_EXPECT(net.disconnect(&pv[0], &pv[1]));
|
||||
BEAST_EXPECT(! net.disconnect(&pv[0], &pv[1]));
|
||||
for(;;)
|
||||
BEAST_EXPECT(!net.disconnect(&pv[0], &pv[1]));
|
||||
for (;;)
|
||||
{
|
||||
auto const links = net.links(&pv[1]);
|
||||
if(links.empty())
|
||||
if (links.empty())
|
||||
break;
|
||||
BEAST_EXPECT(links[0].disconnect());
|
||||
}
|
||||
BEAST_EXPECT(pv[0].set ==
|
||||
std::set<int>({0, 2, 4}));
|
||||
BEAST_EXPECT(pv[1].set ==
|
||||
std::set<int>({1, 3}));
|
||||
BEAST_EXPECT(pv[2].set ==
|
||||
std::set<int>({2, 4}));
|
||||
net.timer(0s, []{});
|
||||
BEAST_EXPECT(pv[0].set == std::set<int>({0, 2, 4}));
|
||||
BEAST_EXPECT(pv[1].set == std::set<int>({1, 3}));
|
||||
BEAST_EXPECT(pv[2].set == std::set<int>({2, 4}));
|
||||
net.timer(0s, [] {});
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(BasicNetwork, test, ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
@@ -46,32 +46,32 @@ namespace csf {
|
||||
|
||||
class Ledger
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
struct ID
|
||||
{
|
||||
std::uint32_t seq = 0;
|
||||
TxSetType txs = TxSetType{};
|
||||
|
||||
bool operator==(ID const & o) const
|
||||
bool
|
||||
operator==(ID const& o) const
|
||||
{
|
||||
return seq == o.seq && txs == o.txs;
|
||||
}
|
||||
|
||||
bool operator!=(ID const & o) const
|
||||
bool
|
||||
operator!=(ID const& o) const
|
||||
{
|
||||
return !(*this == o);
|
||||
}
|
||||
|
||||
bool operator<(ID const & o) const
|
||||
bool
|
||||
operator<(ID const& o) const
|
||||
{
|
||||
return std::tie(seq, txs) < std::tie(o.seq, o.txs);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
auto const &
|
||||
auto const&
|
||||
id() const
|
||||
{
|
||||
return id_;
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
return parentCloseTime_;
|
||||
}
|
||||
|
||||
auto const &
|
||||
auto const&
|
||||
parentID() const
|
||||
{
|
||||
return parentID_;
|
||||
@@ -127,30 +127,28 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//! Apply the given transactions to this ledger
|
||||
Ledger
|
||||
close(TxSetType const & txs,
|
||||
close(
|
||||
TxSetType const& txs,
|
||||
NetClock::duration closeTimeResolution,
|
||||
NetClock::time_point const & consensusCloseTime,
|
||||
NetClock::time_point const& consensusCloseTime,
|
||||
bool closeTimeAgree) const
|
||||
{
|
||||
Ledger res{ *this };
|
||||
Ledger res{*this};
|
||||
res.id_.txs.insert(txs.begin(), txs.end());
|
||||
res.id_ .seq= seq() + 1;
|
||||
res.id_.seq = seq() + 1;
|
||||
res.closeTimeResolution_ = closeTimeResolution;
|
||||
res.actualCloseTime_ = consensusCloseTime;
|
||||
res.closeTime_ = effectiveCloseTime(consensusCloseTime,
|
||||
closeTimeResolution, parentCloseTime_);
|
||||
res.closeTime_ = effCloseTime(
|
||||
consensusCloseTime, closeTimeResolution, parentCloseTime_);
|
||||
res.closeTimeAgree_ = closeTimeAgree;
|
||||
res.parentCloseTime_ = closeTime();
|
||||
res.parentID_ = id();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
//! Unique identifier of ledger is combination of sequence number and id
|
||||
ID id_;
|
||||
|
||||
@@ -171,27 +169,24 @@ private:
|
||||
|
||||
//! Close time unadjusted by closeTimeResolution
|
||||
NetClock::time_point actualCloseTime_;
|
||||
|
||||
};
|
||||
|
||||
inline
|
||||
std::ostream &
|
||||
operator<<(std::ostream & o, Ledger::ID const & id)
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& o, Ledger::ID const& id)
|
||||
{
|
||||
return o << id.seq << "," << id.txs;
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
to_string(Ledger::ID const & id)
|
||||
inline std::string
|
||||
to_string(Ledger::ID const& id)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << id;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -22,15 +22,14 @@
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <test/csf/Tx.h>
|
||||
#include <test/csf/Ledger.h>
|
||||
#include <test/csf/Tx.h>
|
||||
#include <test/csf/UNL.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace csf {
|
||||
|
||||
|
||||
/** Store validations reached by peers */
|
||||
struct Validation
|
||||
{
|
||||
@@ -45,15 +44,17 @@ class Validations
|
||||
{
|
||||
//< Ledgers seen by peers, saved in order received (which should be order
|
||||
//< created)
|
||||
bc::flat_map<Ledger::ID, bc::flat_set<PeerID>> nodesFromLedger;
|
||||
bc::flat_map<Ledger::ID, bc::flat_set<PeerID>> nodesFromPrevLedger;
|
||||
bc::flat_map<Ledger::ID, bc::flat_map<Ledger::ID, std::size_t>> childLedgers;
|
||||
bc::flat_map<Ledger::ID, bc::flat_set<PeerID>> nodesFromLedger;
|
||||
bc::flat_map<Ledger::ID, bc::flat_set<PeerID>> nodesFromPrevLedger;
|
||||
bc::flat_map<Ledger::ID, bc::flat_map<Ledger::ID, std::size_t>>
|
||||
childLedgers;
|
||||
|
||||
public:
|
||||
void
|
||||
update(Validation const & v)
|
||||
update(Validation const& v)
|
||||
{
|
||||
nodesFromLedger[v.ledger].insert(v.id);
|
||||
if(v.ledger.seq > 0)
|
||||
if (v.ledger.seq > 0)
|
||||
{
|
||||
nodesFromPrevLedger[v.prevLedger].insert(v.id);
|
||||
childLedgers[v.prevLedger][v.ledger]++;
|
||||
@@ -62,10 +63,10 @@ public:
|
||||
|
||||
//< The number of peers who have validated this ledger
|
||||
std::size_t
|
||||
proposersValidated(Ledger::ID const & prevLedger) const
|
||||
proposersValidated(Ledger::ID const& prevLedger) const
|
||||
{
|
||||
auto it = nodesFromLedger.find(prevLedger);
|
||||
if(it != nodesFromLedger.end())
|
||||
if (it != nodesFromLedger.end())
|
||||
return it->second.size();
|
||||
return 0;
|
||||
}
|
||||
@@ -75,35 +76,33 @@ public:
|
||||
as an ancestor.
|
||||
*/
|
||||
std::size_t
|
||||
proposersFinished(Ledger::ID const & prevLedger) const
|
||||
proposersFinished(Ledger::ID const& prevLedger) const
|
||||
{
|
||||
auto it = nodesFromPrevLedger.find(prevLedger);
|
||||
if(it != nodesFromPrevLedger.end())
|
||||
if (it != nodesFromPrevLedger.end())
|
||||
return it->second.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Returns the ledger starting from prevLedger with the most validations.
|
||||
*/
|
||||
*/
|
||||
Ledger::ID
|
||||
getBestLCL(Ledger::ID const & currLedger,
|
||||
Ledger::ID const & prevLedger) const
|
||||
getBestLCL(Ledger::ID const& currLedger, Ledger::ID const& prevLedger) const
|
||||
{
|
||||
auto it = childLedgers.find(prevLedger);
|
||||
if (it != childLedgers.end() &&
|
||||
! it->second.empty())
|
||||
if (it != childLedgers.end() && !it->second.empty())
|
||||
{
|
||||
std::size_t bestCount = 0;
|
||||
Ledger::ID bestLedger;
|
||||
|
||||
for (auto const & b : it->second)
|
||||
for (auto const& b : it->second)
|
||||
{
|
||||
auto currCount = b.second;
|
||||
if(currLedger == b.first)
|
||||
if (currLedger == b.first)
|
||||
currCount++;
|
||||
if(currCount > bestCount)
|
||||
if (currCount > bestCount)
|
||||
bestLedger = b.first;
|
||||
if(currCount == bestCount && currLedger == b.first)
|
||||
if (currCount == bestCount && currLedger == b.first)
|
||||
bestLedger = b.first;
|
||||
}
|
||||
return bestLedger;
|
||||
@@ -122,11 +121,8 @@ struct Traits
|
||||
using Ledger_t = Ledger;
|
||||
using NodeID_t = PeerID;
|
||||
using TxSet_t = TxSet;
|
||||
using MissingTxException_t = MissingTx;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Represents a single node participating in the consensus process.
|
||||
It implements the Callbacks required by Consensus.
|
||||
*/
|
||||
@@ -144,7 +140,7 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
Ledger lastClosedLedger;
|
||||
|
||||
//! Handle to network for sending messages
|
||||
BasicNetwork<Peer*> & net;
|
||||
BasicNetwork<Peer*>& net;
|
||||
|
||||
//! UNL of trusted peers
|
||||
UNL unl;
|
||||
@@ -173,12 +169,12 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
//! Delay in acquiring missing ledger from the network
|
||||
std::chrono::milliseconds missingLedgerDelay{0};
|
||||
|
||||
bool validating = true;
|
||||
bool proposing = true;
|
||||
bool validating_ = true;
|
||||
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)
|
||||
: Consensus<Peer, Traits>(n.clock(), beast::Journal{})
|
||||
, id{i}
|
||||
, net{n}
|
||||
, unl(u)
|
||||
@@ -186,19 +182,8 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
ledgers[lastClosedLedger.id()] = lastClosedLedger;
|
||||
}
|
||||
|
||||
|
||||
// @return whether we are proposing,validating
|
||||
// TODO: Bit akward that this is in callbacks, would be nice to extract
|
||||
std::pair<bool, bool>
|
||||
getMode()
|
||||
{
|
||||
// in RCL this hits NetworkOps to decide whether we are proposing
|
||||
// validating
|
||||
return{ proposing, validating };
|
||||
}
|
||||
|
||||
Ledger const *
|
||||
acquireLedger(Ledger::ID const & ledgerHash)
|
||||
Ledger const*
|
||||
acquireLedger(Ledger::ID const& ledgerHash)
|
||||
{
|
||||
auto it = ledgers.find(ledgerHash);
|
||||
if (it != ledgers.end())
|
||||
@@ -208,16 +193,16 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
|
||||
for (auto const& link : net.links(this))
|
||||
{
|
||||
auto const & p = *link.to;
|
||||
auto const& p = *link.to;
|
||||
auto it = p.ledgers.find(ledgerHash);
|
||||
if (it != p.ledgers.end())
|
||||
{
|
||||
schedule(missingLedgerDelay,
|
||||
[this, ledgerHash, ledger = it->second]()
|
||||
{
|
||||
schedule(
|
||||
missingLedgerDelay,
|
||||
[ this, ledgerHash, ledger = it->second ]() {
|
||||
ledgers.emplace(ledgerHash, ledger);
|
||||
});
|
||||
if(missingLedgerDelay == 0ms)
|
||||
if (missingLedgerDelay == 0ms)
|
||||
return &ledgers[ledgerHash];
|
||||
break;
|
||||
}
|
||||
@@ -225,23 +210,22 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto const &
|
||||
proposals(Ledger::ID const & ledgerHash)
|
||||
auto const&
|
||||
proposals(Ledger::ID const& ledgerHash)
|
||||
{
|
||||
return peerPositions_[ledgerHash];
|
||||
}
|
||||
|
||||
TxSet const *
|
||||
acquireTxSet(TxSet::ID const & setId)
|
||||
TxSet const*
|
||||
acquireTxSet(TxSet::ID const& setId)
|
||||
{
|
||||
auto it = txSets.find(setId);
|
||||
if(it != txSets.end())
|
||||
if (it != txSets.end())
|
||||
return &(it->second);
|
||||
// TODO Get from network/oracle instead!
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
hasOpenTransactions() const
|
||||
{
|
||||
@@ -249,114 +233,68 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
}
|
||||
|
||||
std::size_t
|
||||
proposersValidated(Ledger::ID const & prevLedger)
|
||||
proposersValidated(Ledger::ID const& prevLedger)
|
||||
{
|
||||
return peerValidations.proposersValidated(prevLedger);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
proposersFinished(Ledger::ID const & prevLedger)
|
||||
proposersFinished(Ledger::ID const& prevLedger)
|
||||
{
|
||||
return peerValidations.proposersFinished(prevLedger);
|
||||
}
|
||||
|
||||
void
|
||||
onStartRound(Ledger const &) {}
|
||||
|
||||
void
|
||||
onClose(Ledger const &, bool ) {}
|
||||
|
||||
// don't really offload
|
||||
void
|
||||
dispatchAccept(TxSet const & f)
|
||||
Result
|
||||
onClose(Ledger const& prevLedger, NetClock::time_point closeTime, Mode mode)
|
||||
{
|
||||
Base::accept(f);
|
||||
TxSet res{openTxs};
|
||||
|
||||
return Result{TxSet{openTxs},
|
||||
Proposal{prevLedger.id(),
|
||||
Proposal::seqJoin,
|
||||
res.id(),
|
||||
closeTime,
|
||||
now(),
|
||||
id}};
|
||||
}
|
||||
|
||||
void
|
||||
share(TxSet const &s)
|
||||
onForceAccept(
|
||||
Result const& result,
|
||||
Ledger const& prevLedger,
|
||||
NetClock::duration const& closeResolution,
|
||||
CloseTimes const& rawCloseTimes,
|
||||
Mode const& mode)
|
||||
{
|
||||
relay(s);
|
||||
}
|
||||
|
||||
Ledger::ID
|
||||
getLCL(Ledger::ID const & currLedger,
|
||||
Ledger::ID const & priorLedger,
|
||||
bool haveCorrectLCL)
|
||||
{
|
||||
// TODO: Use generic validation code
|
||||
if(currLedger.seq > 0 && priorLedger.seq > 0)
|
||||
return peerValidations.getBestLCL(currLedger, priorLedger);
|
||||
return currLedger;
|
||||
onAccept(result, prevLedger, closeResolution, rawCloseTimes, mode);
|
||||
}
|
||||
|
||||
void
|
||||
propose(Proposal const & pos)
|
||||
onAccept(
|
||||
Result const& result,
|
||||
Ledger const& prevLedger,
|
||||
NetClock::duration const& closeResolution,
|
||||
CloseTimes const& rawCloseTimes,
|
||||
Mode const& mode)
|
||||
{
|
||||
if(proposing)
|
||||
relay(pos);
|
||||
}
|
||||
|
||||
void
|
||||
relay(DisputedTx<Tx, PeerID> const & dispute)
|
||||
{
|
||||
relay(dispute.tx());
|
||||
}
|
||||
|
||||
std::pair <TxSet, Proposal>
|
||||
makeInitialPosition(
|
||||
Ledger const & prevLedger,
|
||||
bool isProposing,
|
||||
bool isCorrectLCL,
|
||||
NetClock::time_point closeTime,
|
||||
NetClock::time_point now)
|
||||
{
|
||||
TxSet res{ openTxs };
|
||||
|
||||
return { res,
|
||||
Proposal{prevLedger.id(), Proposal::seqJoin, res.id(), closeTime, now, id} };
|
||||
}
|
||||
|
||||
// Process the accepted transaction set, generating the newly closed ledger
|
||||
// and clearing out the openTxs that were included.
|
||||
// TODO: Kinda nasty it takes so many arguments . . . sign of bad coupling
|
||||
bool
|
||||
accept(TxSet const& set,
|
||||
NetClock::time_point consensusCloseTime,
|
||||
bool proposing_,
|
||||
bool validating_,
|
||||
bool haveCorrectLCL_,
|
||||
bool consensusFail_,
|
||||
Ledger::ID const & prevLedgerHash_,
|
||||
Ledger const & previousLedger_,
|
||||
NetClock::duration closeResolution_,
|
||||
NetClock::time_point const & now,
|
||||
std::chrono::milliseconds const & roundTime_,
|
||||
hash_map<Tx::ID, DisputedTx <Tx, PeerID>> const & disputes_,
|
||||
std::map <NetClock::time_point, int> closeTimes_,
|
||||
NetClock::time_point const & closeTime)
|
||||
{
|
||||
auto newLedger = previousLedger_.close(set.txs_, closeResolution_,
|
||||
closeTime, consensusCloseTime != NetClock::time_point{});
|
||||
auto newLedger = prevLedger.close(
|
||||
result.set.txs_,
|
||||
closeResolution,
|
||||
rawCloseTimes.self,
|
||||
result.position.closeTime() != NetClock::time_point{});
|
||||
ledgers[newLedger.id()] = newLedger;
|
||||
|
||||
lastClosedLedger = newLedger;
|
||||
|
||||
auto it = std::remove_if(openTxs.begin(), openTxs.end(),
|
||||
[&](Tx const & tx)
|
||||
{
|
||||
return set.exists(tx.id());
|
||||
auto it =
|
||||
std::remove_if(openTxs.begin(), openTxs.end(), [&](Tx const& tx) {
|
||||
return result.set.exists(tx.id());
|
||||
});
|
||||
openTxs.erase(it, openTxs.end());
|
||||
|
||||
if(validating)
|
||||
if (validating_)
|
||||
relay(Validation{id, newLedger.id(), newLedger.parentID()});
|
||||
return validating_;
|
||||
}
|
||||
|
||||
void
|
||||
endConsensus(bool correct)
|
||||
{
|
||||
// kick off the next round...
|
||||
// in the actual implementation, this passes back through
|
||||
// network ops
|
||||
@@ -364,41 +302,58 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
// startRound sets the LCL state, so we need to call it once after
|
||||
// the last requested round completes
|
||||
// TODO: reconsider this and instead just save LCL generated here?
|
||||
if(completedLedgers <= targetLedgers)
|
||||
if (completedLedgers <= targetLedgers)
|
||||
{
|
||||
startRound(now(), lastClosedLedger.id(),
|
||||
lastClosedLedger);
|
||||
startRound(
|
||||
now(), lastClosedLedger.id(), lastClosedLedger, proposing_);
|
||||
}
|
||||
}
|
||||
|
||||
Ledger::ID
|
||||
getPrevLedger(Ledger::ID const& ledgerID, Ledger const& ledger, Mode mode)
|
||||
{
|
||||
// TODO: Use generic validation code
|
||||
if (mode != Mode::wrongLedger && ledgerID.seq > 0 &&
|
||||
ledger.id().seq > 0)
|
||||
return peerValidations.getBestLCL(ledgerID, ledger.parentID());
|
||||
return ledgerID;
|
||||
}
|
||||
|
||||
void
|
||||
propose(Proposal const& pos)
|
||||
{
|
||||
if (proposing_)
|
||||
relay(pos);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// non-callback helpers
|
||||
void
|
||||
receive(Proposal const & p)
|
||||
receive(Proposal const& p)
|
||||
{
|
||||
if(unl.find(p.nodeID()) == unl.end())
|
||||
if (unl.find(p.nodeID()) == unl.end())
|
||||
return;
|
||||
|
||||
// TODO: Be sure this is a new proposal!!!!!
|
||||
auto & dest = peerPositions_[p.prevLedger()];
|
||||
if(std::find(dest.begin(), dest.end(), p) != dest.end())
|
||||
auto& dest = peerPositions_[p.prevLedger()];
|
||||
if (std::find(dest.begin(), dest.end(), p) != dest.end())
|
||||
return;
|
||||
|
||||
dest.push_back(p);
|
||||
peerProposal(now(), p);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
receive(TxSet const & txs)
|
||||
receive(TxSet const& txs)
|
||||
{
|
||||
// save and map complete?
|
||||
auto it = txSets.insert(std::make_pair(txs.id(), txs));
|
||||
if(it.second)
|
||||
if (it.second)
|
||||
gotTxSet(now(), txs);
|
||||
}
|
||||
|
||||
void
|
||||
receive(Tx const & tx)
|
||||
receive(Tx const& tx)
|
||||
{
|
||||
if (openTxs.find(tx.id()) == openTxs.end())
|
||||
{
|
||||
@@ -409,33 +364,26 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
}
|
||||
|
||||
void
|
||||
receive(Validation const & v)
|
||||
receive(Validation const& v)
|
||||
{
|
||||
if(unl.find(v.id) != unl.end())
|
||||
if (unl.find(v.id) != unl.end())
|
||||
{
|
||||
schedule(validationDelay,
|
||||
[&, v]()
|
||||
{
|
||||
peerValidations.update(v);
|
||||
});
|
||||
schedule(validationDelay, [&, v]() { peerValidations.update(v); });
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
relay(T const & t)
|
||||
relay(T const& t)
|
||||
{
|
||||
for(auto const& link : net.links(this))
|
||||
net.send(this, link.to,
|
||||
[msg = t, to = link.to]
|
||||
{
|
||||
to->receive(msg);
|
||||
});
|
||||
for (auto const& link : net.links(this))
|
||||
net.send(
|
||||
this, link.to, [ msg = t, to = link.to ] { to->receive(msg); });
|
||||
}
|
||||
|
||||
// Receive and relay locally submitted transaction
|
||||
void
|
||||
submit(Tx const & tx)
|
||||
submit(Tx const& tx)
|
||||
{
|
||||
receive(tx);
|
||||
relay(tx);
|
||||
@@ -446,7 +394,7 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
{
|
||||
Base::timerEntry(now());
|
||||
// only reschedule if not completed
|
||||
if(completedLedgers < targetLedgers)
|
||||
if (completedLedgers < targetLedgers)
|
||||
net.timer(LEDGER_GRANULARITY, [&]() { timerEntry(); });
|
||||
}
|
||||
void
|
||||
@@ -456,10 +404,9 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
// 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()
|
||||
auto bestLCL = peerValidations.getBestLCL(lastClosedLedger.id(),
|
||||
lastClosedLedger.parentID());
|
||||
startRound(now(), bestLCL,
|
||||
lastClosedLedger);
|
||||
auto bestLCL = peerValidations.getBestLCL(
|
||||
lastClosedLedger.id(), lastClosedLedger.parentID());
|
||||
startRound(now(), bestLCL, lastClosedLedger, proposing_);
|
||||
}
|
||||
|
||||
NetClock::time_point
|
||||
@@ -470,23 +417,24 @@ struct Peer : public Consensus<Peer, Traits>
|
||||
// any subtractions of two NetClock::time_point in the consensu
|
||||
// code are positive. (e.g. PROPOSE_FRESHNESS)
|
||||
using namespace std::chrono;
|
||||
return NetClock::time_point(duration_cast<NetClock::duration>
|
||||
(net.now().time_since_epoch()+ 86400s + clockSkew));
|
||||
return NetClock::time_point(duration_cast<NetClock::duration>(
|
||||
net.now().time_since_epoch() + 86400s + clockSkew));
|
||||
}
|
||||
|
||||
// Schedule the provided callback in `when` duration, but if
|
||||
// `when` is 0, call immediately
|
||||
template <class T>
|
||||
void schedule(std::chrono::nanoseconds when, T && what)
|
||||
void
|
||||
schedule(std::chrono::nanoseconds when, T&& what)
|
||||
{
|
||||
if(when == 0ns)
|
||||
if (when == 0ns)
|
||||
what();
|
||||
else
|
||||
net.timer(when, std::forward<T>(what));
|
||||
}
|
||||
};
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
#endif
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#ifndef RIPPLE_TEST_CSF_SIM_H_INCLUDED
|
||||
#define RIPPLE_TEST_CSF_SIM_H_INCLUDED
|
||||
|
||||
#include <test/csf/UNL.h>
|
||||
#include <test/csf/BasicNetwork.h>
|
||||
#include <test/csf/UNL.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -50,19 +50,19 @@ public:
|
||||
|
||||
*/
|
||||
template <class Topology>
|
||||
Sim(TrustGraph const & g, Topology const & top)
|
||||
Sim(TrustGraph const& g, Topology const& top)
|
||||
{
|
||||
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));
|
||||
|
||||
for(int i = 0; i < peers.size(); ++i)
|
||||
for (int i = 0; i < peers.size(); ++i)
|
||||
{
|
||||
for(int j = 0; j < peers.size(); ++j)
|
||||
for (int j = 0; j < peers.size(); ++j)
|
||||
{
|
||||
if( i != j)
|
||||
if (i != j)
|
||||
{
|
||||
auto d = top(i,j);
|
||||
auto d = top(i, j);
|
||||
if (d)
|
||||
{
|
||||
net.connect(&peers[i], &peers[j], *d);
|
||||
@@ -81,10 +81,10 @@ public:
|
||||
void
|
||||
run(int ledgers)
|
||||
{
|
||||
for (auto & p : peers)
|
||||
for (auto& p : peers)
|
||||
{
|
||||
if(p.completedLedgers == 0)
|
||||
p.relay(Validation{p.id, p.LCL(), p.LCL()});
|
||||
if (p.completedLedgers == 0)
|
||||
p.relay(Validation{p.id, p.prevLedgerID(), p.prevLedgerID()});
|
||||
p.targetLedgers = p.completedLedgers + ledgers;
|
||||
p.start();
|
||||
}
|
||||
@@ -93,12 +93,10 @@ public:
|
||||
|
||||
std::vector<Peer> peers;
|
||||
BasicNetwork<Peer*> net;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
#include <ripple/beast/hash/hash_append.h>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -35,7 +35,9 @@ class Tx
|
||||
public:
|
||||
using ID = std::uint32_t;
|
||||
|
||||
Tx(ID i) : id_{ i } {}
|
||||
Tx(ID i) : id_{i}
|
||||
{
|
||||
}
|
||||
|
||||
ID
|
||||
id() const
|
||||
@@ -44,21 +46,19 @@ public:
|
||||
}
|
||||
|
||||
bool
|
||||
operator<(Tx const & o) const
|
||||
operator<(Tx const& o) const
|
||||
{
|
||||
return id_ < o.id_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(Tx const & o) const
|
||||
operator==(Tx const& o) const
|
||||
{
|
||||
return id_ == o.id_;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
ID id_;
|
||||
|
||||
};
|
||||
|
||||
//!-------------------------------------------------------------------------
|
||||
@@ -74,37 +74,39 @@ public:
|
||||
using MutableTxSet = TxSet;
|
||||
|
||||
TxSet() = default;
|
||||
TxSet(TxSetType const & s) : txs_{ s } {}
|
||||
TxSet(TxSetType const& s) : txs_{s}
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
insert(Tx const & t)
|
||||
insert(Tx const& t)
|
||||
{
|
||||
return txs_.insert(t).second;
|
||||
}
|
||||
|
||||
bool
|
||||
erase(Tx::ID const & txId)
|
||||
erase(Tx::ID const& txId)
|
||||
{
|
||||
return txs_.erase(Tx{ txId }) > 0;
|
||||
return txs_.erase(Tx{txId}) > 0;
|
||||
}
|
||||
|
||||
bool
|
||||
exists(Tx::ID const txId) const
|
||||
{
|
||||
auto it = txs_.find(Tx{ txId });
|
||||
auto it = txs_.find(Tx{txId});
|
||||
return it != txs_.end();
|
||||
}
|
||||
|
||||
Tx const *
|
||||
Tx const*
|
||||
find(Tx::ID const& txId) const
|
||||
{
|
||||
auto it = txs_.find(Tx{ txId });
|
||||
auto it = txs_.find(Tx{txId});
|
||||
if (it != txs_.end())
|
||||
return &(*it);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto const &
|
||||
auto const&
|
||||
id() const
|
||||
{
|
||||
return txs_;
|
||||
@@ -119,19 +121,14 @@ public:
|
||||
{
|
||||
std::map<Tx::ID, bool> res;
|
||||
|
||||
auto populate_diffs = [&res](auto const & a, auto const & b, bool s)
|
||||
{
|
||||
auto populator = [&](auto const & tx)
|
||||
{
|
||||
res[tx.id()] = s;
|
||||
};
|
||||
auto populate_diffs = [&res](auto const& a, auto const& b, bool s) {
|
||||
auto populator = [&](auto const& tx) { res[tx.id()] = s; };
|
||||
std::set_difference(
|
||||
a.begin(), a.end(),
|
||||
b.begin(), b.end(),
|
||||
boost::make_function_output_iterator(
|
||||
std::ref(populator)
|
||||
)
|
||||
);
|
||||
a.begin(),
|
||||
a.end(),
|
||||
b.begin(),
|
||||
b.end(),
|
||||
boost::make_function_output_iterator(std::ref(populator)));
|
||||
};
|
||||
|
||||
populate_diffs(txs_, other.txs_, true);
|
||||
@@ -143,55 +140,35 @@ public:
|
||||
TxSetType txs_;
|
||||
};
|
||||
|
||||
|
||||
/** The RCL consensus process catches missing node SHAMap error
|
||||
in several points. This exception is meant to represent a similar
|
||||
case for the unit test.
|
||||
*/
|
||||
class MissingTx : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
MissingTx()
|
||||
: std::runtime_error("MissingTx")
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper functions for debug printing
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream & o, const Tx & t)
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& o, const Tx& t)
|
||||
{
|
||||
return o << t.id();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream & o, boost::container::flat_set<T> const & ts)
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& o, boost::container::flat_set<T> const& ts)
|
||||
{
|
||||
o << "{ ";
|
||||
bool do_comma = false;
|
||||
for (auto const & t : ts)
|
||||
for (auto const& t : ts)
|
||||
{
|
||||
if (do_comma)
|
||||
o << ", ";
|
||||
else
|
||||
do_comma = true;
|
||||
o << t;
|
||||
|
||||
|
||||
}
|
||||
o << " }";
|
||||
return o;
|
||||
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
to_string(TxSetType const & txs)
|
||||
inline std::string
|
||||
to_string(TxSetType const& txs)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << txs;
|
||||
@@ -199,23 +176,15 @@ to_string(TxSetType const & txs)
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
inline
|
||||
void
|
||||
hash_append(Hasher& h, Tx const & tx)
|
||||
inline void
|
||||
hash_append(Hasher& h, Tx const& tx)
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, tx.id());
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream & o, MissingTx const &m)
|
||||
{
|
||||
return o << m.what();
|
||||
}
|
||||
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -42,7 +42,7 @@ namespace csf {
|
||||
*/
|
||||
template <class T, class G>
|
||||
std::vector<T>
|
||||
random_weighted_shuffle(std::vector<T> v, std::vector<double> w, G & g)
|
||||
random_weighted_shuffle(std::vector<T> v, std::vector<double> w, G& g)
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
@@ -57,7 +57,6 @@ random_weighted_shuffle(std::vector<T> v, std::vector<double> w, G & g)
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/** Power-law distribution with PDF
|
||||
|
||||
P(x) = (x/xmin)^-a
|
||||
@@ -69,25 +68,22 @@ class PowerLawDistribution
|
||||
double xmin_;
|
||||
double a_;
|
||||
double inv_;
|
||||
std::uniform_real_distribution<double> uf_{0,1};
|
||||
std::uniform_real_distribution<double> uf_{0, 1};
|
||||
|
||||
public:
|
||||
PowerLawDistribution(double xmin, double a)
|
||||
: xmin_{xmin}, a_{a}
|
||||
{
|
||||
inv_ = 1.0/(1.0 - a_);
|
||||
}
|
||||
PowerLawDistribution(double xmin, double a) : xmin_{xmin}, a_{a}
|
||||
{
|
||||
inv_ = 1.0 / (1.0 - a_);
|
||||
}
|
||||
|
||||
template <class Generator>
|
||||
inline
|
||||
double
|
||||
operator()(Generator & g)
|
||||
inline double
|
||||
operator()(Generator& g)
|
||||
{
|
||||
// use inverse transform of CDF to sample
|
||||
// CDF is P(X <= x): 1 - (x/xmin)^(1-a)
|
||||
return xmin_ * std::pow(1 - uf_(g), inv_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//< Unique identifier for each node in the network
|
||||
@@ -110,25 +106,23 @@ class TrustGraph
|
||||
std::vector<UNL> UNLs_;
|
||||
|
||||
std::vector<int> assignment_;
|
||||
public:
|
||||
|
||||
public:
|
||||
//< Constructor
|
||||
TrustGraph(std::vector<UNL> UNLs, std::vector<int> assignment)
|
||||
: UNLs_{UNLs}
|
||||
, assignment_{assignment}
|
||||
{}
|
||||
: UNLs_{UNLs}, assignment_{assignment}
|
||||
{
|
||||
}
|
||||
|
||||
//< Whether node `i` trusts node `j`
|
||||
inline
|
||||
bool
|
||||
inline bool
|
||||
trusts(PeerID i, PeerID j) const
|
||||
{
|
||||
return unl(i).find(j) != unl(i).end();
|
||||
}
|
||||
|
||||
//< Get the UNL for node `i`
|
||||
inline
|
||||
UNL const &
|
||||
inline UNL const&
|
||||
unl(PeerID i) const
|
||||
{
|
||||
return UNLs_[assignment_[i]];
|
||||
@@ -138,7 +132,6 @@ public:
|
||||
bool
|
||||
canFork(double quorum) const;
|
||||
|
||||
|
||||
auto
|
||||
numPeers() const
|
||||
{
|
||||
@@ -147,7 +140,7 @@ public:
|
||||
|
||||
//< Save grapviz dot file reprentation of the trust graph
|
||||
void
|
||||
save_dot(std::string const & fileName);
|
||||
save_dot(std::string const& fileName);
|
||||
|
||||
/** Generate a random trust graph based on random ranking of peers
|
||||
|
||||
@@ -176,28 +169,23 @@ public:
|
||||
|
||||
*/
|
||||
template <class RankPDF, class SizePDF, class Generator>
|
||||
static
|
||||
TrustGraph
|
||||
makeRandomRanked(int size,
|
||||
int numUNLs,
|
||||
RankPDF rankPDF,
|
||||
SizePDF unlSizePDF,
|
||||
Generator & g)
|
||||
static TrustGraph
|
||||
makeRandomRanked(
|
||||
int size,
|
||||
int numUNLs,
|
||||
RankPDF rankPDF,
|
||||
SizePDF unlSizePDF,
|
||||
Generator& g)
|
||||
{
|
||||
|
||||
|
||||
// 1. Generate ranks
|
||||
std::vector<double> weights(size);
|
||||
std::generate(weights.begin(), weights.end(), [&]()
|
||||
{
|
||||
return rankPDF(g);
|
||||
});
|
||||
std::generate(
|
||||
weights.begin(), weights.end(), [&]() { return rankPDF(g); });
|
||||
|
||||
// 2. Generate UNLs based on sampling without replacement according
|
||||
// to weights
|
||||
std::vector<UNL> unls(numUNLs);
|
||||
std::generate(unls.begin(), unls.end(), [&]()
|
||||
{
|
||||
std::generate(unls.begin(), unls.end(), [&]() {
|
||||
std::vector<PeerID> ids(size);
|
||||
std::iota(ids.begin(), ids.end(), 0);
|
||||
auto res = random_weighted_shuffle(ids, weights, g);
|
||||
@@ -206,12 +194,9 @@ public:
|
||||
|
||||
// 3. Assign membership
|
||||
std::vector<int> assignment(size);
|
||||
std::uniform_int_distribution<int> u(0, numUNLs-1);
|
||||
std::generate(assignment.begin(), assignment.end(),
|
||||
[&]()
|
||||
{
|
||||
return u(g);
|
||||
});
|
||||
std::uniform_int_distribution<int> u(0, numUNLs - 1);
|
||||
std::generate(
|
||||
assignment.begin(), assignment.end(), [&]() { return u(g); });
|
||||
|
||||
return TrustGraph(unls, assignment);
|
||||
}
|
||||
@@ -226,8 +211,7 @@ public:
|
||||
@param size The number of nodes in the trust graph
|
||||
@param overlap The number of nodes trusting both cliques
|
||||
*/
|
||||
static
|
||||
TrustGraph
|
||||
static TrustGraph
|
||||
makeClique(int size, int overlap);
|
||||
|
||||
/** Generate a complete (fully-connect) trust graph
|
||||
@@ -237,21 +221,17 @@ public:
|
||||
|
||||
@param size The number of nodes in the trust graph
|
||||
*/
|
||||
static
|
||||
TrustGraph
|
||||
static TrustGraph
|
||||
makeComplete(int size);
|
||||
};
|
||||
|
||||
|
||||
|
||||
//< Make the TrustGraph into a topology with delays given by DelayModel
|
||||
template <class DelayModel>
|
||||
auto
|
||||
topology(TrustGraph const & tg, DelayModel const & d)
|
||||
topology(TrustGraph const& tg, DelayModel const& d)
|
||||
{
|
||||
return [&](PeerID i, PeerID j)
|
||||
{
|
||||
return tg.trusts(i,j) ? boost::make_optional(d(i,j)) : boost::none;
|
||||
return [&](PeerID i, PeerID j) {
|
||||
return tg.trusts(i, j) ? boost::make_optional(d(i, j)) : boost::none;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -260,18 +240,19 @@ class fixed
|
||||
std::chrono::nanoseconds d_;
|
||||
|
||||
public:
|
||||
fixed(std::chrono::nanoseconds const & d) : d_{d} {}
|
||||
fixed(std::chrono::nanoseconds const& d) : d_{d}
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
std::chrono::nanoseconds
|
||||
operator()(PeerID const & i, PeerID const & j) const
|
||||
inline std::chrono::nanoseconds
|
||||
operator()(PeerID const& i, PeerID const& j) const
|
||||
{
|
||||
return d_;
|
||||
}
|
||||
};
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <test/csf/UNL.h>
|
||||
#include <boost/iterator/counting_iterator.hpp>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <test/csf/UNL.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -38,8 +38,8 @@ TrustGraph::canFork(double quorum) const
|
||||
|
||||
for (int i = 0; i < assignment_.size(); ++i)
|
||||
{
|
||||
auto const & myUNL = UNLs_[assignment_[i]];
|
||||
if(myUNL.find(i) == myUNL.end())
|
||||
auto const& myUNL = UNLs_[assignment_[i]];
|
||||
if (myUNL.find(i) == myUNL.end())
|
||||
{
|
||||
auto myUNLcopy = myUNL;
|
||||
myUNLcopy.insert(i);
|
||||
@@ -50,21 +50,20 @@ TrustGraph::canFork(double quorum) const
|
||||
// Loop over all pairs of uniqueUNLs
|
||||
for (int i = 0; i < uniqueUNLs.size(); ++i)
|
||||
{
|
||||
for (int j = (i+1); j < uniqueUNLs.size(); ++j)
|
||||
for (int j = (i + 1); j < uniqueUNLs.size(); ++j)
|
||||
{
|
||||
auto const & unlA = uniqueUNLs[i];
|
||||
auto const & unlB = uniqueUNLs[j];
|
||||
auto const& unlA = uniqueUNLs[i];
|
||||
auto const& unlB = uniqueUNLs[j];
|
||||
|
||||
double rhs = 2.0*(1.-quorum) *
|
||||
std::max(unlA.size(), unlB.size() );
|
||||
double rhs =
|
||||
2.0 * (1. - quorum) * std::max(unlA.size(), unlB.size());
|
||||
|
||||
int intersectionSize = std::count_if(unlA.begin(), unlA.end(),
|
||||
[&](PeerID id)
|
||||
{
|
||||
int intersectionSize =
|
||||
std::count_if(unlA.begin(), unlA.end(), [&](PeerID id) {
|
||||
return unlB.find(id) != unlB.end();
|
||||
});
|
||||
|
||||
if(intersectionSize < rhs)
|
||||
if (intersectionSize < rhs)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -80,56 +79,53 @@ TrustGraph::makeClique(int size, int overlap)
|
||||
// Clique A has nodes [0,endA) and Clique B has [startB,numPeers)
|
||||
// Note: Clique B will have an extra peer when numPeers - overlap
|
||||
// is odd
|
||||
int endA = (size + overlap)/2;
|
||||
int startB = (size - overlap)/2;
|
||||
int endA = (size + overlap) / 2;
|
||||
int startB = (size - overlap) / 2;
|
||||
|
||||
std::vector<UNL> unls;
|
||||
unls.emplace_back(bci(0), bci(endA));
|
||||
unls.emplace_back(bci(startB), bci(size));
|
||||
unls.emplace_back(bci(0), bci(size));
|
||||
|
||||
std::vector<int> assignment(size,0);
|
||||
std::vector<int> assignment(size, 0);
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
if(i < startB)
|
||||
if (i < startB)
|
||||
assignment[i] = 0;
|
||||
else if(i > endA)
|
||||
else if (i > endA)
|
||||
assignment[i] = 1;
|
||||
else
|
||||
assignment[i] = 2;
|
||||
}
|
||||
|
||||
|
||||
return TrustGraph(unls, assignment);
|
||||
}
|
||||
|
||||
TrustGraph
|
||||
TrustGraph::makeComplete(int size)
|
||||
{
|
||||
UNL all{ boost::counting_iterator<PeerID>( 0 ),
|
||||
boost::counting_iterator<PeerID>( size ) };
|
||||
UNL all{boost::counting_iterator<PeerID>(0),
|
||||
boost::counting_iterator<PeerID>(size)};
|
||||
|
||||
return TrustGraph(std::vector<UNL>(1,all),
|
||||
std::vector<int>(size, 0));
|
||||
return TrustGraph(std::vector<UNL>(1, all), std::vector<int>(size, 0));
|
||||
}
|
||||
|
||||
inline void TrustGraph::save_dot(std::string const & fileName)
|
||||
inline void
|
||||
TrustGraph::save_dot(std::string const& fileName)
|
||||
{
|
||||
std::ofstream out(fileName);
|
||||
out << "digraph {\n";
|
||||
for (int i = 0; i < assignment_.size(); ++i)
|
||||
{
|
||||
for (auto & j : UNLs_[assignment_[i]])
|
||||
for (auto& j : UNLs_[assignment_[i]])
|
||||
{
|
||||
out << i << " -> " << j << ";\n";
|
||||
}
|
||||
|
||||
}
|
||||
out << "}\n";
|
||||
|
||||
}
|
||||
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
} // csf
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
Reference in New Issue
Block a user