Refactor Validations (RIPD-1412,RIPD-1356):

Introduces a generic Validations class for storing and querying current and
recent validations.  Aditionally migrates the validation related timing
constants from LedgerTiming to the new Validations code.

Introduces RCLValidations as the version of Validations adapted for use in the
RCL.  This adds support for flushing/writing validations to the sqlite log and
also manages concurrent access to the Validations data.

RCLValidations::flush() no longer uses the JobQueue for its database
write at shutdown.  It performs the write directly without
changing threads.
This commit is contained in:
Brad Chase
2017-04-05 13:27:01 -04:00
committed by seelabs
parent 95f107d487
commit 7ae3c91015
25 changed files with 2312 additions and 746 deletions

View File

@@ -867,6 +867,12 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\consensus\RCLCxTx.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\consensus\RCLValidations.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\consensus\RCLValidations.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\ledger\AbstractFetchPackContainer.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\ledger\AcceptedLedger.cpp">
@@ -1125,12 +1131,6 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\misc\Validations.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\misc\Validations.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorSite.h">
@@ -1871,6 +1871,8 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\Validations.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\Config.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\ConfigSections.h">
@@ -4455,6 +4457,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\consensus\Validations_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\core\Config_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -1401,6 +1401,12 @@
<ClInclude Include="..\..\src\ripple\app\consensus\RCLCxTx.h">
<Filter>ripple\app\consensus</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\consensus\RCLValidations.cpp">
<Filter>ripple\app\consensus</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\consensus\RCLValidations.h">
<Filter>ripple\app\consensus</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\ledger\AbstractFetchPackContainer.h">
<Filter>ripple\app\ledger</Filter>
</ClInclude>
@@ -1665,12 +1671,6 @@
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
<Filter>ripple\app\misc</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\misc\Validations.cpp">
<Filter>ripple\app\misc</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\misc\Validations.h">
<Filter>ripple\app\misc</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
<Filter>ripple\app\misc</Filter>
</ClInclude>
@@ -2517,6 +2517,9 @@
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
<Filter>ripple\consensus</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\consensus\Validations.h">
<Filter>ripple\consensus</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\Config.h">
<Filter>ripple\core</Filter>
</ClInclude>
@@ -5217,6 +5220,9 @@
<ClCompile Include="..\..\src\test\consensus\LedgerTiming_test.cpp">
<Filter>test\consensus</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\consensus\Validations_test.cpp">
<Filter>test\consensus</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\core\Config_test.cpp">
<Filter>test\core</Filter>
</ClCompile>

View File

@@ -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 =

View File

@@ -19,6 +19,7 @@
#include <BeastConfig.h>
#include <ripple/app/consensus/RCLConsensus.h>
#include <ripple/app/consensus/RCLValidations.h>
#include <ripple/app/ledger/InboundLedgers.h>
#include <ripple/app/ledger/InboundTransactions.h>
#include <ripple/app/ledger/LedgerMaster.h>
@@ -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,7 +245,8 @@ RCLConsensus::getPrevLedger(
// Get validators that are on our ledger, or "close" to being on
// our ledger.
auto vals = app_.getValidations().getCurrentValidations(
auto vals =
app_.getValidations().currentTrustedDistribution(
ledgerID, parentID, ledgerMaster_.getValidLedgerIndex());
uint256 netLgr = ledgerID;
@@ -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());

View File

@@ -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 <BeastConfig.h>
#include <ripple/app/consensus/RCLValidations.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/app/misc/ValidatorList.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/basics/chrono.h>
#include <ripple/consensus/LedgerTiming.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/core/JobQueue.h>
#include <ripple/core/TimeKeeper.h>
#include <memory>
#include <mutex>
#include <thread>
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<PublicKey, RCLValidation>&& 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<RCLValidation> 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<std::uint64_t> 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<const char*>(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<PublicKey> 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

View File

@@ -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 <ripple/basics/ScopedLock.h>
#include <ripple/consensus/Validations.h>
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/STValidation.h>
#include <vector>
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<std::uint32_t>
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<LockType>;
using ScopedUnlockType = GenericScopedUnlock<LockType>;
Application& app_;
// Lock for managing staleValidations_ and writing_
std::mutex staleLock_;
std::vector<RCLValidation> 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<PublicKey, RCLValidation> && remaining);
};
/// Alias for RCL-specific instantiation of generic Validations
using RCLValidations =
Validations<RCLValidationsPolicy, RCLValidation, std::mutex>;
/** 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

View File

@@ -32,7 +32,7 @@
#include <ripple/app/misc/SHAMapStore.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/app/consensus/RCLValidations.h>
#include <ripple/app/misc/ValidatorList.h>
#include <ripple/app/paths/PathRequests.h>
#include <ripple/basics/contract.h>
@@ -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

View File

@@ -20,6 +20,7 @@
#include <BeastConfig.h>
#include <ripple/app/main/Application.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/app/consensus/RCLValidations.h>
#include <ripple/app/main/DBInit.h>
#include <ripple/app/main/BasicApp.h>
#include <ripple/app/main/Tuning.h>
@@ -328,7 +329,7 @@ public:
std::unique_ptr <AmendmentTable> m_amendmentTable;
std::unique_ptr <LoadFeeTrack> mFeeTrack;
std::unique_ptr <HashRouter> mHashRouter;
std::unique_ptr <Validations> mValidations;
RCLValidations mValidations;
std::unique_ptr <LoadManager> m_loadManager;
std::unique_ptr <TxQ> txQ_;
DeadlineTimer m_sweepTimer;
@@ -471,7 +472,8 @@ public:
, mHashRouter (std::make_unique<HashRouter>(
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();

View File

@@ -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 <SHAMapHash, Blob>;
template <class StalePolicy, class Validation, class MutexType>
class Validations;
class RCLValidation;
class RCLValidationsPolicy;
using RCLValidations =
Validations<RCLValidationsPolicy, RCLValidation, std::mutex>;
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;

View File

@@ -21,7 +21,7 @@
#define RIPPLE_APP_MISC_AMENDMENTTABLE_H_INCLUDED
#include <ripple/app/ledger/Ledger.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/protocol/STValidation.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/Protocol.h>
@@ -78,7 +78,7 @@ public:
NetClock::time_point closeTime,
std::set <uint256> const& enabledAmendments,
majorityAmendments_t const& majorityAmendments,
ValidationSet const& valSet) = 0;
std::vector<STValidation::pointer> 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 <ReadView const> const& lastClosedLedger,
ValidationSet const& parentValidations,
std::vector<STValidation::pointer> const& parentValidations,
std::shared_ptr<SHAMap> const& initialPosition)
{
// Ask implementation what to do

View File

@@ -22,7 +22,7 @@
#include <ripple/ledger/ReadView.h>
#include <ripple/shamap/SHAMap.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/protocol/STValidation.h>
#include <ripple/basics/BasicConfig.h>
#include <ripple/protocol/SystemParameters.h>
@@ -72,7 +72,7 @@ public:
virtual
void
doVoting (std::shared_ptr<ReadView const> const& lastClosedLedger,
ValidationSet const& parentValidations,
std::vector<STValidation::pointer> const& parentValidations,
std::shared_ptr<SHAMap> const& initialPosition) = 0;
};

View File

@@ -21,7 +21,7 @@
#include <ripple/protocol/st.h>
#include <ripple/app/misc/FeeVote.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/protocol/STValidation.h>
#include <ripple/basics/BasicConfig.h>
#include <ripple/beast/utility/Journal.h>
@@ -103,7 +103,7 @@ public:
void
doVoting (std::shared_ptr<ReadView const> const& lastClosedLedger,
ValidationSet const& parentValidations,
std::vector<STValidation::pointer> const& parentValidations,
std::shared_ptr<SHAMap> const& initialPosition) override;
};
@@ -149,7 +149,7 @@ FeeVoteImpl::doValidation(
void
FeeVoteImpl::doVoting(
std::shared_ptr<ReadView const> const& lastClosedLedger,
ValidationSet const& set,
std::vector<STValidation::pointer> const& set,
std::shared_ptr<SHAMap> const& initialPosition)
{
// LCL must be flag ledger
@@ -164,33 +164,31 @@ FeeVoteImpl::doVoting(
detail::VotableInteger<std::uint32_t> 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
{

View File

@@ -21,6 +21,7 @@
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/consensus/Consensus.h>
#include <ripple/app/consensus/RCLConsensus.h>
#include <ripple/app/consensus/RCLValidations.h>
#include <ripple/app/ledger/AcceptedLedger.h>
#include <ripple/app/ledger/InboundLedgers.h>
#include <ripple/app/ledger/LedgerMaster.h>
@@ -1301,17 +1302,17 @@ bool NetworkOPsImp::checkLastClosedLedger (
hash_map<uint256, ValidationCount> 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 ()

View File

@@ -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 <BeastConfig.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/consensus/LedgerTiming.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/app/misc/ValidatorList.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/basics/chrono.h>
#include <ripple/core/JobQueue.h>
#include <ripple/core/TimeKeeper.h>
#include <memory>
#include <mutex>
#include <thread>
namespace ripple {
class ValidationsImp : public Validations
{
private:
using LockType = std::mutex;
using ScopedLockType = std::lock_guard <LockType>;
using ScopedUnlockType = GenericScopedUnlock <LockType>;
Application& app_;
std::mutex mutable mLock;
TaggedCache<uint256, ValidationSet> mValidations;
ValidationSet mCurrentValidations;
std::vector<STValidation::pointer> mStaleValidations;
bool mWriting;
beast::Journal j_;
private:
std::shared_ptr<ValidationSet> findCreateSet (uint256 const& ledgerHash)
{
auto j = mValidations.fetch (ledgerHash);
if (!j)
{
j = std::make_shared<ValidationSet> ();
mValidations.canonicalize (ledgerHash, j);
}
return j;
}
std::shared_ptr<ValidationSet> 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 <std::uint64_t>
fees (uint256 const& ledger, std::uint64_t base) override
{
std::vector <std::uint64_t> result;
std::lock_guard <std::mutex> 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<STValidation::pointer> getCurrentTrustedValidations () override
{
std::list<STValidation::pointer> 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<PublicKey> getCurrentPublicKeys () override
{
hash_set<PublicKey> 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<NetClock::time_point>
getValidationTimes (uint256 const& hash) override
{
std::vector <NetClock::time_point> 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<STValidation::pointer> 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<std::uint64_t> 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<const char*>(
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 <Validations> make_Validations (Application& app)
{
return std::make_unique <ValidationsImp> (app);
}
} // ripple

View File

@@ -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 <ripple/app/main/Application.h>
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/STValidation.h>
#include <memory>
#include <vector>
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<PublicKey, STValidation::pointer>;
using ValidationCounter = std::pair<int, NodeID>;
using LedgerToValidationCounter = hash_map<uint256, ValidationCounter>;
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 <std::uint64_t>
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<PublicKey> 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<NetClock::time_point> getValidationTimes (
uint256 const& ledger) = 0;
virtual std::list <STValidation::pointer>
getCurrentTrustedValidations () = 0;
virtual void flush () = 0;
virtual void sweep () = 0;
};
extern
std::unique_ptr<Validations>
make_Validations(Application& app);
} // ripple
#endif

View File

@@ -20,7 +20,7 @@
#include <BeastConfig.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/protocol/STValidation.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/JsonFields.h>
@@ -207,7 +207,7 @@ public:
NetClock::time_point closeTime,
std::set<uint256> const& enabledAmendments,
majorityAmendments_t const& majorityAmendments,
ValidationSet const& validations) override;
std::vector<STValidation::pointer> const& validations) override;
};
//------------------------------------------------------------------------------
@@ -411,7 +411,7 @@ AmendmentTableImpl::doVoting (
NetClock::time_point closeTime,
std::set<uint256> const& enabledAmendments,
majorityAmendments_t const& majorityAmendments,
ValidationSet const& valSet)
std::vector<STValidation::pointer> const& valSet)
{
JLOG (j_.trace()) <<
"voting at " << closeTime.time_since_epoch().count() <<
@@ -422,16 +422,16 @@ AmendmentTableImpl::doVoting (
auto vote = std::make_unique <AmendmentSet> ();
// 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<uint256> 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 ());
}

View File

@@ -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;

View File

@@ -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 <ripple/basics/Log.h>
#include <ripple/basics/UnorderedContainers.h>
#include <ripple/basics/chrono.h>
#include <ripple/beast/container/aged_container_utility.h>
#include <ripple/beast/container/aged_unordered_map.h>
#include <ripple/beast/utility/Journal.h>
#include <ripple/beast/utility/Zero.h>
#include <boost/optional.hpp>
#include <mutex>
#include <utility>
#include <vector>
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<NodeKey,Validation> && 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 StalePolicy, class Validation, class MutexType>
class Validations
{
template <typename T>
using decay_result_t = std::decay_t<std::result_of_t<T>>;
using WrappedValidationType =
decay_result_t<decltype (&Validation::unwrap)(Validation)>;
using LedgerID =
decay_result_t<decltype (&Validation::ledgerID)(Validation)>;
using NodeKey = decay_result_t<decltype (&Validation::key)(Validation)>;
using NodeID = decay_result_t<decltype (&Validation::nodeID)(Validation)>;
using ValidationMap = hash_map<NodeKey, Validation>;
using ScopedLock = std::lock_guard<MutexType>;
// 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 <class Pre, class F>
void
current(boost::optional<NetClock::time_point> 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 <class Pre, class F>
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 <class... Ts>
Validations(
ValidationParms const& p,
beast::abstract_clock<std::chrono::steady_clock>& c,
beast::Journal j,
Ts&&... ts)
: byLedger_(c), parms_(p), j_(j), stalePolicy_(std::forward<Ts>(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<Validation> 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<LedgerID, ValidationCounts>
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<LedgerID, ValidationCounts> 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<WrappedValidationType>
currentTrusted()
{
std::vector<WrappedValidationType> 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<NodeKey>
getCurrentPublicKeys()
{
hash_set<NodeKey> 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<WrappedValidationType>
getTrustedForLedger(LedgerID const& ledgerID)
{
std::vector<WrappedValidationType> 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<NetClock::time_point>
getTrustedValidationTimes(LedgerID const& ledgerID)
{
std::vector<NetClock::time_point> 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<std::uint32_t>
fees(LedgerID const& ledgerID, std::uint32_t baseFee)
{
std::vector<std::uint32_t> res;
byLedger(
ledgerID,
[&](std::size_t numValidations) { res.reserve(numValidations); },
[&](NodeKey const&, Validation const& v) {
if (v.trusted())
{
boost::optional<std::uint32_t> 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<NodeKey, Validation> flushed;
using std::swap;
{
ScopedLock lock{mutex_};
swap(flushed, current_);
}
stalePolicy_.flush(std::move(flushed));
JLOG(j_.debug()) << "Validations flushed";
}
};
} // namespace ripple
#endif

View File

@@ -29,7 +29,7 @@
#include <ripple/app/misc/LoadFeeTrack.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/app/misc/Transaction.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/app/consensus/RCLValidations.h>
#include <ripple/app/misc/ValidatorList.h>
#include <ripple/app/tx/apply.h>
#include <ripple/protocol/digest.h>
@@ -1576,7 +1576,10 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMValidation> 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;

View File

@@ -20,3 +20,5 @@
#include <ripple/app/consensus/RCLConsensus.cpp>
#include <ripple/app/consensus/RCLCxPeerPos.cpp>
#include <ripple/app/consensus/RCLValidations.cpp>

View File

@@ -24,7 +24,6 @@
#include <ripple/app/misc/HashRouter.cpp>
#include <ripple/app/misc/NetworkOPs.cpp>
#include <ripple/app/misc/SHAMapStoreImp.cpp>
#include <ripple/app/misc/Validations.cpp>
#include <ripple/app/misc/impl/AccountTxPaging.cpp>
#include <ripple/app/misc/impl/AmendmentTable.cpp>

View File

@@ -378,7 +378,7 @@ public:
auto const roundTime = weekTime (week);
// Build validations
ValidationSet validations;
std::vector<STValidation::pointer> 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);

View File

@@ -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 <BeastConfig.h>
#include <ripple/beast/clock/manual_clock.h>
#include <ripple/beast/unit_test.h>
#include <ripple/consensus/Validations.h>
#include <tuple>
#include <type_traits>
#include <vector>
#include <memory>
namespace ripple {
namespace test {
class Validations_test : public beast::unit_test::suite
{
using clock_type = beast::abstract_clock<std::chrono::steady_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 <class Hasher>
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<std::uint32_t> loadFee_;
// Shared prevID to ensure value is shared amongst instances
std::shared_ptr<ID> prevID_;
public:
Validation() : prevID_(std::make_shared<ID>(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<std::uint32_t>
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<NetClock::duration>(
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<std::uint32_t> 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<Validation> stale;
hash_map<std::string, Validation> 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<std::string, Validation>&& remaining)
{
staleData_.flushed = std::move(remaining);
}
};
// Specialize generic Validations using the above types
using TestValidations =
Validations<StalePolicy, Validation, DummyMutex>;
// 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<std::chrono::steady_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 <class... Ts>
std::enable_if_t<(sizeof...(Ts) > 1), AddOutcome>
add(Node const& n, Ts&&... ts)
{
return add(n, n.validation(std::forward<Ts>(ts)...));
}
TestValidations&
vals()
{
return tv_;
}
Node
makeNode()
{
return Node(nextNodeId_++, clock_);
}
ValidationParms
parms() const
{
return p_;
}
auto&
clock()
{
return clock_;
}
std::vector<Validation> const&
stale() const
{
return staleData_.stale;
}
hash_map<std::string, Validation> const&
flushed() const
{
return staleData_.flushed;
}
};
void
testAddValidation()
{
// Test adding current,stale,repeat,sameSeq validations
using namespace std::chrono_literals;
TestHarness harness;
Node a = harness.makeNode();
{
{
auto const v = a.validation(Seq{1}, ID{1});
// Add a current validation
BEAST_EXPECT(AddOutcome::current == harness.add(a, v));
// Re-adding is repeat
BEAST_EXPECT(AddOutcome::repeat == harness.add(a, v));
}
{
harness.clock().advance(1s);
// Replace with a new validation and ensure the old one is stale
BEAST_EXPECT(harness.stale().empty());
BEAST_EXPECT(
AddOutcome::current == harness.add(a, Seq{2}, ID{2}));
BEAST_EXPECT(harness.stale().size() == 1);
BEAST_EXPECT(harness.stale()[0].ledgerID() == 1);
}
{
// Test the node changing signing key, then reissuing a ledger
// Confirm old ledger on hand, but not new ledger
BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{2}) == 1);
BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{20}) == 0);
// Issue a new signing key and re-issue the validation with a
// new ID but the same sequence number
a.advanceKey();
BEAST_EXPECT(
AddOutcome::sameSeq == harness.add(a, Seq{2}, ID{20}));
// Old ID should be gone ...
BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{2}) == 0);
BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{20}) == 1);
// ... and the previous ID set to the old ID
{
auto trustedVals =
harness.vals().getTrustedForLedger(ID{20});
BEAST_EXPECT(trustedVals.size() == 1);
BEAST_EXPECT(trustedVals[0].key() == a.currKey());
BEAST_EXPECT(trustedVals[0].getPreviousLedgerID() == ID{2});
}
// A new key, but re-issue a validation with the same ID and
// Sequence
a.advanceKey();
BEAST_EXPECT(
AddOutcome::sameSeq == harness.add(a, Seq{2}, ID{20}));
// Ensure the old prevLedgerID was retained
{
auto trustedVals =
harness.vals().getTrustedForLedger(ID{20});
BEAST_EXPECT(trustedVals.size() == 1);
BEAST_EXPECT(trustedVals[0].key() == a.currKey());
BEAST_EXPECT(trustedVals[0].getPreviousLedgerID() == ID{2});
}
}
{
// Processing validations out of order should ignore the older
harness.clock().advance(2s);
auto const val3 = a.validation(Seq{3}, ID{3});
harness.clock().advance(4s);
auto const val4 = a.validation(Seq{4}, ID{4});
BEAST_EXPECT(AddOutcome::current == harness.add(a, val4));
BEAST_EXPECT(AddOutcome::stale == harness.add(a, val3));
// re-issued should not be added
auto const val4reissue = a.validation(Seq{4}, ID{44});
BEAST_EXPECT(AddOutcome::stale == harness.add(a, val4reissue));
}
{
// Process validations out of order with shifted times
// flush old validations
harness.clock().advance(1h);
// Establish a new current validation
BEAST_EXPECT(
AddOutcome::current == harness.add(a, Seq{8}, ID{8}));
// Process a validation that has "later" seq but early sign time
BEAST_EXPECT(
AddOutcome::stale ==
harness.add(a, Seq{9}, ID{9}, -1s, -1s));
// Process a validation that has an "earlier" seq but later sign time
BEAST_EXPECT(
AddOutcome::current ==
harness.add(a, Seq{7}, ID{7}, 1s, 1s));
}
{
// Test stale on arrival validations
harness.clock().advance(1h);
BEAST_EXPECT(
AddOutcome::stale ==
harness.add(
a,
Seq{15},
ID{15},
-harness.parms().validationCURRENT_EARLY,
0s));
BEAST_EXPECT(
AddOutcome::stale ==
harness.add(
a,
Seq{15},
ID{15},
harness.parms().validationCURRENT_WALL,
0s));
BEAST_EXPECT(
AddOutcome::stale ==
harness.add(
a,
Seq{15},
ID{15},
0s,
harness.parms().validationCURRENT_LOCAL));
}
}
}
void
testOnStale()
{
// Verify validation becomes stale based solely on time passing
TestHarness harness;
Node a = harness.makeNode();
BEAST_EXPECT(AddOutcome::current == harness.add(a, Seq{1}, ID{1}));
harness.vals().currentTrusted();
BEAST_EXPECT(harness.stale().empty());
harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
// trigger iteration over current
harness.vals().currentTrusted();
BEAST_EXPECT(harness.stale().size() == 1);
BEAST_EXPECT(harness.stale()[0].ledgerID() == 1);
}
void
testGetNodesAfter()
{
// Test getting number of nodes working on a validation following
// a prescribed one
using namespace std::chrono_literals;
TestHarness harness;
Node a = harness.makeNode(), b = harness.makeNode(),
c = harness.makeNode(), d = harness.makeNode();
c.untrust();
// first round a,b,c agree, d has differing id
for (auto const& node : {a, b, c})
BEAST_EXPECT(
AddOutcome::current == harness.add(node, Seq{1}, ID{1}));
BEAST_EXPECT(AddOutcome::current == harness.add(d, Seq{1}, ID{10}));
// Nothing past ledger 1 yet
BEAST_EXPECT(harness.vals().getNodesAfter(ID{1}) == 0);
harness.clock().advance(5s);
// a and b have the same prior id, but b has a different current id
// c is untrusted but on the same prior id
// d has a different prior id
BEAST_EXPECT(AddOutcome::current == harness.add(a, Seq{2}, ID{2}));
BEAST_EXPECT(AddOutcome::current == harness.add(b, Seq{2}, ID{20}));
BEAST_EXPECT(AddOutcome::current == harness.add(c, Seq{2}, ID{2}));
BEAST_EXPECT(AddOutcome::current == harness.add(d, Seq{2}, ID{2}));
BEAST_EXPECT(harness.vals().getNodesAfter(ID{1}) == 2);
}
void
testCurrentTrusted()
{
// Test getting current trusted validations
using namespace std::chrono_literals;
TestHarness harness;
Node a = harness.makeNode(), b = harness.makeNode();
b.untrust();
BEAST_EXPECT(AddOutcome::current == harness.add(a, Seq{1}, ID{1}));
BEAST_EXPECT(AddOutcome::current == harness.add(b, Seq{1}, ID{3}));
// Only a is trusted
BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
BEAST_EXPECT(harness.vals().currentTrusted()[0].ledgerID() == ID{1});
BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == Seq{1});
harness.clock().advance(3s);
for (auto const& node : {a, b})
BEAST_EXPECT(
AddOutcome::current == harness.add(node, Seq{2}, ID{2}));
// New validation for a
BEAST_EXPECT(harness.vals().currentTrusted().size() == 1);
BEAST_EXPECT(harness.vals().currentTrusted()[0].ledgerID() == ID{2});
BEAST_EXPECT(harness.vals().currentTrusted()[0].seq() == Seq{2});
// Pass enough time for it to go stale
harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
BEAST_EXPECT(harness.vals().currentTrusted().empty());
}
void
testGetCurrentPublicKeys()
{
// Test getting current keys validations
using namespace std::chrono_literals;
TestHarness harness;
Node a = harness.makeNode(), b = harness.makeNode();
b.untrust();
for (auto const& node : {a, b})
BEAST_EXPECT(
AddOutcome::current == harness.add(node, Seq{1}, ID{1}));
{
hash_set<std::string> const expectedKeys = {a.masterKey(),
b.masterKey()};
BEAST_EXPECT(
harness.vals().getCurrentPublicKeys() == expectedKeys);
}
harness.clock().advance(3s);
// Change keys
a.advanceKey();
b.advanceKey();
for (auto const& node : {a, b})
BEAST_EXPECT(
AddOutcome::current == harness.add(node, Seq{2}, ID{2}));
{
hash_set<std::string> const expectedKeys = {a.masterKey(),
b.masterKey()};
BEAST_EXPECT(
harness.vals().getCurrentPublicKeys() == expectedKeys);
}
// Pass enough time for them to go stale
harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
BEAST_EXPECT(harness.vals().getCurrentPublicKeys().empty());
}
void
testCurrentTrustedDistribution()
{
// Test the trusted distribution calculation, including ledger slips
// and sequence cutoffs
using namespace std::chrono_literals;
TestHarness harness;
Node baby = harness.makeNode(), papa = harness.makeNode(),
mama = harness.makeNode(), goldilocks = harness.makeNode();
goldilocks.untrust();
// Stagger the validations around sequence 2
// papa on seq 1 is behind
// baby on seq 2 is just right
// mama on seq 3 is ahead
// goldilocks on seq 2, but is not trusted
for (auto const& node : {baby, papa, mama, goldilocks})
BEAST_EXPECT(
AddOutcome::current == harness.add(node, Seq{1}, ID{1}));
harness.clock().advance(1s);
for (auto const& node : {baby, mama, goldilocks})
BEAST_EXPECT(
AddOutcome::current == harness.add(node, Seq{2}, ID{2}));
harness.clock().advance(1s);
BEAST_EXPECT(AddOutcome::current == harness.add(mama, Seq{3}, ID{3}));
{
// Allow slippage that treats all trusted as the current ledger
auto res = harness.vals().currentTrustedDistribution(
ID{2}, // Current ledger
ID{1}, // Prior ledger
Seq{0}); // No cutoff
BEAST_EXPECT(res.size() == 1);
BEAST_EXPECT(res[ID{2}].count == 3);
BEAST_EXPECT(res[ID{2}].highNode == mama.nodeID());
}
{
// Don't allow slippage back for prior ledger
auto res = harness.vals().currentTrustedDistribution(
ID{2}, // Current ledger
ID{0}, // No prior ledger
Seq{0}); // No cutoff
BEAST_EXPECT(res.size() == 2);
BEAST_EXPECT(res[ID{2}].count == 2);
BEAST_EXPECT(res[ID{2}].highNode == mama.nodeID());
BEAST_EXPECT(res[ID{1}].count == 1);
BEAST_EXPECT(res[ID{1}].highNode == papa.nodeID());
}
{
// Don't allow any slips
auto res = harness.vals().currentTrustedDistribution(
ID{0}, // No current ledger
ID{0}, // No prior ledger
Seq{0}); // No cutoff
BEAST_EXPECT(res.size() == 3);
BEAST_EXPECT(res[ID{1}].count == 1);
BEAST_EXPECT(res[ID{1}].highNode == papa.nodeID());
BEAST_EXPECT(res[ID{2}].count == 1);
BEAST_EXPECT(res[ID{2}].highNode == baby.nodeID());
BEAST_EXPECT(res[ID{3}].count == 1);
BEAST_EXPECT(res[ID{3}].highNode == mama.nodeID());
}
{
// Cutoff old sequence numbers
auto res = harness.vals().currentTrustedDistribution(
ID{2}, // current ledger
ID{1}, // prior ledger
Seq{2}); // Only sequence 2 or later
BEAST_EXPECT(res.size() == 1);
BEAST_EXPECT(res[ID{2}].count == 2);
BEAST_EXPECT(res[ID{2}].highNode == mama.nodeID());
}
}
void
testTrustedByLedgerFunctions()
{
// Test the Validations functions that calculate a value by ledger ID
using namespace std::chrono_literals;
// Several Validations functions return a set of values associated
// with trusted ledgers sharing the same ledger ID. The tests below
// exercise this logic by saving the set of trusted Validations, and
// verifying that the Validations member functions all calculate the
// proper transformation of the available ledgers.
TestHarness harness;
Node a = harness.makeNode(), b = harness.makeNode(),
c = harness.makeNode(), d = harness.makeNode();
c.untrust();
// Mix of load fees
a.setLoadFee(12);
b.setLoadFee(1);
c.setLoadFee(12);
hash_map<ID, std::vector<Validation>> trustedValidations;
//----------------------------------------------------------------------
// checkers
auto sorted = [](auto vec) {
std::sort(vec.begin(), vec.end());
return vec;
};
auto compare = [&]() {
for (auto& it : trustedValidations)
{
auto const& id = it.first;
auto const& expectedValidations = it.second;
BEAST_EXPECT(
harness.vals().numTrustedForLedger(id) ==
expectedValidations.size());
BEAST_EXPECT(
sorted(harness.vals().getTrustedForLedger(id)) ==
sorted(expectedValidations));
std::vector<NetClock::time_point> expectedTimes;
std::uint32_t baseFee = 0;
std::vector<uint32_t> expectedFees;
for (auto const& val : expectedValidations)
{
expectedTimes.push_back(val.signTime());
expectedFees.push_back(val.loadFee().value_or(baseFee));
}
BEAST_EXPECT(
sorted(harness.vals().fees(id, baseFee)) ==
sorted(expectedFees));
BEAST_EXPECT(
sorted(harness.vals().getTrustedValidationTimes(id)) ==
sorted(expectedTimes));
}
};
//----------------------------------------------------------------------
// Add a dummy ID to cover unknown ledger identifiers
trustedValidations[ID{100}] = {};
// first round a,b,c agree, d differs
for (auto const& node : {a, b, c})
{
auto const val = node.validation(Seq{1}, ID{1});
BEAST_EXPECT(AddOutcome::current == harness.add(node, val));
if (val.trusted())
trustedValidations[val.ledgerID()].emplace_back(val);
}
{
auto const val = d.validation(Seq{1}, ID{11});
BEAST_EXPECT(AddOutcome::current == harness.add(d, val));
trustedValidations[val.ledgerID()].emplace_back(val);
}
harness.clock().advance(5s);
// second round, a,b,c move to ledger 2, d now thinks ledger 1
for (auto const& node : {a, b, c})
{
auto const val = node.validation(Seq{2}, ID{2});
BEAST_EXPECT(AddOutcome::current == harness.add(node, val));
if (val.trusted())
trustedValidations[val.ledgerID()].emplace_back(val);
}
{
auto const val = d.validation(Seq{2}, ID{1});
BEAST_EXPECT(AddOutcome::current == harness.add(d, val));
trustedValidations[val.ledgerID()].emplace_back(val);
}
compare();
}
void
testExpire()
{
// Verify expiring clears out validations stored by ledger
TestHarness harness;
Node a = harness.makeNode();
BEAST_EXPECT(AddOutcome::current == harness.add(a, Seq{1}, ID{1}));
BEAST_EXPECT(harness.vals().numTrustedForLedger(ID{1}));
harness.clock().advance(harness.parms().validationSET_EXPIRES);
harness.vals().expire();
BEAST_EXPECT(!harness.vals().numTrustedForLedger(ID{1}));
}
void
testFlush()
{
// Test final flush of validations
using namespace std::chrono_literals;
TestHarness harness;
Node a = harness.makeNode(), b = harness.makeNode(),
c = harness.makeNode();
c.untrust();
hash_map<std::string, Validation> expected;
Validation staleA;
for (auto const& node : {a, b, c})
{
auto const val = node.validation(Seq{1}, ID{1});
BEAST_EXPECT(AddOutcome::current == harness.add(node, val));
if (node.nodeID() == a.nodeID())
{
staleA = val;
}
else
expected[node.masterKey()] = val;
}
// Send in a new validation for a, saving the new one into the expected
// map after setting the proper prior ledger ID it replaced
harness.clock().advance(1s);
auto newVal = a.validation(Seq{2}, ID{2});
BEAST_EXPECT(AddOutcome::current == harness.add(a, newVal));
newVal.setPreviousLedgerID(staleA.ledgerID());
expected[a.masterKey()] = newVal;
// Now flush
harness.vals().flush();
// Original a validation was stale
BEAST_EXPECT(harness.stale().size() == 1);
BEAST_EXPECT(harness.stale()[0] == staleA);
BEAST_EXPECT(harness.stale()[0].nodeID() == a.nodeID());
auto const& flushed = harness.flushed();
BEAST_EXPECT(flushed == expected);
}
void
run() override
{
testAddValidation();
testOnStale();
testGetNodesAfter();
testCurrentTrusted();
testGetCurrentPublicKeys();
testCurrentTrustedDistribution();
testTrustedByLedgerFunctions();
testExpire();
testFlush();
}
};
BEAST_DEFINE_TESTSUITE(Validations, consensus, ripple);
} // namespace test
} // namespace ripple

View File

@@ -134,6 +134,7 @@ public:
return b.second < a.second;
});
if(top.size() > 10)
top.resize(10);
os_ << "Longest suite times:\n";

View File

@@ -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,3 +19,4 @@
#include <test/consensus/Consensus_test.cpp>
#include <test/consensus/LedgerTiming_test.cpp>
#include <test/consensus/Validations_test.cpp>