diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index fb68dd3e7..44bbbfd43 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -867,6 +867,12 @@
+
+ True
+ True
+
+
+
@@ -1125,12 +1131,6 @@
-
- True
- True
-
-
-
@@ -1871,6 +1871,8 @@
+
+
@@ -4455,6 +4457,10 @@
True
True
+
+ True
+ True
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index bac2171c9..efaff1978 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -1401,6 +1401,12 @@
ripple\app\consensus
+
+ ripple\app\consensus
+
+
+ ripple\app\consensus
+
ripple\app\ledger
@@ -1665,12 +1671,6 @@
ripple\app\misc
-
- ripple\app\misc
-
-
- ripple\app\misc
-
ripple\app\misc
@@ -2517,6 +2517,9 @@
ripple\consensus
+
+ ripple\consensus
+
ripple\core
@@ -5217,6 +5220,9 @@
test\consensus
+
+ test\consensus
+
test\core
diff --git a/docs/source.dox b/docs/source.dox
index 7475222c9..1fb7de3f0 100644
--- a/docs/source.dox
+++ b/docs/source.dox
@@ -113,6 +113,7 @@ INPUT = \
../src/ripple/consensus/ConsensusProposal.h \
../src/ripple/consensus/DisputedTx.h \
../src/ripple/consensus/LedgerTiming.h \
+ ../src/ripple/consensus/Validations.h \
../src/ripple/app/consensus/RCLCxTx.h \
../src/ripple/app/consensus/RCLCxLedger.h \
../src/ripple/app/consensus/RCLConsensus.h \
@@ -120,6 +121,7 @@ INPUT = \
../src/ripple/app/tx/apply.h \
../src/ripple/app/tx/applySteps.h \
../src/ripple/app/tx/impl/InvariantCheck.h \
+ ../src/ripple/app/consensus/RCLValidations.h \
INPUT_ENCODING = UTF-8
FILE_PATTERNS =
diff --git a/src/ripple/app/consensus/RCLConsensus.cpp b/src/ripple/app/consensus/RCLConsensus.cpp
index 55323ff86..2d95cdb99 100644
--- a/src/ripple/app/consensus/RCLConsensus.cpp
+++ b/src/ripple/app/consensus/RCLConsensus.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
#include
@@ -222,7 +223,7 @@ RCLConsensus::hasOpenTransactions() const
std::size_t
RCLConsensus::proposersValidated(LedgerHash const& h) const
{
- return app_.getValidations().getTrustedValidationCount(h);
+ return app_.getValidations().numTrustedForLedger(h);
}
std::size_t
@@ -244,8 +245,9 @@ RCLConsensus::getPrevLedger(
// Get validators that are on our ledger, or "close" to being on
// our ledger.
- auto vals = app_.getValidations().getCurrentValidations(
- ledgerID, parentID, ledgerMaster_.getValidLedgerIndex());
+ auto vals =
+ app_.getValidations().currentTrustedDistribution(
+ ledgerID, parentID, ledgerMaster_.getValidLedgerIndex());
uint256 netLgr = ledgerID;
int netLgrCount = 0;
@@ -253,11 +255,11 @@ RCLConsensus::getPrevLedger(
{
// Switch to ledger supported by more peers
// Or stick with ours on a tie
- if ((it.second.first > netLgrCount) ||
- ((it.second.first == netLgrCount) && (it.first == ledgerID)))
+ if ((it.second.count > netLgrCount) ||
+ ((it.second.count== netLgrCount) && (it.first == ledgerID)))
{
netLgr = it.first;
- netLgrCount = it.second.first;
+ netLgrCount = it.second.count;
}
}
@@ -269,7 +271,7 @@ RCLConsensus::getPrevLedger(
if (auto stream = j_.debug())
{
for (auto& it : vals)
- stream << "V: " << it.first << ", " << it.second.first;
+ stream << "V: " << it.first << ", " << it.second.count;
stream << getJson(true);
}
}
@@ -317,14 +319,10 @@ RCLConsensus::onClose(
{
// previous ledger was flag ledger, add pseudo-transactions
auto const validations =
- app_.getValidations().getValidations(prevLedger->info().parentHash);
+ app_.getValidations().getTrustedForLedger (
+ prevLedger->info().parentHash);
- std::size_t const count = std::count_if(
- validations.begin(), validations.end(), [](auto const& v) {
- return v.second->isTrusted();
- });
-
- if (count >= app_.validators().quorum())
+ if (validations.size() >= app_.validators ().quorum ())
{
feeVote_->doVoting(prevLedger, validations, initialSet);
app_.getAmendmentTable().doVoting(
@@ -843,7 +841,7 @@ RCLConsensus::validate(RCLCxLedger const& ledger, bool proposing)
v->setTrusted();
// suppress it if we receive it - FIXME: wrong suppression
app_.getHashRouter().addSuppression(signingHash);
- app_.getValidations().addValidation(v, "local");
+ handleNewValidation(app_, v, "local");
Blob validation = v->getSerialized();
protocol::TMValidation val;
val.set_validation(&validation[0], validation.size());
diff --git a/src/ripple/app/consensus/RCLValidations.cpp b/src/ripple/app/consensus/RCLValidations.cpp
new file mode 100644
index 000000000..f1de8f55a
--- /dev/null
+++ b/src/ripple/app/consensus/RCLValidations.cpp
@@ -0,0 +1,280 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2017 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+
+RCLValidationsPolicy::RCLValidationsPolicy(Application& app) : app_(app)
+{
+ staleValidations_.reserve(512);
+}
+
+NetClock::time_point
+RCLValidationsPolicy::now() const
+{
+ return app_.timeKeeper().closeTime();
+}
+
+void
+RCLValidationsPolicy::onStale(RCLValidation&& v)
+{
+ // Store the newly stale validation; do not do significant work in this
+ // function since this is a callback from Validations, which may be
+ // doing other work.
+
+ ScopedLockType sl(staleLock_);
+ staleValidations_.emplace_back(std::move(v));
+ if (staleWriting_)
+ return;
+
+ staleWriting_ = true;
+ app_.getJobQueue().addJob(
+ jtWRITE, "Validations::doStaleWrite", [this](Job&) {
+ auto event =
+ app_.getJobQueue().makeLoadEvent(jtDISK, "ValidationWrite");
+ ScopedLockType sl(staleLock_);
+ doStaleWrite(sl);
+ });
+}
+
+void
+RCLValidationsPolicy::flush(hash_map&& remaining)
+{
+ bool anyNew = false;
+ {
+ ScopedLockType sl(staleLock_);
+
+ for (auto const& keyVal : remaining)
+ {
+ staleValidations_.emplace_back(std::move(keyVal.second));
+ anyNew = true;
+ }
+
+ // If we have new validations to write and there isn't a write in
+ // progress already, then write to the database synchronously.
+ if (anyNew && !staleWriting_)
+ {
+ staleWriting_ = true;
+ doStaleWrite(sl);
+ }
+
+ // In the case when a prior asynchronous doStaleWrite was scheduled,
+ // this loop will block until all validations have been flushed.
+ // This ensures that all validations are written upon return from
+ // this function.
+
+ while (staleWriting_)
+ {
+ ScopedUnlockType sul(staleLock_);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ }
+}
+
+// NOTE: doStaleWrite() must be called with staleLock_ *locked*. The passed
+// ScopedLockType& acts as a reminder to future maintainers.
+void
+RCLValidationsPolicy::doStaleWrite(ScopedLockType&)
+{
+ static const std::string insVal(
+ "INSERT INTO Validations "
+ "(InitialSeq, LedgerSeq, LedgerHash,NodePubKey,SignTime,RawData) "
+ "VALUES (:initialSeq, :ledgerSeq, "
+ ":ledgerHash,:nodePubKey,:signTime,:rawData);");
+ static const std::string findSeq(
+ "SELECT LedgerSeq FROM Ledgers WHERE Ledgerhash=:ledgerHash;");
+
+ assert(staleWriting_);
+
+ while (!staleValidations_.empty())
+ {
+ std::vector currentStale;
+ currentStale.reserve(512);
+ staleValidations_.swap(currentStale);
+
+ {
+ ScopedUnlockType sul(staleLock_);
+ {
+ auto db = app_.getLedgerDB().checkoutDb();
+
+ Serializer s(1024);
+ soci::transaction tr(*db);
+ for (auto const& rclValidation : currentStale)
+ {
+ s.erase();
+ STValidation::pointer const& val = rclValidation.unwrap();
+ val->add(s);
+
+ auto const ledgerHash = to_string(val->getLedgerHash());
+
+ boost::optional ledgerSeq;
+ *db << findSeq, soci::use(ledgerHash),
+ soci::into(ledgerSeq);
+
+ auto const initialSeq = ledgerSeq.value_or(
+ app_.getLedgerMaster().getCurrentLedgerIndex());
+ auto const nodePubKey = toBase58(
+ TokenType::TOKEN_NODE_PUBLIC, val->getSignerPublic());
+ auto const signTime =
+ val->getSignTime().time_since_epoch().count();
+
+ soci::blob rawData(*db);
+ rawData.append(
+ reinterpret_cast(s.peekData().data()),
+ s.peekData().size());
+ assert(rawData.get_len() == s.peekData().size());
+
+ *db << insVal, soci::use(initialSeq), soci::use(ledgerSeq),
+ soci::use(ledgerHash), soci::use(nodePubKey),
+ soci::use(signTime), soci::use(rawData);
+ }
+
+ tr.commit();
+ }
+ }
+ }
+
+ staleWriting_ = false;
+}
+
+bool
+handleNewValidation(Application& app,
+ STValidation::ref val,
+ std::string const& source)
+{
+ PublicKey const& signer = val->getSignerPublic();
+ uint256 const& hash = val->getLedgerHash();
+
+ // Ensure validation is marked as trusted if signer currently trusted
+ boost::optional pubKey = app.validators().getTrustedKey(signer);
+ if (!val->isTrusted() && pubKey)
+ val->setTrusted();
+ RCLValidations& validations = app.getValidations();
+
+ beast::Journal j = validations.journal();
+
+ // Do not process partial validations.
+ if (!val->isFull())
+ {
+ const bool current = isCurrent(
+ validations.parms(),
+ app.timeKeeper().closeTime(),
+ val->getSignTime(),
+ val->getSeenTime());
+
+ JLOG(j.debug()) << "Val (partial) for " << hash << " from "
+ << toBase58(TokenType::TOKEN_NODE_PUBLIC, signer)
+ << " ignored "
+ << (val->isTrusted() ? "trusted/" : "UNtrusted/")
+ << (current ? "current" : "stale");
+
+ // Only forward if current and trusted
+ return current && val->isTrusted();
+ }
+
+ if (!val->isTrusted())
+ {
+ JLOG(j.trace()) << "Node "
+ << toBase58(TokenType::TOKEN_NODE_PUBLIC, signer)
+ << " not in UNL st="
+ << val->getSignTime().time_since_epoch().count()
+ << ", hash=" << hash
+ << ", shash=" << val->getSigningHash()
+ << " src=" << source;
+ }
+
+ // If not currently trusted, see if signer is currently listed
+ if (!pubKey)
+ pubKey = app.validators().getListedKey(signer);
+
+ bool shouldRelay = false;
+
+ // only add trusted or listed
+ if (pubKey)
+ {
+ using AddOutcome = RCLValidations::AddOutcome;
+
+ AddOutcome const res = validations.add(*pubKey, val);
+
+ // This is a duplicate validation
+ if (res == AddOutcome::repeat)
+ return false;
+
+ // This validation replaced a prior one with the same sequence number
+ if (res == AddOutcome::sameSeq)
+ {
+ auto const seq = val->getFieldU32(sfLedgerSequence);
+ JLOG(j.warn()) << "Trusted node "
+ << toBase58(TokenType::TOKEN_NODE_PUBLIC, *pubKey)
+ << " published multiple validations for ledger "
+ << seq;
+ }
+
+ JLOG(j.debug()) << "Val for " << hash << " from "
+ << toBase58(TokenType::TOKEN_NODE_PUBLIC, signer)
+ << " added "
+ << (val->isTrusted() ? "trusted/" : "UNtrusted/")
+ << ((res == AddOutcome::current) ? "current" : "stale");
+
+ // Trusted current validations should be checked and relayed.
+ // Trusted validations with sameSeq replaced an older validation
+ // with that sequence number, so should still be checked and relayed.
+ if (val->isTrusted() &&
+ (res == AddOutcome::current || res == AddOutcome::sameSeq))
+ {
+ app.getLedgerMaster().checkAccept(
+ hash, val->getFieldU32(sfLedgerSequence));
+
+ shouldRelay = true;
+ }
+ }
+ else
+ {
+ JLOG(j.debug()) << "Val for " << hash << " from "
+ << toBase58(TokenType::TOKEN_NODE_PUBLIC, signer)
+ << " not added UNtrusted/";
+ }
+
+ // This currently never forwards untrusted validations, though we may
+ // reconsider in the future. From @JoelKatz:
+ // The idea was that we would have a certain number of validation slots with
+ // priority going to validators we trusted. Remaining slots might be
+ // allocated to validators that were listed by publishers we trusted but
+ // that we didn't choose to trust. The shorter term plan was just to forward
+ // untrusted validations if peers wanted them or if we had the
+ // ability/bandwidth to. None of that was implemented.
+ return shouldRelay;
+}
+} // namespace ripple
diff --git a/src/ripple/app/consensus/RCLValidations.h b/src/ripple/app/consensus/RCLValidations.h
new file mode 100644
index 000000000..ec1939e8c
--- /dev/null
+++ b/src/ripple/app/consensus/RCLValidations.h
@@ -0,0 +1,214 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_APP_CONSENSUSS_VALIDATIONS_H_INCLUDED
+#define RIPPLE_APP_CONSENSUSS_VALIDATIONS_H_INCLUDED
+
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+
+class Application;
+
+/** Wrapper over STValidation for generic Validation code
+
+ Wraps an STValidation::pointer for compatibility with the generic validation
+ code.
+*/
+class RCLValidation
+{
+ STValidation::pointer val_;
+public:
+
+ /** Constructor
+
+ @param v The validation to wrap.
+ */
+ RCLValidation(STValidation::pointer const& v) : val_{v}
+ {
+ }
+
+ /// Validated ledger's hash
+ uint256
+ ledgerID() const
+ {
+ return val_->getLedgerHash();
+ }
+
+ /// Validated ledger's sequence number (0 if none)
+ std::uint32_t
+ seq() const
+ {
+ if(auto res = (*val_)[~sfLedgerSequence])
+ return *res;
+ return 0;
+ }
+
+ /// Validation's signing time
+ NetClock::time_point
+ signTime() const
+ {
+ return val_->getSignTime();
+ }
+
+ /// Validated ledger's first seen time
+ NetClock::time_point
+ seenTime() const
+ {
+ return val_->getSeenTime();
+ }
+
+ /// Public key of validator that published the validation
+ PublicKey
+ key() const
+ {
+ return val_->getSignerPublic();
+ }
+
+ /// NodeID of validator that published the validation
+ NodeID
+ nodeID() const
+ {
+ return val_->getNodeID();
+ }
+
+ /// Whether the validation is considered trusted.
+ bool
+ trusted() const
+ {
+ return val_->isTrusted();
+ }
+
+ /// Set the prior ledger hash this validation is following
+ void
+ setPreviousLedgerID(uint256 const& hash)
+ {
+ val_->setPreviousHash(hash);
+ }
+
+ /// Get the prior ledger hash this validation is following
+ uint256
+ getPreviousLedgerID() const
+ {
+ return val_->getPreviousHash();
+ }
+
+ /// Check whether the given hash matches this validation's prior hash
+ bool
+ isPreviousLedgerID(uint256 const& hash) const
+ {
+ return val_->isPreviousHash(hash);
+ }
+
+ /// Get the load fee of the validation if it exists
+ boost::optional
+ loadFee() const
+ {
+ return ~(*val_)[~sfLoadFee];
+ }
+
+ /// Extract the underlying STValidation being wrapped
+ STValidation::pointer
+ unwrap() const
+ {
+ return val_;
+ }
+
+};
+
+/** Implements the StalePolicy policy class for adapting Validations in the RCL
+
+ Manages storing and writing stale RCLValidations to the sqlite DB.
+*/
+class RCLValidationsPolicy
+{
+ using LockType = std::mutex;
+ using ScopedLockType = std::lock_guard;
+ using ScopedUnlockType = GenericScopedUnlock;
+
+ Application& app_;
+
+ // Lock for managing staleValidations_ and writing_
+ std::mutex staleLock_;
+ std::vector staleValidations_;
+ bool staleWriting_ = false;
+
+ // Write the stale validations to sqlite DB, the scoped lock argument
+ // is used to remind callers that the staleLock_ must be *locked* prior
+ // to making the call
+ void
+ doStaleWrite(ScopedLockType&);
+
+public:
+
+ RCLValidationsPolicy(Application & app);
+
+ /** Current time used to determine if validations are stale.
+ */
+ NetClock::time_point
+ now() const;
+
+ /** Handle a newly stale validation.
+
+ @param v The newly stale validation
+
+ @warning This should do minimal work, as it is expected to be called
+ by the generic Validations code while it may be holding an
+ internal lock
+ */
+ void
+ onStale(RCLValidation&& v);
+
+ /** Flush current validations to disk before shutdown.
+
+ @param remaining The remaining validations to flush
+ */
+ void
+ flush(hash_map && remaining);
+};
+
+
+/// Alias for RCL-specific instantiation of generic Validations
+using RCLValidations =
+ Validations;
+
+/** Handle a new validation
+
+ 1. Set the trust status of a validation based on the validating node's
+ public key and this node's current UNL.
+ 2. Add the validation to the set of validations if current.
+ 3. If new and trusted, send the validation to the ledgerMaster.
+
+ @param app Application object containing validations and ledgerMaster
+ @param val The validation to add
+ @param source Name associated with validation used in logging
+
+ @return Whether the validation should be relayed
+*/
+bool
+handleNewValidation(Application & app, STValidation::ref val, std::string const& source);
+
+
+} // namespace ripple
+
+#endif
diff --git a/src/ripple/app/ledger/impl/LedgerMaster.cpp b/src/ripple/app/ledger/impl/LedgerMaster.cpp
index 6814c1d2e..a0e3f83b8 100644
--- a/src/ripple/app/ledger/impl/LedgerMaster.cpp
+++ b/src/ripple/app/ledger/impl/LedgerMaster.cpp
@@ -32,7 +32,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -200,7 +200,7 @@ LedgerMaster::setValidLedger(
if (! standalone_)
{
- times = app_.getValidations().getValidationTimes(
+ times = app_.getValidations().getTrustedValidationTimes(
l->info().hash);
}
@@ -700,7 +700,7 @@ LedgerMaster::checkAccept (uint256 const& hash, std::uint32_t seq)
return;
valCount =
- app_.getValidations().getTrustedValidationCount (hash);
+ app_.getValidations().numTrustedForLedger (hash);
if (valCount >= app_.validators ().quorum ())
{
@@ -764,8 +764,7 @@ LedgerMaster::checkAccept (
return;
auto const minVal = getNeededValidations();
- auto const tvc = app_.getValidations().getTrustedValidationCount(
- ledger->info().hash);
+ auto const tvc = app_.getValidations().numTrustedForLedger(ledger->info().hash);
if (tvc < minVal) // nothing we can do
{
JLOG (m_journal.trace()) <<
@@ -791,7 +790,7 @@ LedgerMaster::checkAccept (
app_.getOrderBookDB().setup(ledger);
}
- std::uint64_t const base = app_.getFeeTrack().getLoadBase();
+ std::uint32_t const base = app_.getFeeTrack().getLoadBase();
auto fees = app_.getValidations().fees (ledger->info().hash, base);
{
auto fees2 = app_.getValidations().fees (
@@ -799,7 +798,7 @@ LedgerMaster::checkAccept (
fees.reserve (fees.size() + fees2.size());
std::copy (fees2.begin(), fees2.end(), std::back_inserter(fees));
}
- std::uint64_t fee;
+ std::uint32_t fee;
if (! fees.empty())
{
std::sort (fees.begin(), fees.end());
@@ -854,7 +853,7 @@ LedgerMaster::consensusBuilt(
// maybe we saved up validations for some other ledger that can be
auto const val =
- app_.getValidations().getCurrentTrustedValidations();
+ app_.getValidations().currentTrusted();
// Track validation counts with sequence numbers
class valSeq
diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp
index 30c4f758c..d827ac496 100644
--- a/src/ripple/app/main/Application.cpp
+++ b/src/ripple/app/main/Application.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -328,7 +329,7 @@ public:
std::unique_ptr m_amendmentTable;
std::unique_ptr mFeeTrack;
std::unique_ptr mHashRouter;
- std::unique_ptr mValidations;
+ RCLValidations mValidations;
std::unique_ptr m_loadManager;
std::unique_ptr txQ_;
DeadlineTimer m_sweepTimer;
@@ -471,7 +472,8 @@ public:
, mHashRouter (std::make_unique(
stopwatch(), HashRouter::getDefaultHoldTime ()))
- , mValidations (make_Validations (*this))
+ , mValidations (ValidationParms(),stopwatch(), logs_->journal("Validations"),
+ *this)
, m_loadManager (make_LoadManager (*this, *this, logs_->journal("LoadManager")))
@@ -670,9 +672,9 @@ public:
return *mHashRouter;
}
- Validations& getValidations () override
+ RCLValidations& getValidations () override
{
- return *mValidations;
+ return mValidations;
}
ValidatorList& validators () override
@@ -853,7 +855,7 @@ public:
m_entropyTimer.cancel ();
}
- mValidations->flush ();
+ mValidations.flush ();
validatorSites_->stop ();
@@ -926,7 +928,7 @@ public:
getNodeStore().sweep();
getLedgerMaster().sweep();
getTempNodeCache().sweep();
- getValidations().sweep();
+ getValidations().expire();
getInboundLedgers().sweep();
m_acceptedLedgerCache.sweep();
family().treecache().sweep();
diff --git a/src/ripple/app/main/Application.h b/src/ripple/app/main/Application.h
index a6033636c..589c18922 100644
--- a/src/ripple/app/main/Application.h
+++ b/src/ripple/app/main/Application.h
@@ -64,7 +64,7 @@ class STLedgerEntry;
class TimeKeeper;
class TransactionMaster;
class TxQ;
-class Validations;
+
class ValidatorList;
class ValidatorSite;
class Cluster;
@@ -74,6 +74,13 @@ class SHAMapStore;
using NodeCache = TaggedCache ;
+template
+class Validations;
+class RCLValidation;
+class RCLValidationsPolicy;
+using RCLValidations =
+ Validations;
+
class Application : public beast::PropertyStream::Source
{
public:
@@ -128,7 +135,7 @@ public:
virtual ManifestCache& validatorManifests () = 0;
virtual ManifestCache& publisherManifests () = 0;
virtual Cluster& cluster () = 0;
- virtual Validations& getValidations () = 0;
+ virtual RCLValidations& getValidations () = 0;
virtual NodeStore::Database& getNodeStore () = 0;
virtual InboundLedgers& getInboundLedgers () = 0;
virtual InboundTransactions& getInboundTransactions () = 0;
diff --git a/src/ripple/app/misc/AmendmentTable.h b/src/ripple/app/misc/AmendmentTable.h
index 50f722e28..2cad2343b 100644
--- a/src/ripple/app/misc/AmendmentTable.h
+++ b/src/ripple/app/misc/AmendmentTable.h
@@ -21,7 +21,7 @@
#define RIPPLE_APP_MISC_AMENDMENTTABLE_H_INCLUDED
#include
-#include
+#include
#include
#include
@@ -78,7 +78,7 @@ public:
NetClock::time_point closeTime,
std::set const& enabledAmendments,
majorityAmendments_t const& majorityAmendments,
- ValidationSet const& valSet) = 0;
+ std::vector const& valSet) = 0;
// Called by the consensus code when we need to
// add feature entries to a validation
@@ -112,7 +112,7 @@ public:
void
doVoting (
std::shared_ptr const& lastClosedLedger,
- ValidationSet const& parentValidations,
+ std::vector const& parentValidations,
std::shared_ptr const& initialPosition)
{
// Ask implementation what to do
diff --git a/src/ripple/app/misc/FeeVote.h b/src/ripple/app/misc/FeeVote.h
index ea271545e..4e8377303 100644
--- a/src/ripple/app/misc/FeeVote.h
+++ b/src/ripple/app/misc/FeeVote.h
@@ -22,7 +22,7 @@
#include
#include
-#include
+#include
#include
#include
@@ -72,7 +72,7 @@ public:
virtual
void
doVoting (std::shared_ptr const& lastClosedLedger,
- ValidationSet const& parentValidations,
+ std::vector const& parentValidations,
std::shared_ptr const& initialPosition) = 0;
};
diff --git a/src/ripple/app/misc/FeeVoteImpl.cpp b/src/ripple/app/misc/FeeVoteImpl.cpp
index 221d43df6..9a24aca59 100644
--- a/src/ripple/app/misc/FeeVoteImpl.cpp
+++ b/src/ripple/app/misc/FeeVoteImpl.cpp
@@ -21,7 +21,7 @@
#include
#include
#include
-#include
+#include
#include
#include
@@ -103,7 +103,7 @@ public:
void
doVoting (std::shared_ptr const& lastClosedLedger,
- ValidationSet const& parentValidations,
+ std::vector const& parentValidations,
std::shared_ptr const& initialPosition) override;
};
@@ -149,8 +149,8 @@ FeeVoteImpl::doValidation(
void
FeeVoteImpl::doVoting(
std::shared_ptr const& lastClosedLedger,
- ValidationSet const& set,
- std::shared_ptr const& initialPosition)
+ std::vector const& set,
+ std::shared_ptr const& initialPosition)
{
// LCL must be flag ledger
assert ((lastClosedLedger->info().seq % 256) == 0);
@@ -164,33 +164,31 @@ FeeVoteImpl::doVoting(
detail::VotableInteger incReserveVote (
lastClosedLedger->fees().increment, target_.owner_reserve);
- for (auto const& e : set)
+ for (auto const& val : set)
{
- STValidation const& val = *e.second;
-
- if (val.isTrusted ())
+ if (val->isTrusted ())
{
- if (val.isFieldPresent (sfBaseFee))
+ if (val->isFieldPresent (sfBaseFee))
{
- baseFeeVote.addVote (val.getFieldU64 (sfBaseFee));
+ baseFeeVote.addVote (val->getFieldU64 (sfBaseFee));
}
else
{
baseFeeVote.noVote ();
}
- if (val.isFieldPresent (sfReserveBase))
+ if (val->isFieldPresent (sfReserveBase))
{
- baseReserveVote.addVote (val.getFieldU32 (sfReserveBase));
+ baseReserveVote.addVote (val->getFieldU32 (sfReserveBase));
}
else
{
baseReserveVote.noVote ();
}
- if (val.isFieldPresent (sfReserveIncrement))
+ if (val->isFieldPresent (sfReserveIncrement))
{
- incReserveVote.addVote (val.getFieldU32 (sfReserveIncrement));
+ incReserveVote.addVote (val->getFieldU32 (sfReserveIncrement));
}
else
{
diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp
index 231f31a5b..4c9690ebf 100644
--- a/src/ripple/app/misc/NetworkOPs.cpp
+++ b/src/ripple/app/misc/NetworkOPs.cpp
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1301,17 +1302,17 @@ bool NetworkOPsImp::checkLastClosedLedger (
hash_map ledgers;
{
- auto current = app_.getValidations ().getCurrentValidations (
+ auto current = app_.getValidations ().currentTrustedDistribution (
closedLedger, prevClosedLedger,
m_ledgerMaster.getValidLedgerIndex());
for (auto& it: current)
{
auto& vc = ledgers[it.first];
- vc.trustedValidations += it.second.first;
+ vc.trustedValidations += it.second.count;
- if (it.second.second > vc.highValidation)
- vc.highValidation = it.second.second;
+ if (it.second.highNode > vc.highValidation)
+ vc.highValidation = it.second.highNode;
}
}
@@ -2103,7 +2104,7 @@ bool NetworkOPsImp::recvValidation (
JLOG(m_journal.debug()) << "recvValidation " << val->getLedgerHash ()
<< " from " << source;
pubValidation (val);
- return app_.getValidations ().addValidation (val, source);
+ return handleNewValidation(app_, val, source);
}
Json::Value NetworkOPsImp::getConsensusInfo ()
diff --git a/src/ripple/app/misc/Validations.cpp b/src/ripple/app/misc/Validations.cpp
deleted file mode 100644
index 9f11aae5c..000000000
--- a/src/ripple/app/misc/Validations.cpp
+++ /dev/null
@@ -1,557 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-
-class ValidationsImp : public Validations
-{
-private:
- using LockType = std::mutex;
- using ScopedLockType = std::lock_guard ;
- using ScopedUnlockType = GenericScopedUnlock ;
-
- Application& app_;
- std::mutex mutable mLock;
-
- TaggedCache mValidations;
- ValidationSet mCurrentValidations;
- std::vector mStaleValidations;
-
- bool mWriting;
- beast::Journal j_;
-
-private:
- std::shared_ptr findCreateSet (uint256 const& ledgerHash)
- {
- auto j = mValidations.fetch (ledgerHash);
-
- if (!j)
- {
- j = std::make_shared ();
- mValidations.canonicalize (ledgerHash, j);
- }
-
- return j;
- }
-
- std::shared_ptr findSet (uint256 const& ledgerHash)
- {
- return mValidations.fetch (ledgerHash);
- }
-
-public:
- explicit
- ValidationsImp (Application& app)
- : app_ (app)
- , mValidations ("Validations", 4096, 600, stopwatch(),
- app.journal("TaggedCache"))
- , mWriting (false)
- , j_ (app.journal ("Validations"))
- {
- mStaleValidations.reserve (512);
- }
-
-private:
- bool addValidation (STValidation::ref val, std::string const& source) override
- {
- auto signer = val->getSignerPublic ();
- auto hash = val->getLedgerHash ();
- bool isCurrent = current (val);
-
- auto pubKey = app_.validators ().getTrustedKey (signer);
-
- if (!val->isTrusted() && pubKey)
- val->setTrusted();
-
- // Do not process partial validations.
- if(!val->isFull())
- {
- JLOG (j_.debug()) <<
- "Val (partial) for " << hash <<
- " from " << toBase58 (TokenType::TOKEN_NODE_PUBLIC, signer) <<
- " ignored " << (val->isTrusted () ? "trusted/" : "UNtrusted/") <<
- (isCurrent ? "current" : "stale");
-
- // Only forward if current
- return isCurrent && val->isTrusted();
- }
-
- if (!val->isTrusted ())
- {
- JLOG (j_.trace()) <<
- "Node " << toBase58 (TokenType::TOKEN_NODE_PUBLIC, signer) <<
- " not in UNL st=" << val->getSignTime().time_since_epoch().count() <<
- ", hash=" << hash <<
- ", shash=" << val->getSigningHash () <<
- " src=" << source;
- }
-
- if (! pubKey)
- pubKey = app_.validators ().getListedKey (signer);
-
- if (isCurrent &&
- (val->isTrusted () || pubKey))
- {
- ScopedLockType sl (mLock);
-
- if (!findCreateSet (hash)->insert (
- std::make_pair (*pubKey, val)).second)
- return false;
-
- auto it = mCurrentValidations.find (*pubKey);
-
- if (it == mCurrentValidations.end ())
- {
- // No previous validation from this validator
- mCurrentValidations.emplace (*pubKey, val);
- }
- else if (!it->second)
- {
- // Previous validation has expired
- it->second = val;
- }
- else
- {
- auto const oldSeq = (*it->second)[~sfLedgerSequence];
- auto const newSeq = (*val)[~sfLedgerSequence];
-
- if (oldSeq && newSeq && *oldSeq == *newSeq)
- {
- JLOG (j_.warn()) <<
- "Trusted node " <<
- toBase58 (TokenType::TOKEN_NODE_PUBLIC, *pubKey) <<
- " published multiple validations for ledger " <<
- *oldSeq;
-
- // Remove current validation for the revoked signing key
- if (signer != it->second->getSignerPublic())
- {
- auto set = findSet (it->second->getLedgerHash ());
- if (set)
- set->erase (*pubKey);
- }
- }
-
- if (val->getSignTime () > it->second->getSignTime () ||
- signer != it->second->getSignerPublic())
- {
- // This is either a newer validation or a new signing key
- val->setPreviousHash (it->second->getLedgerHash ());
- mStaleValidations.push_back (it->second);
- it->second = val;
- condWrite ();
- }
- else
- {
- // We already have a newer validation from this source
- isCurrent = false;
- }
- }
- }
-
- JLOG (j_.debug()) <<
- "Val for " << hash <<
- " from " << toBase58 (TokenType::TOKEN_NODE_PUBLIC, signer) <<
- " added " << (val->isTrusted () ? "trusted/" : "UNtrusted/") <<
- (isCurrent ? "current" : "stale");
-
- if (val->isTrusted () && isCurrent)
- {
- app_.getLedgerMaster ().checkAccept (hash, val->getFieldU32 (sfLedgerSequence));
- return true;
- }
-
- // FIXME: This never forwards untrusted validations
- return false;
- }
-
- ValidationSet getValidations (uint256 const& ledger) override
- {
- {
- ScopedLockType sl (mLock);
- auto set = findSet (ledger);
-
- if (set)
- return *set;
- }
- return ValidationSet ();
- }
-
- bool current (STValidation::ref val) override
- {
- // Because this can be called on untrusted, possibly
- // malicious validations, we do our math in a way
- // that avoids any chance of overflowing or underflowing
- // the signing time.
-
- auto const now = app_.timeKeeper().closeTime();
- auto const signTime = val->getSignTime();
-
- return
- (signTime > (now - VALIDATION_VALID_EARLY)) &&
- (signTime < (now + VALIDATION_VALID_WALL)) &&
- ((val->getSeenTime() == NetClock::time_point{}) ||
- (val->getSeenTime() < (now + VALIDATION_VALID_LOCAL)));
- }
-
- std::size_t
- getTrustedValidationCount (uint256 const& ledger) override
- {
- std::size_t trusted = 0;
- ScopedLockType sl (mLock);
- auto set = findSet (ledger);
-
- if (set)
- {
- for (auto& it: *set)
- {
- if (it.second->isTrusted ())
- ++trusted;
- }
- }
-
- return trusted;
- }
-
- std::vector
- fees (uint256 const& ledger, std::uint64_t base) override
- {
- std::vector result;
- std::lock_guard lock (mLock);
- auto const set = findSet (ledger);
- if (set)
- {
- for (auto const& v : *set)
- {
- if (v.second->isTrusted())
- {
- if (v.second->isFieldPresent(sfLoadFee))
- result.push_back(v.second->getFieldU32(sfLoadFee));
- else
- result.push_back(base);
- }
- }
- }
-
- return result;
- }
-
- int getNodesAfter (uint256 const& ledger) override
- {
- // Number of trusted nodes that have moved past this ledger
- int count = 0;
- ScopedLockType sl (mLock);
- for (auto& it: mCurrentValidations)
- {
- if (it.second->isTrusted () && it.second->isPreviousHash (ledger))
- ++count;
- }
- return count;
- }
-
- int getLoadRatio (bool overLoaded) override
- {
- // how many trusted nodes are able to keep up, higher is better
- int goodNodes = overLoaded ? 1 : 0;
- int badNodes = overLoaded ? 0 : 1;
- {
- ScopedLockType sl (mLock);
- for (auto& it: mCurrentValidations)
- {
- if (it.second->isTrusted ())
- {
- if (it.second->isFull ())
- ++goodNodes;
- else
- ++badNodes;
- }
- }
- }
- return (goodNodes * 100) / (goodNodes + badNodes);
- }
-
- std::list getCurrentTrustedValidations () override
- {
- std::list ret;
-
- ScopedLockType sl (mLock);
- auto it = mCurrentValidations.begin ();
-
- while (it != mCurrentValidations.end ())
- {
- if (!it->second) // contains no record
- it = mCurrentValidations.erase (it);
- else if (! current (it->second))
- {
- // contains a stale record
- mStaleValidations.push_back (it->second);
- it->second.reset ();
- condWrite ();
- it = mCurrentValidations.erase (it);
- }
- else
- {
- // contains a live record
- if (it->second->isTrusted ())
- ret.push_back (it->second);
-
- ++it;
- }
- }
-
- return ret;
- }
-
- hash_set getCurrentPublicKeys () override
- {
- hash_set ret;
-
- ScopedLockType sl (mLock);
- auto it = mCurrentValidations.begin ();
-
- while (it != mCurrentValidations.end ())
- {
- if (!it->second) // contains no record
- it = mCurrentValidations.erase (it);
- else if (! current (it->second))
- {
- // contains a stale record
- mStaleValidations.push_back (it->second);
- it->second.reset ();
- condWrite ();
- it = mCurrentValidations.erase (it);
- }
- else
- {
- // contains a live record
- ret.insert (it->first);
-
- ++it;
- }
- }
-
- return ret;
- }
-
- LedgerToValidationCounter getCurrentValidations (
- uint256 currentLedger,
- uint256 priorLedger,
- LedgerIndex cutoffBefore) override
- {
- bool valCurrentLedger = currentLedger.isNonZero ();
- bool valPriorLedger = priorLedger.isNonZero ();
-
- LedgerToValidationCounter ret;
-
- ScopedLockType sl (mLock);
- auto it = mCurrentValidations.begin ();
-
- while (it != mCurrentValidations.end ())
- {
- if (!it->second) // contains no record
- it = mCurrentValidations.erase (it);
- else if (! current (it->second))
- {
- // contains a stale record
- mStaleValidations.push_back (it->second);
- it->second.reset ();
- condWrite ();
- it = mCurrentValidations.erase (it);
- }
- else if (! it->second->isTrusted())
- ++it;
- else if (! it->second->isFieldPresent (sfLedgerSequence) ||
- (it->second->getFieldU32 (sfLedgerSequence) >= cutoffBefore))
- {
- // contains a live record
- bool countPreferred = valCurrentLedger && (it->second->getLedgerHash () == currentLedger);
-
- if (!countPreferred && // allow up to one ledger slip in either direction
- ((valCurrentLedger && it->second->isPreviousHash (currentLedger)) ||
- (valPriorLedger && (it->second->getLedgerHash () == priorLedger))))
- {
- countPreferred = true;
- JLOG (j_.trace()) << "Counting for " << currentLedger << " not " << it->second->getLedgerHash ();
- }
-
- ValidationCounter& p = countPreferred ? ret[currentLedger] : ret[it->second->getLedgerHash ()];
- ++ (p.first);
- auto ni = it->second->getNodeID ();
-
- if (ni > p.second)
- p.second = ni;
-
- ++it;
- }
- else
- {
- ++it;
- }
- }
-
- return ret;
- }
-
- std::vector
- getValidationTimes (uint256 const& hash) override
- {
- std::vector times;
- ScopedLockType sl (mLock);
- if (auto j = findSet (hash))
- for (auto& it : *j)
- if (it.second->isTrusted())
- times.push_back (it.second->getSignTime());
- return times;
- }
-
- void flush () override
- {
- bool anyNew = false;
-
- JLOG (j_.info()) << "Flushing validations";
- ScopedLockType sl (mLock);
- for (auto& it: mCurrentValidations)
- {
- if (it.second)
- mStaleValidations.push_back (it.second);
-
- anyNew = true;
- }
- mCurrentValidations.clear ();
-
- if (anyNew)
- condWrite ();
-
- while (mWriting)
- {
- ScopedUnlockType sul (mLock);
- std::this_thread::sleep_for (std::chrono::milliseconds (100));
- }
-
- JLOG (j_.debug()) << "Validations flushed";
- }
-
- void condWrite ()
- {
- if (mWriting)
- return;
-
- mWriting = true;
- app_.getJobQueue ().addJob (
- jtWRITE, "Validations::doWrite",
- [this] (Job&) { doWrite(); });
- }
-
- void doWrite ()
- {
- auto event = app_.getJobQueue ().makeLoadEvent (jtDISK, "ValidationWrite");
-
- std::string insVal ("INSERT INTO Validations "
- "(InitialSeq, LedgerSeq, LedgerHash,NodePubKey,SignTime,RawData) "
- "VALUES (:initialSeq, :ledgerSeq, :ledgerHash,:nodePubKey,:signTime,:rawData);");
- std::string findSeq("SELECT LedgerSeq FROM Ledgers WHERE Ledgerhash=:ledgerHash;");
-
- ScopedLockType sl (mLock);
- assert (mWriting);
-
- while (!mStaleValidations.empty ())
- {
- std::vector vector;
- vector.reserve (512);
- mStaleValidations.swap (vector);
-
- {
- ScopedUnlockType sul (mLock);
- {
- auto db = app_.getLedgerDB ().checkoutDb ();
-
- Serializer s (1024);
- soci::transaction tr(*db);
- for (auto it: vector)
- {
- s.erase ();
- it->add (s);
-
- auto const ledgerHash = to_string(it->getLedgerHash());
-
- boost::optional ledgerSeq;
- *db << findSeq, soci::use(ledgerHash),
- soci::into(ledgerSeq);
-
- auto const initialSeq = ledgerSeq.value_or(
- app_.getLedgerMaster().getCurrentLedgerIndex());
- auto const nodePubKey = toBase58(
- TokenType::TOKEN_NODE_PUBLIC,
- it->getSignerPublic());
- auto const signTime =
- it->getSignTime().time_since_epoch().count();
-
- soci::blob rawData(*db);
- rawData.append(reinterpret_cast(
- s.peekData().data()), s.peekData().size());
- assert(rawData.get_len() == s.peekData().size());
-
- *db <<
- insVal,
- soci::use(initialSeq),
- soci::use(ledgerSeq),
- soci::use(ledgerHash),
- soci::use(nodePubKey),
- soci::use(signTime),
- soci::use(rawData);
- }
-
- tr.commit ();
- }
- }
- }
-
- mWriting = false;
- }
-
- void sweep () override
- {
- ScopedLockType sl (mLock);
- mValidations.sweep ();
- }
-};
-
-std::unique_ptr make_Validations (Application& app)
-{
- return std::make_unique (app);
-}
-
-} // ripple
diff --git a/src/ripple/app/misc/Validations.h b/src/ripple/app/misc/Validations.h
deleted file mode 100644
index af6285d31..000000000
--- a/src/ripple/app/misc/Validations.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#ifndef RIPPLE_APP_MISC_VALIDATIONS_H_INCLUDED
-#define RIPPLE_APP_MISC_VALIDATIONS_H_INCLUDED
-
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-
-// VFALCO TODO rename and move these type aliases into the Validations interface
-
-// nodes validating and highest node ID validating
-using ValidationSet = hash_map;
-
-using ValidationCounter = std::pair;
-using LedgerToValidationCounter = hash_map;
-
-class Validations
-{
-public:
- virtual ~Validations() = default;
-
- virtual bool addValidation (STValidation::ref, std::string const& source) = 0;
-
- virtual bool current (STValidation::ref) = 0;
-
- virtual ValidationSet getValidations (uint256 const& ledger) = 0;
-
- virtual std::size_t getTrustedValidationCount (uint256 const& ledger) = 0;
-
- /** Returns fees reported by trusted validators in the given ledger. */
- virtual
- std::vector
- fees (uint256 const& ledger, std::uint64_t base) = 0;
-
- virtual int getNodesAfter (uint256 const& ledger) = 0;
- virtual int getLoadRatio (bool overLoaded) = 0;
-
- virtual hash_set getCurrentPublicKeys () = 0;
-
- // VFALCO TODO make a type alias for this ugly return value!
- virtual LedgerToValidationCounter getCurrentValidations (
- uint256 currentLedger, uint256 previousLedger,
- LedgerIndex cutoffBefore) = 0;
-
- /** Return the times of all validations for a particular ledger hash. */
- virtual std::vector getValidationTimes (
- uint256 const& ledger) = 0;
-
- virtual std::list
- getCurrentTrustedValidations () = 0;
-
- virtual void flush () = 0;
-
- virtual void sweep () = 0;
-};
-
-extern
-std::unique_ptr
-make_Validations(Application& app);
-
-} // ripple
-
-#endif
diff --git a/src/ripple/app/misc/impl/AmendmentTable.cpp b/src/ripple/app/misc/impl/AmendmentTable.cpp
index 10820e23b..9be2cb183 100644
--- a/src/ripple/app/misc/impl/AmendmentTable.cpp
+++ b/src/ripple/app/misc/impl/AmendmentTable.cpp
@@ -20,7 +20,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -207,7 +207,7 @@ public:
NetClock::time_point closeTime,
std::set const& enabledAmendments,
majorityAmendments_t const& majorityAmendments,
- ValidationSet const& validations) override;
+ std::vector const& validations) override;
};
//------------------------------------------------------------------------------
@@ -411,7 +411,7 @@ AmendmentTableImpl::doVoting (
NetClock::time_point closeTime,
std::set const& enabledAmendments,
majorityAmendments_t const& majorityAmendments,
- ValidationSet const& valSet)
+ std::vector const& valSet)
{
JLOG (j_.trace()) <<
"voting at " << closeTime.time_since_epoch().count() <<
@@ -422,16 +422,16 @@ AmendmentTableImpl::doVoting (
auto vote = std::make_unique ();
// process validations for ledger before flag ledger
- for (auto const& entry : valSet)
+ for (auto const& val : valSet)
{
- if (entry.second->isTrusted ())
+ if (val->isTrusted ())
{
std::set ballot;
- if (entry.second->isFieldPresent (sfAmendments))
+ if (val->isFieldPresent (sfAmendments))
{
auto const choices =
- entry.second->getFieldV256 (sfAmendments);
+ val->getFieldV256 (sfAmendments);
ballot.insert (choices.begin (), choices.end ());
}
diff --git a/src/ripple/consensus/LedgerTiming.h b/src/ripple/consensus/LedgerTiming.h
index db135031b..19f2914d0 100644
--- a/src/ripple/consensus/LedgerTiming.h
+++ b/src/ripple/consensus/LedgerTiming.h
@@ -55,29 +55,6 @@ auto constexpr decreaseLedgerTimeResolutionEvery = 1;
//! The number of seconds a ledger may remain idle before closing
auto constexpr LEDGER_IDLE_INTERVAL = 15s;
-/** The number of seconds a validation remains current after its ledger's close
- time.
-
- This is a safety to protect against very old validations and the time
- it takes to adjust the close time accuracy window.
-*/
-auto constexpr VALIDATION_VALID_WALL = 5min;
-
-/** Duration a validation remains current after first observed.
-
- The number of seconds a validation remains current after the time we first
- saw it. This provides faster recovery in very rare cases where the number
- of validations produced by the network is lower than normal
-*/
-auto constexpr VALIDATION_VALID_LOCAL = 3min;
-
-/** Duration pre-close in which validations are acceptable.
-
- The number of seconds before a close time that we consider a validation
- acceptable. This protects against extreme clock errors
-*/
-auto constexpr VALIDATION_VALID_EARLY = 3min;
-
//! The number of seconds we wait minimum to ensure participation
auto constexpr LEDGER_MIN_CONSENSUS = 1950ms;
diff --git a/src/ripple/consensus/Validations.h b/src/ripple/consensus/Validations.h
new file mode 100644
index 000000000..78bcd3761
--- /dev/null
+++ b/src/ripple/consensus/Validations.h
@@ -0,0 +1,726 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2017 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CONSENSUS_VALIDATIONS_H_INCLUDED
+#define RIPPLE_CONSENSUS_VALIDATIONS_H_INCLUDED
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+
+/** Timing parameters to control validation staleness and expiration.
+
+ @note These are protocol level parameters that should not be changed without
+ careful consideration. They are *not* implemented as static constexpr
+ to allow simulation code to test alternate parameter settings.
+ */
+struct ValidationParms
+{
+ /** The number of seconds a validation remains current after its ledger's
+ close time.
+
+ This is a safety to protect against very old validations and the time
+ it takes to adjust the close time accuracy window.
+ */
+ std::chrono::seconds validationCURRENT_WALL = std::chrono::minutes{5};
+
+ /** Duration a validation remains current after first observed.
+
+ The number of seconds a validation remains current after the time we
+ first saw it. This provides faster recovery in very rare cases where the
+ number of validations produced by the network is lower than normal
+ */
+ std::chrono::seconds validationCURRENT_LOCAL = std::chrono::minutes{3};
+
+ /** Duration pre-close in which validations are acceptable.
+
+ The number of seconds before a close time that we consider a validation
+ acceptable. This protects against extreme clock errors
+ */
+ std::chrono::seconds validationCURRENT_EARLY = std::chrono::minutes{3};
+
+ /** Duration a set of validations for a given ledger hash remain valid
+
+ The number of seconds before a set of validations for a given ledger
+ hash can expire. This keeps validations for recent ledgers available
+ for a reasonable interval.
+ */
+ std::chrono::seconds validationSET_EXPIRES = std::chrono::minutes{10};
+};
+
+/** Whether a validation is still current
+
+ Determines whether a validation can still be considered the current
+ validation from a node based on when it was signed by that node and first
+ seen by this node.
+
+ @param p ValidationParms with timing parameters
+ @param now Current time
+ @param signTime When the validation was signed
+ @param seenTime When the validation was first seen locally
+*/
+inline bool
+isCurrent(
+ ValidationParms const& p,
+ NetClock::time_point now,
+ NetClock::time_point signTime,
+ NetClock::time_point seenTime)
+{
+ // Because this can be called on untrusted, possibly
+ // malicious validations, we do our math in a way
+ // that avoids any chance of overflowing or underflowing
+ // the signing time.
+
+ return (signTime > (now - p.validationCURRENT_EARLY)) &&
+ (signTime < (now + p.validationCURRENT_WALL)) &&
+ ((seenTime == NetClock::time_point{}) ||
+ (seenTime < (now + p.validationCURRENT_LOCAL)));
+}
+
+/** Maintains current and recent ledger validations.
+
+ Manages storage and queries related to validations received on the network.
+ Stores the most current validation from nodes and sets of recent
+ validations grouped by ledger identifier.
+
+ Stored validations are not necessarily from trusted nodes, so clients
+ and implementations should take care to use `trusted` member functions or
+ check the validation's trusted status.
+
+ This class uses a policy design to allow adapting the handling of stale
+ validations in various circumstances. Below is a set of stubs illustrating
+ the required type interface.
+
+ @warning The MutexType is used to manage concurrent access to private
+ members of Validations but does not manage any data in the
+ StalePolicy instance.
+
+ @code
+
+ // Identifier types that should be equality-comparable and copyable
+ struct LedgerID;
+ struct NodeID;
+ struct NodeKey;
+
+ struct Validation
+ {
+ // Ledger ID associated with this validation
+ LedgerID ledgerID() const;
+
+ // Sequence number of validation's ledger (0 means no sequence number)
+ std::uint32_t seq() const
+
+ // When the validation was signed
+ NetClock::time_point signTime() const;
+
+ // When the validation was first observed by this node
+ NetClock::time_point seenTime() const;
+
+ // Signing key of node that published the validation
+ NodeKey key() const;
+
+ // Identifier of node that published the validation
+ NodeID nodeID() const;
+
+ // Whether the publishing node was trusted at the time the validation
+ // arrived
+ bool trusted() const;
+
+ // The PreviousLedger functions below assume instances corresponding
+ // to the same validation (ID/hash/key etc.) share the same previous
+ // ledger ID storage.
+
+ // Set the previous validation ledger from this publishing node that
+ // this validation replaced
+ void setPreviousLedgerID(LedgerID &);
+
+ // Get the previous validation ledger from this publishing node that
+ // this validation replaced
+ LedgerID getPreviousLedgerID() const;
+
+ // Check if this validation had the given ledger ID as its prior ledger
+ bool isPreviousLedgerID(LedgerID const& ) const;
+
+ implementation_specific_t
+ unwrap() -> return the implementation-specific type being wrapped
+
+ // ... implementation specific
+ };
+
+ class StalePolicy
+ {
+ // Handle a newly stale validation, this should do minimal work since
+ // it is called by Validations while it may be iterating Validations
+ // under lock
+ void onStale(Validation && );
+
+ // Flush the remaining validations (typically done on shutdown)
+ void flush(hash_map && remaining);
+
+ // Return the current network time (used to determine staleness)
+ NetClock::time_point now() const;
+
+ // ... implementation specific
+ };
+ @endcode
+
+ @tparam StalePolicy Determines how to determine and handle stale validations
+ @tparam Validation Conforming type representing a ledger validation
+ @tparam MutexType Mutex used to manage concurrent access
+
+*/
+template
+class Validations
+{
+ template
+ using decay_result_t = std::decay_t>;
+
+ using WrappedValidationType =
+ decay_result_t;
+ using LedgerID =
+ decay_result_t;
+ using NodeKey = decay_result_t;
+ using NodeID = decay_result_t;
+ using ValidationMap = hash_map;
+
+ using ScopedLock = std::lock_guard;
+
+ // Manages concurrent access to current_ and byLedger_
+ MutexType mutex_;
+
+ //! The latest validation from each node
+ ValidationMap current_;
+
+ //! Recent validations from nodes, indexed by ledger identifier
+ beast::aged_unordered_map<
+ LedgerID,
+ ValidationMap,
+ std::chrono::steady_clock,
+ beast::uhash<>>
+ byLedger_;
+
+ //! Parameters to determine validation staleness
+ ValidationParms const parms_;
+
+ beast::Journal j_;
+
+ //! StalePolicy details providing now(), onStale() and flush() callbacks
+ //! Is NOT managed by the mutex_ above
+ StalePolicy stalePolicy_;
+
+private:
+ /** Iterate current validations.
+
+ Iterate current validations, optionally removing any stale validations
+ if a time is specified.
+
+ @param t (Optional) Time used to determine staleness
+ @param pre Invokable with signature (std::size_t) called prior to
+ looping.
+ @param f Invokable with signature (NodeKey const &, Validations const &)
+ for each current validation.
+
+ @note The invokable `pre` is called _prior_ to checking for staleness
+ and reflects an upper-bound on the number of calls to `f.
+ @warning The invokable `f` is expected to be a simple transformation of
+ its arguments and will be called with mutex_ under lock.
+ */
+
+ template
+ void
+ current(boost::optional t, Pre&& pre, F&& f)
+ {
+ ScopedLock lock{mutex_};
+ pre(current_.size());
+ auto it = current_.begin();
+ while (it != current_.end())
+ {
+ // Check for staleness, if time specified
+ if (t &&
+ !isCurrent(
+ parms_, *t, it->second.signTime(), it->second.seenTime()))
+ {
+ // contains a stale record
+ stalePolicy_.onStale(std::move(it->second));
+ it = current_.erase(it);
+ }
+ else
+ {
+ auto cit = typename ValidationMap::const_iterator{it};
+ // contains a live record
+ f(cit->first, cit->second);
+ ++it;
+ }
+ }
+ }
+
+ /** Iterate the set of validations associated with a given ledger id
+
+ @param ledgerID The identifier of the ledger
+ @param pre Invokable with signature(std::size_t)
+ @param f Invokable with signature (NodeKey const &, Validation const &)
+
+ @note The invokable `pre` is called prior to iterating validations. The
+ argument is the number of times `f` will be called.
+ @warning The invokable f is expected to be a simple transformation of
+ its arguments and will be called with mutex_ under lock.
+ */
+ template
+ void
+ byLedger(LedgerID const& ledgerID, Pre&& pre, F&& f)
+ {
+ ScopedLock lock{mutex_};
+ auto it = byLedger_.find(ledgerID);
+ if (it != byLedger_.end())
+ {
+ // Update set time since it is being used
+ byLedger_.touch(it);
+ pre(it->second.size());
+ for (auto const& keyVal : it->second)
+ f(keyVal.first, keyVal.second);
+ }
+ }
+
+public:
+ /** Constructor
+
+ @param p ValidationParms to control staleness/expiration of validaitons
+ @param c Clock to use for expiring validations stored by ledger
+ @param j Journal used for logging
+ @param ts Parameters for constructing StalePolicy instance
+ */
+ template
+ Validations(
+ ValidationParms const& p,
+ beast::abstract_clock& c,
+ beast::Journal j,
+ Ts&&... ts)
+ : byLedger_(c), parms_(p), j_(j), stalePolicy_(std::forward(ts)...)
+ {
+ }
+
+ /** Return the validation timing parameters
+ */
+ ValidationParms const&
+ parms() const
+ {
+ return parms_;
+ }
+
+ /** Return the journal
+ */
+ beast::Journal
+ journal() const
+ {
+ return j_;
+ }
+
+ /** Result of adding a new validation
+ */
+ enum class AddOutcome {
+ /// This was a new validation and was added
+ current,
+ /// Already had this validation
+ repeat,
+ /// Not current or was older than current from this node
+ stale,
+ /// Had a validation with same sequence number
+ sameSeq,
+ };
+
+ /** Add a new validation
+
+ Attempt to add a new validation.
+
+ @param key The NodeKey to use for the validation
+ @param val The validation to store
+ @return The outcome of the attempt
+
+ @note The provided key may differ from the validation's
+ key() member since we might be storing by master key and the
+ validation might be signed by a temporary or rotating key.
+
+ */
+ AddOutcome
+ add(NodeKey const& key, Validation const& val)
+ {
+ NetClock::time_point t = stalePolicy_.now();
+ if (!isCurrent(parms_, t, val.signTime(), val.seenTime()))
+ return AddOutcome::stale;
+
+ LedgerID const& id = val.ledgerID();
+
+ // This is only seated if a validation became stale
+ boost::optional maybeStaleValidation;
+
+ AddOutcome result = AddOutcome::current;
+
+ {
+ ScopedLock lock{mutex_};
+
+ auto const ret = byLedger_[id].emplace(key, val);
+
+ // This validation is a repeat if we already have
+ // one with the same id and signing key.
+ if (!ret.second && ret.first->second.key() == val.key())
+ return AddOutcome::repeat;
+
+ // Attempt to insert
+ auto const ins = current_.emplace(key, val);
+
+ if (!ins.second)
+ {
+ // Had a previous validation from the node, consider updating
+ Validation& oldVal = ins.first->second;
+
+ std::uint32_t const oldSeq{oldVal.seq()};
+ std::uint32_t const newSeq{val.seq()};
+
+ // Sequence of 0 indicates a missing sequence number
+ if (oldSeq && newSeq && oldSeq == newSeq)
+ {
+ result = AddOutcome::sameSeq;
+
+ // If the validation key was revoked, update the
+ // existing validation in the byLedger_ set
+ if (val.key() != oldVal.key())
+ {
+ auto const mapIt = byLedger_.find(oldVal.ledgerID());
+ if (mapIt != byLedger_.end())
+ {
+ ValidationMap& validationMap = mapIt->second;
+ // If a new validation with the same ID was
+ // reissued we simply replace.
+ if(oldVal.ledgerID() == val.ledgerID())
+ {
+ auto replaceRes = validationMap.emplace(key, val);
+ // If it was already there, replace
+ if(!replaceRes.second)
+ replaceRes.first->second = val;
+ }
+ else
+ {
+ // If the new validation has a different ID,
+ // we remove the old.
+ validationMap.erase(key);
+ // Erase the set if it is now empty
+ if (validationMap.empty())
+ byLedger_.erase(mapIt);
+ }
+ }
+ }
+ }
+
+ if (val.signTime() > oldVal.signTime() ||
+ val.key() != oldVal.key())
+ {
+ // This is either a newer validation or a new signing key
+ LedgerID const prevID = [&]() {
+ // In the normal case, the prevID is the ID of the
+ // ledger we replace
+ if (oldVal.ledgerID() != val.ledgerID())
+ return oldVal.ledgerID();
+ // In the case the key was revoked and a new validation
+ // for the same ledger ID was sent, the previous ledger
+ // is still the one the now revoked validation had
+ return oldVal.getPreviousLedgerID();
+ }();
+
+ // Allow impl to take over oldVal
+ maybeStaleValidation.emplace(std::move(oldVal));
+ // Replace old val in the map and set the previous ledger ID
+ ins.first->second = val;
+ ins.first->second.setPreviousLedgerID(prevID);
+ }
+ else
+ {
+ // We already have a newer validation from this source
+ result = AddOutcome::stale;
+ }
+ }
+ }
+
+ // Handle the newly stale validation outside the lock
+ if (maybeStaleValidation)
+ {
+ stalePolicy_.onStale(std::move(*maybeStaleValidation));
+ }
+
+ return result;
+ }
+
+ /** Expire old validation sets
+
+ Remove validation sets that were accessed more than
+ validationSET_EXPIRES ago.
+ */
+ void
+ expire()
+ {
+ ScopedLock lock{mutex_};
+ beast::expire(byLedger_, parms_.validationSET_EXPIRES);
+ }
+
+ struct ValidationCounts
+ {
+ //! The number of trusted validations
+ std::size_t count;
+ //! The highest trusted node ID
+ NodeID highNode;
+ };
+
+ /** Distribution of current trusted validations
+
+ Calculates the distribution of current validations but allows
+ ledgers one away from the current ledger to count as the current.
+
+ @param currentLedger The identifier of the ledger we believe is current
+ @param priorLedger The identifier of our previous current ledger
+ @param cutoffBefore Ignore ledgers with sequence number before this
+ */
+ hash_map
+ currentTrustedDistribution(
+ LedgerID const& currentLedger,
+ LedgerID const& priorLedger,
+ std::uint32_t cutoffBefore)
+ {
+ bool const valCurrentLedger = currentLedger != beast::zero;
+ bool const valPriorLedger = priorLedger != beast::zero;
+
+ hash_map ret;
+
+ current(
+ stalePolicy_.now(),
+ // The number of validations does not correspond to the number of
+ // distinct ledgerIDs so we do not call reserve on ret.
+ [&](std::size_t) {},
+ [&](NodeKey const&, Validation const& v) {
+
+ if (!v.trusted())
+ return;
+
+ std::uint32_t const seq = v.seq();
+ if ((seq == 0) || (seq >= cutoffBefore))
+ {
+ // contains a live record
+ bool countPreferred =
+ valCurrentLedger && (v.ledgerID() == currentLedger);
+
+ if (!countPreferred && // allow up to one ledger slip in
+ // either direction
+ ((valCurrentLedger &&
+ v.isPreviousLedgerID(currentLedger)) ||
+ (valPriorLedger && (v.ledgerID() == priorLedger))))
+ {
+ countPreferred = true;
+ JLOG(j_.trace()) << "Counting for " << currentLedger
+ << " not " << v.ledgerID();
+ }
+
+ ValidationCounts& p =
+ countPreferred ? ret[currentLedger] : ret[v.ledgerID()];
+ ++(p.count);
+
+ NodeID const ni = v.nodeID();
+ if (ni > p.highNode)
+ p.highNode = ni;
+ }
+ });
+
+ return ret;
+ }
+
+ /** Count the number of current trusted validators working on the next
+ ledger.
+
+ Counts the number of current trusted validations that replaced the
+ provided ledger. Does not check or update staleness of the validations.
+
+ @param ledgerID The identifier of the preceding ledger of interest
+ @return The number of current trusted validators with ledgerID as the
+ prior ledger.
+ */
+ std::size_t
+ getNodesAfter(LedgerID const& ledgerID)
+ {
+ std::size_t count = 0;
+
+ // Historically this did not not check for stale validations
+ // That may not be important, but this preserves the behavior
+ current(
+ boost::none,
+ [&](std::size_t) {}, // nothing to reserve
+ [&](NodeKey const&, Validation const& v) {
+ if (v.trusted() && v.isPreviousLedgerID(ledgerID))
+ ++count;
+ });
+ return count;
+ }
+
+ /** Get the currently trusted validations
+
+ @return Vector of validations from currently trusted validators
+ */
+ std::vector
+ currentTrusted()
+ {
+ std::vector ret;
+
+ current(
+ stalePolicy_.now(),
+ [&](std::size_t numValidations) { ret.reserve(numValidations); },
+ [&](NodeKey const&, Validation const& v) {
+ if (v.trusted())
+ ret.push_back(v.unwrap());
+ });
+ return ret;
+ }
+
+ /** Get the set of known public keys associated with current validations
+
+ @return The set of of knowns keys for current trusted and untrusted
+ validations
+ */
+ hash_set
+ getCurrentPublicKeys()
+ {
+ hash_set ret;
+ current(
+ stalePolicy_.now(),
+ [&](std::size_t numValidations) { ret.reserve(numValidations); },
+ [&](NodeKey const& k, Validation const&) { ret.insert(k); });
+
+ return ret;
+ }
+
+ /** Count the number of trusted validations for the given ledger
+
+ @param ledgerID The identifier of ledger of interest
+ @return The number of trusted validations
+ */
+ std::size_t
+ numTrustedForLedger(LedgerID const& ledgerID)
+ {
+ std::size_t count = 0;
+ byLedger(
+ ledgerID,
+ [&](std::size_t) {}, // nothing to reserve
+ [&](NodeKey const&, Validation const& v) {
+ if (v.trusted())
+ ++count;
+ });
+ return count;
+ }
+
+ /** Get set of trusted validations associated with a given ledger
+
+ @param ledgerID The identifier of ledger of interest
+ @return Trusted validations associated with ledger
+ */
+ std::vector
+ getTrustedForLedger(LedgerID const& ledgerID)
+ {
+ std::vector res;
+ byLedger(
+ ledgerID,
+ [&](std::size_t numValidations) { res.reserve(numValidations); },
+ [&](NodeKey const&, Validation const& v) {
+ if (v.trusted())
+ res.emplace_back(v.unwrap());
+ });
+
+ return res;
+ }
+
+ /** Return the sign times of all validations associated with a given ledger
+
+ @param ledgerID The identifier of ledger of interest
+ @return Vector of times
+ */
+ std::vector
+ getTrustedValidationTimes(LedgerID const& ledgerID)
+ {
+ std::vector times;
+ byLedger(
+ ledgerID,
+ [&](std::size_t numValidations) { times.reserve(numValidations); },
+ [&](NodeKey const&, Validation const& v) {
+ if (v.trusted())
+ times.emplace_back(v.signTime());
+ });
+ return times;
+ }
+
+ /** Returns fees reported by trusted validators in the given ledger
+
+ @param ledgerID The identifier of ledger of interest
+ @param baseFee The fee to report if not present in the validation
+ @return Vector of fees
+ */
+ std::vector
+ fees(LedgerID const& ledgerID, std::uint32_t baseFee)
+ {
+ std::vector res;
+ byLedger(
+ ledgerID,
+ [&](std::size_t numValidations) { res.reserve(numValidations); },
+ [&](NodeKey const&, Validation const& v) {
+ if (v.trusted())
+ {
+ boost::optional loadFee = v.loadFee();
+ if (loadFee)
+ res.push_back(*loadFee);
+ else
+ res.push_back(baseFee);
+ }
+ });
+ return res;
+ }
+
+ /** Flush all current validations
+ */
+ void
+ flush()
+ {
+ JLOG(j_.info()) << "Flushing validations";
+
+ hash_map flushed;
+ using std::swap;
+ {
+ ScopedLock lock{mutex_};
+ swap(flushed, current_);
+ }
+
+ stalePolicy_.flush(std::move(flushed));
+
+ JLOG(j_.debug()) << "Validations flushed";
+ }
+};
+} // namespace ripple
+#endif
diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp
index bcb356f9a..f74491daf 100644
--- a/src/ripple/overlay/impl/PeerImp.cpp
+++ b/src/ripple/overlay/impl/PeerImp.cpp
@@ -29,7 +29,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -1576,7 +1576,10 @@ PeerImp::onMessage (std::shared_ptr const& m)
val->setSeen (closeTime);
}
- if (! app_.getValidations().current (val))
+ if (! isCurrent(app_.getValidations().parms(),
+ app_.timeKeeper().closeTime(),
+ val->getSignTime(),
+ val->getSeenTime()))
{
JLOG(p_journal_.trace()) << "Validation: Not current";
fee_ = Resource::feeUnwantedData;
diff --git a/src/ripple/unity/app_consensus.cpp b/src/ripple/unity/app_consensus.cpp
index 794f35192..e716d88d0 100644
--- a/src/ripple/unity/app_consensus.cpp
+++ b/src/ripple/unity/app_consensus.cpp
@@ -20,3 +20,5 @@
#include
#include
+#include
+
diff --git a/src/ripple/unity/app_misc.cpp b/src/ripple/unity/app_misc.cpp
index ae6522f2d..65d0a5b9b 100644
--- a/src/ripple/unity/app_misc.cpp
+++ b/src/ripple/unity/app_misc.cpp
@@ -24,7 +24,6 @@
#include
#include
#include
-#include
#include
#include
diff --git a/src/test/app/AmendmentTable_test.cpp b/src/test/app/AmendmentTable_test.cpp
index 1ff93dec3..b2301cd33 100644
--- a/src/test/app/AmendmentTable_test.cpp
+++ b/src/test/app/AmendmentTable_test.cpp
@@ -378,7 +378,7 @@ public:
auto const roundTime = weekTime (week);
// Build validations
- ValidationSet validations;
+ std::vector validations;
validations.reserve (validators.size ());
int i = 0;
@@ -402,7 +402,7 @@ public:
v->setFieldV256 (sfAmendments, field);
v->setTrusted();
- validations [val] = v;
+ validations.emplace_back(v);
}
ourVotes = table.doValidation (enabled);
diff --git a/src/test/consensus/Validations_test.cpp b/src/test/consensus/Validations_test.cpp
new file mode 100644
index 000000000..52878f752
--- /dev/null
+++ b/src/test/consensus/Validations_test.cpp
@@ -0,0 +1,986 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012-2017 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace test {
+
+class Validations_test : public beast::unit_test::suite
+{
+ using clock_type = beast::abstract_clock const;
+ //--------------------------------------------------------------------------
+ // Basic type wrappers for validation types
+
+ // Represents a ledger sequence number
+ struct Seq
+ {
+ explicit Seq(std::uint32_t sIn) : s{sIn}
+ {
+ }
+
+ Seq() : s{0}
+ {
+ }
+
+ operator std::uint32_t() const
+ {
+ return s;
+ }
+
+ std::uint32_t s;
+ };
+
+ // Represents a unique ledger identifier
+ struct ID
+ {
+ explicit ID(std::uint32_t idIn) : id{idIn}
+ {
+ }
+
+ ID() : id{0}
+ {
+ }
+
+ int
+ signum() const
+ {
+ return id == 0 ? 0 : 1;
+ }
+
+ operator std::size_t() const
+ {
+ return id;
+ }
+
+ template
+ friend void
+ hash_append(Hasher& h, ID const& id)
+ {
+ using beast::hash_append;
+ hash_append(h, id.id);
+ }
+
+ std::uint32_t id;
+ };
+
+ class Node;
+
+ // Basic implementation of the requirements of Validation in the generic
+ // Validations class
+ class Validation
+ {
+ friend class Node;
+
+ ID ledgerID_ = ID{0};
+ Seq seq_ = Seq{0};
+ NetClock::time_point signTime_;
+ NetClock::time_point seenTime_;
+ std::string key_;
+ std::size_t nodeID_ = 0;
+ bool trusted_ = true;
+ boost::optional loadFee_;
+ // Shared prevID to ensure value is shared amongst instances
+ std::shared_ptr prevID_;
+
+ public:
+ Validation() : prevID_(std::make_shared(0))
+ {
+ }
+
+ ID
+ ledgerID() const
+ {
+ return ledgerID_;
+ }
+
+ Seq
+ seq() const
+ {
+ return seq_;
+ }
+
+ NetClock::time_point
+ signTime() const
+ {
+ return signTime_;
+ }
+
+ NetClock::time_point
+ seenTime() const
+ {
+ return seenTime_;
+ }
+
+ std::string
+ key() const
+ {
+ return key_;
+ }
+
+ std::uint32_t
+ nodeID() const
+ {
+ return nodeID_;
+ }
+
+ bool
+ trusted() const
+ {
+ return trusted_;
+ }
+
+ void
+ setPreviousLedgerID(ID const& prevID)
+ {
+ *prevID_ = prevID;
+ }
+
+ bool
+ isPreviousLedgerID(ID const& prevID) const
+ {
+ return *prevID_ == prevID;
+ }
+
+ ID
+ getPreviousLedgerID() const
+ {
+ return *prevID_;
+ }
+
+ boost::optional
+ loadFee() const
+ {
+ return loadFee_;
+ }
+
+ Validation const&
+ unwrap() const
+ {
+ return *this;
+ }
+
+ auto
+ asTie() const
+ {
+ return std::tie(
+ ledgerID_,
+ seq_,
+ signTime_,
+ seenTime_,
+ key_,
+ nodeID_,
+ trusted_,
+ *prevID_,
+ loadFee_);
+ }
+ bool
+ operator==(Validation const& o) const
+ {
+ return asTie() == o.asTie();
+ }
+
+ bool
+ operator<(Validation const& o) const
+ {
+ return asTie() < o.asTie();
+ }
+ };
+
+ // Helper to convert steady_clock to a reasonable NetClock
+ // This allows a single manual clock in the unit tests
+ static NetClock::time_point
+ toNetClock(clock_type const& c)
+ {
+ // We don't care about the actual epochs, but do want the
+ // generated NetClock time to be well past its epoch to ensure
+ // any subtractions are positive
+ using namespace std::chrono;
+ return NetClock::time_point(duration_cast(
+ c.now().time_since_epoch() + 86400s));
+ }
+
+ // Represents a node that can issue validations
+ class Node
+ {
+ clock_type const& c_;
+ std::size_t nodeID_;
+ bool trusted_ = true;
+ std::size_t signIdx_ = 0;
+ boost::optional loadFee_;
+
+ public:
+ Node(std::uint32_t nodeID, clock_type const& c) : c_(c), nodeID_(nodeID)
+ {
+ }
+
+ void
+ untrust()
+ {
+ trusted_ = false;
+ }
+
+ void
+ trust()
+ {
+ trusted_ = true;
+ }
+
+ void
+ setLoadFee(std::uint32_t fee)
+ {
+ loadFee_ = fee;
+ }
+
+ std::size_t
+ nodeID() const
+ {
+ return nodeID_;
+ }
+
+ void
+ advanceKey()
+ {
+ signIdx_++;
+ }
+
+ std::string
+ masterKey() const
+ {
+ return std::to_string(nodeID_);
+ }
+
+ std::string
+ currKey() const
+ {
+ return masterKey() + "_" + std::to_string(signIdx_);
+ }
+
+ NetClock::time_point
+ now() const
+ {
+ return toNetClock(c_);
+ }
+
+ // Issue a new validation with given sequence number and id and
+ // with signing and seen times offset from the common clock
+ Validation
+ validation(
+ Seq seq,
+ ID i,
+ NetClock::duration signOffset,
+ NetClock::duration seenOffset) const
+ {
+ Validation v;
+ v.seq_ = seq;
+ v.ledgerID_ = i;
+
+ v.signTime_ = now() + signOffset;
+ v.seenTime_ = now() + seenOffset;
+
+ v.nodeID_ = nodeID_;
+ v.key_ = currKey();
+ v.trusted_ = trusted_;
+ v.loadFee_ = loadFee_;
+ return v;
+ }
+
+ // Issue a new validation with the given sequence number and id
+ Validation
+ validation(Seq seq, ID i) const
+ {
+ return validation(
+ seq, i, NetClock::duration{0}, NetClock::duration{0});
+ }
+ };
+
+ // Non-locking mutex to avoid the need for testing generic Validations
+ struct DummyMutex
+ {
+ void
+ lock()
+ {
+ }
+
+ void
+ unlock()
+ {
+ }
+ };
+
+ // Saved StaleData for inspection in test
+ struct StaleData
+ {
+ std::vector stale;
+ hash_map flushed;
+ };
+
+ // Generic Validations policy that saves stale/flushed data into
+ // a StaleData instance.
+ class StalePolicy
+ {
+ StaleData& staleData_;
+ clock_type& c_;
+
+ public:
+ StalePolicy(StaleData& sd, clock_type& c)
+ : staleData_{sd}, c_{c}
+ {
+ }
+
+ NetClock::time_point
+ now() const
+ {
+ return toNetClock(c_);
+ }
+
+ void
+ onStale(Validation&& v)
+ {
+ staleData_.stale.emplace_back(std::move(v));
+ }
+
+ void
+ flush(hash_map&& remaining)
+ {
+ staleData_.flushed = std::move(remaining);
+ }
+ };
+
+ // Specialize generic Validations using the above types
+ using TestValidations =
+ Validations;
+
+ // Hoist enum for writing simpler tests
+ using AddOutcome = TestValidations::AddOutcome;
+
+ // Gather the dependencies of TestValidations in a single class and provide
+ // accessors for simplifying test logic
+ class TestHarness
+ {
+ StaleData staleData_;
+ ValidationParms p_;
+ beast::manual_clock clock_;
+ beast::Journal j_;
+ TestValidations tv_;
+ int nextNodeId_ = 0;
+
+ public:
+ TestHarness() : tv_(p_, clock_, j_, staleData_, clock_)
+ {
+ }
+
+ // Helper to add an existing validation
+ AddOutcome
+ add(Node const& n, Validation const& v)
+ {
+ return tv_.add(n.masterKey(), v);
+ }
+
+ // Helper to directly create the validation
+ template
+ std::enable_if_t<(sizeof...(Ts) > 1), AddOutcome>
+ add(Node const& n, Ts&&... ts)
+ {
+ return add(n, n.validation(std::forward