mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
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:
@@ -867,6 +867,12 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\consensus\RCLCxTx.h">
|
<ClInclude Include="..\..\src\ripple\app\consensus\RCLCxTx.h">
|
||||||
</ClInclude>
|
</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 Include="..\..\src\ripple\app\ledger\AbstractFetchPackContainer.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\ledger\AcceptedLedger.cpp">
|
<ClCompile Include="..\..\src\ripple\app\ledger\AcceptedLedger.cpp">
|
||||||
@@ -1125,12 +1131,6 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
|
||||||
</ClInclude>
|
</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 Include="..\..\src\ripple\app\misc\ValidatorList.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorSite.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorSite.h">
|
||||||
@@ -1871,6 +1871,8 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
|
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\consensus\Validations.h">
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\core\Config.h">
|
<ClInclude Include="..\..\src\ripple\core\Config.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\core\ConfigSections.h">
|
<ClInclude Include="..\..\src\ripple\core\ConfigSections.h">
|
||||||
@@ -4455,6 +4457,10 @@
|
|||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</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">
|
<ClCompile Include="..\..\src\test\core\Config_test.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -1401,6 +1401,12 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\consensus\RCLCxTx.h">
|
<ClInclude Include="..\..\src\ripple\app\consensus\RCLCxTx.h">
|
||||||
<Filter>ripple\app\consensus</Filter>
|
<Filter>ripple\app\consensus</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ripple\app\ledger\AbstractFetchPackContainer.h">
|
||||||
<Filter>ripple\app\ledger</Filter>
|
<Filter>ripple\app\ledger</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -1665,12 +1671,6 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\TxQ.h">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorList.h">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -2517,6 +2517,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
|
<ClInclude Include="..\..\src\ripple\consensus\LedgerTiming.h">
|
||||||
<Filter>ripple\consensus</Filter>
|
<Filter>ripple\consensus</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\consensus\Validations.h">
|
||||||
|
<Filter>ripple\consensus</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\core\Config.h">
|
<ClInclude Include="..\..\src\ripple\core\Config.h">
|
||||||
<Filter>ripple\core</Filter>
|
<Filter>ripple\core</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -5217,6 +5220,9 @@
|
|||||||
<ClCompile Include="..\..\src\test\consensus\LedgerTiming_test.cpp">
|
<ClCompile Include="..\..\src\test\consensus\LedgerTiming_test.cpp">
|
||||||
<Filter>test\consensus</Filter>
|
<Filter>test\consensus</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\test\consensus\Validations_test.cpp">
|
||||||
|
<Filter>test\consensus</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\test\core\Config_test.cpp">
|
<ClCompile Include="..\..\src\test\core\Config_test.cpp">
|
||||||
<Filter>test\core</Filter>
|
<Filter>test\core</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ INPUT = \
|
|||||||
../src/ripple/consensus/ConsensusProposal.h \
|
../src/ripple/consensus/ConsensusProposal.h \
|
||||||
../src/ripple/consensus/DisputedTx.h \
|
../src/ripple/consensus/DisputedTx.h \
|
||||||
../src/ripple/consensus/LedgerTiming.h \
|
../src/ripple/consensus/LedgerTiming.h \
|
||||||
|
../src/ripple/consensus/Validations.h \
|
||||||
../src/ripple/app/consensus/RCLCxTx.h \
|
../src/ripple/app/consensus/RCLCxTx.h \
|
||||||
../src/ripple/app/consensus/RCLCxLedger.h \
|
../src/ripple/app/consensus/RCLCxLedger.h \
|
||||||
../src/ripple/app/consensus/RCLConsensus.h \
|
../src/ripple/app/consensus/RCLConsensus.h \
|
||||||
@@ -120,6 +121,7 @@ INPUT = \
|
|||||||
../src/ripple/app/tx/apply.h \
|
../src/ripple/app/tx/apply.h \
|
||||||
../src/ripple/app/tx/applySteps.h \
|
../src/ripple/app/tx/applySteps.h \
|
||||||
../src/ripple/app/tx/impl/InvariantCheck.h \
|
../src/ripple/app/tx/impl/InvariantCheck.h \
|
||||||
|
../src/ripple/app/consensus/RCLValidations.h \
|
||||||
|
|
||||||
INPUT_ENCODING = UTF-8
|
INPUT_ENCODING = UTF-8
|
||||||
FILE_PATTERNS =
|
FILE_PATTERNS =
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
#include <ripple/app/consensus/RCLConsensus.h>
|
#include <ripple/app/consensus/RCLConsensus.h>
|
||||||
|
#include <ripple/app/consensus/RCLValidations.h>
|
||||||
#include <ripple/app/ledger/InboundLedgers.h>
|
#include <ripple/app/ledger/InboundLedgers.h>
|
||||||
#include <ripple/app/ledger/InboundTransactions.h>
|
#include <ripple/app/ledger/InboundTransactions.h>
|
||||||
#include <ripple/app/ledger/LedgerMaster.h>
|
#include <ripple/app/ledger/LedgerMaster.h>
|
||||||
@@ -222,7 +223,7 @@ RCLConsensus::hasOpenTransactions() const
|
|||||||
std::size_t
|
std::size_t
|
||||||
RCLConsensus::proposersValidated(LedgerHash const& h) const
|
RCLConsensus::proposersValidated(LedgerHash const& h) const
|
||||||
{
|
{
|
||||||
return app_.getValidations().getTrustedValidationCount(h);
|
return app_.getValidations().numTrustedForLedger(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
@@ -244,8 +245,9 @@ RCLConsensus::getPrevLedger(
|
|||||||
|
|
||||||
// Get validators that are on our ledger, or "close" to being on
|
// Get validators that are on our ledger, or "close" to being on
|
||||||
// our ledger.
|
// our ledger.
|
||||||
auto vals = app_.getValidations().getCurrentValidations(
|
auto vals =
|
||||||
ledgerID, parentID, ledgerMaster_.getValidLedgerIndex());
|
app_.getValidations().currentTrustedDistribution(
|
||||||
|
ledgerID, parentID, ledgerMaster_.getValidLedgerIndex());
|
||||||
|
|
||||||
uint256 netLgr = ledgerID;
|
uint256 netLgr = ledgerID;
|
||||||
int netLgrCount = 0;
|
int netLgrCount = 0;
|
||||||
@@ -253,11 +255,11 @@ RCLConsensus::getPrevLedger(
|
|||||||
{
|
{
|
||||||
// Switch to ledger supported by more peers
|
// Switch to ledger supported by more peers
|
||||||
// Or stick with ours on a tie
|
// Or stick with ours on a tie
|
||||||
if ((it.second.first > netLgrCount) ||
|
if ((it.second.count > netLgrCount) ||
|
||||||
((it.second.first == netLgrCount) && (it.first == ledgerID)))
|
((it.second.count== netLgrCount) && (it.first == ledgerID)))
|
||||||
{
|
{
|
||||||
netLgr = it.first;
|
netLgr = it.first;
|
||||||
netLgrCount = it.second.first;
|
netLgrCount = it.second.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +271,7 @@ RCLConsensus::getPrevLedger(
|
|||||||
if (auto stream = j_.debug())
|
if (auto stream = j_.debug())
|
||||||
{
|
{
|
||||||
for (auto& it : vals)
|
for (auto& it : vals)
|
||||||
stream << "V: " << it.first << ", " << it.second.first;
|
stream << "V: " << it.first << ", " << it.second.count;
|
||||||
stream << getJson(true);
|
stream << getJson(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,14 +319,10 @@ RCLConsensus::onClose(
|
|||||||
{
|
{
|
||||||
// previous ledger was flag ledger, add pseudo-transactions
|
// previous ledger was flag ledger, add pseudo-transactions
|
||||||
auto const validations =
|
auto const validations =
|
||||||
app_.getValidations().getValidations(prevLedger->info().parentHash);
|
app_.getValidations().getTrustedForLedger (
|
||||||
|
prevLedger->info().parentHash);
|
||||||
|
|
||||||
std::size_t const count = std::count_if(
|
if (validations.size() >= app_.validators ().quorum ())
|
||||||
validations.begin(), validations.end(), [](auto const& v) {
|
|
||||||
return v.second->isTrusted();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (count >= app_.validators().quorum())
|
|
||||||
{
|
{
|
||||||
feeVote_->doVoting(prevLedger, validations, initialSet);
|
feeVote_->doVoting(prevLedger, validations, initialSet);
|
||||||
app_.getAmendmentTable().doVoting(
|
app_.getAmendmentTable().doVoting(
|
||||||
@@ -843,7 +841,7 @@ RCLConsensus::validate(RCLCxLedger const& ledger, bool proposing)
|
|||||||
v->setTrusted();
|
v->setTrusted();
|
||||||
// suppress it if we receive it - FIXME: wrong suppression
|
// suppress it if we receive it - FIXME: wrong suppression
|
||||||
app_.getHashRouter().addSuppression(signingHash);
|
app_.getHashRouter().addSuppression(signingHash);
|
||||||
app_.getValidations().addValidation(v, "local");
|
handleNewValidation(app_, v, "local");
|
||||||
Blob validation = v->getSerialized();
|
Blob validation = v->getSerialized();
|
||||||
protocol::TMValidation val;
|
protocol::TMValidation val;
|
||||||
val.set_validation(&validation[0], validation.size());
|
val.set_validation(&validation[0], validation.size());
|
||||||
|
|||||||
280
src/ripple/app/consensus/RCLValidations.cpp
Normal file
280
src/ripple/app/consensus/RCLValidations.cpp
Normal 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
|
||||||
214
src/ripple/app/consensus/RCLValidations.h
Normal file
214
src/ripple/app/consensus/RCLValidations.h
Normal 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
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
#include <ripple/app/misc/SHAMapStore.h>
|
#include <ripple/app/misc/SHAMapStore.h>
|
||||||
#include <ripple/app/misc/Transaction.h>
|
#include <ripple/app/misc/Transaction.h>
|
||||||
#include <ripple/app/misc/TxQ.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/misc/ValidatorList.h>
|
||||||
#include <ripple/app/paths/PathRequests.h>
|
#include <ripple/app/paths/PathRequests.h>
|
||||||
#include <ripple/basics/contract.h>
|
#include <ripple/basics/contract.h>
|
||||||
@@ -200,7 +200,7 @@ LedgerMaster::setValidLedger(
|
|||||||
|
|
||||||
if (! standalone_)
|
if (! standalone_)
|
||||||
{
|
{
|
||||||
times = app_.getValidations().getValidationTimes(
|
times = app_.getValidations().getTrustedValidationTimes(
|
||||||
l->info().hash);
|
l->info().hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,7 +700,7 @@ LedgerMaster::checkAccept (uint256 const& hash, std::uint32_t seq)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
valCount =
|
valCount =
|
||||||
app_.getValidations().getTrustedValidationCount (hash);
|
app_.getValidations().numTrustedForLedger (hash);
|
||||||
|
|
||||||
if (valCount >= app_.validators ().quorum ())
|
if (valCount >= app_.validators ().quorum ())
|
||||||
{
|
{
|
||||||
@@ -764,8 +764,7 @@ LedgerMaster::checkAccept (
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
auto const minVal = getNeededValidations();
|
auto const minVal = getNeededValidations();
|
||||||
auto const tvc = app_.getValidations().getTrustedValidationCount(
|
auto const tvc = app_.getValidations().numTrustedForLedger(ledger->info().hash);
|
||||||
ledger->info().hash);
|
|
||||||
if (tvc < minVal) // nothing we can do
|
if (tvc < minVal) // nothing we can do
|
||||||
{
|
{
|
||||||
JLOG (m_journal.trace()) <<
|
JLOG (m_journal.trace()) <<
|
||||||
@@ -791,7 +790,7 @@ LedgerMaster::checkAccept (
|
|||||||
app_.getOrderBookDB().setup(ledger);
|
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 fees = app_.getValidations().fees (ledger->info().hash, base);
|
||||||
{
|
{
|
||||||
auto fees2 = app_.getValidations().fees (
|
auto fees2 = app_.getValidations().fees (
|
||||||
@@ -799,7 +798,7 @@ LedgerMaster::checkAccept (
|
|||||||
fees.reserve (fees.size() + fees2.size());
|
fees.reserve (fees.size() + fees2.size());
|
||||||
std::copy (fees2.begin(), fees2.end(), std::back_inserter(fees));
|
std::copy (fees2.begin(), fees2.end(), std::back_inserter(fees));
|
||||||
}
|
}
|
||||||
std::uint64_t fee;
|
std::uint32_t fee;
|
||||||
if (! fees.empty())
|
if (! fees.empty())
|
||||||
{
|
{
|
||||||
std::sort (fees.begin(), fees.end());
|
std::sort (fees.begin(), fees.end());
|
||||||
@@ -854,7 +853,7 @@ LedgerMaster::consensusBuilt(
|
|||||||
// maybe we saved up validations for some other ledger that can be
|
// maybe we saved up validations for some other ledger that can be
|
||||||
|
|
||||||
auto const val =
|
auto const val =
|
||||||
app_.getValidations().getCurrentTrustedValidations();
|
app_.getValidations().currentTrusted();
|
||||||
|
|
||||||
// Track validation counts with sequence numbers
|
// Track validation counts with sequence numbers
|
||||||
class valSeq
|
class valSeq
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
#include <ripple/app/main/Application.h>
|
#include <ripple/app/main/Application.h>
|
||||||
#include <ripple/core/DatabaseCon.h>
|
#include <ripple/core/DatabaseCon.h>
|
||||||
|
#include <ripple/app/consensus/RCLValidations.h>
|
||||||
#include <ripple/app/main/DBInit.h>
|
#include <ripple/app/main/DBInit.h>
|
||||||
#include <ripple/app/main/BasicApp.h>
|
#include <ripple/app/main/BasicApp.h>
|
||||||
#include <ripple/app/main/Tuning.h>
|
#include <ripple/app/main/Tuning.h>
|
||||||
@@ -328,7 +329,7 @@ public:
|
|||||||
std::unique_ptr <AmendmentTable> m_amendmentTable;
|
std::unique_ptr <AmendmentTable> m_amendmentTable;
|
||||||
std::unique_ptr <LoadFeeTrack> mFeeTrack;
|
std::unique_ptr <LoadFeeTrack> mFeeTrack;
|
||||||
std::unique_ptr <HashRouter> mHashRouter;
|
std::unique_ptr <HashRouter> mHashRouter;
|
||||||
std::unique_ptr <Validations> mValidations;
|
RCLValidations mValidations;
|
||||||
std::unique_ptr <LoadManager> m_loadManager;
|
std::unique_ptr <LoadManager> m_loadManager;
|
||||||
std::unique_ptr <TxQ> txQ_;
|
std::unique_ptr <TxQ> txQ_;
|
||||||
DeadlineTimer m_sweepTimer;
|
DeadlineTimer m_sweepTimer;
|
||||||
@@ -471,7 +472,8 @@ public:
|
|||||||
, mHashRouter (std::make_unique<HashRouter>(
|
, mHashRouter (std::make_unique<HashRouter>(
|
||||||
stopwatch(), HashRouter::getDefaultHoldTime ()))
|
stopwatch(), HashRouter::getDefaultHoldTime ()))
|
||||||
|
|
||||||
, mValidations (make_Validations (*this))
|
, mValidations (ValidationParms(),stopwatch(), logs_->journal("Validations"),
|
||||||
|
*this)
|
||||||
|
|
||||||
, m_loadManager (make_LoadManager (*this, *this, logs_->journal("LoadManager")))
|
, m_loadManager (make_LoadManager (*this, *this, logs_->journal("LoadManager")))
|
||||||
|
|
||||||
@@ -670,9 +672,9 @@ public:
|
|||||||
return *mHashRouter;
|
return *mHashRouter;
|
||||||
}
|
}
|
||||||
|
|
||||||
Validations& getValidations () override
|
RCLValidations& getValidations () override
|
||||||
{
|
{
|
||||||
return *mValidations;
|
return mValidations;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidatorList& validators () override
|
ValidatorList& validators () override
|
||||||
@@ -853,7 +855,7 @@ public:
|
|||||||
m_entropyTimer.cancel ();
|
m_entropyTimer.cancel ();
|
||||||
}
|
}
|
||||||
|
|
||||||
mValidations->flush ();
|
mValidations.flush ();
|
||||||
|
|
||||||
validatorSites_->stop ();
|
validatorSites_->stop ();
|
||||||
|
|
||||||
@@ -926,7 +928,7 @@ public:
|
|||||||
getNodeStore().sweep();
|
getNodeStore().sweep();
|
||||||
getLedgerMaster().sweep();
|
getLedgerMaster().sweep();
|
||||||
getTempNodeCache().sweep();
|
getTempNodeCache().sweep();
|
||||||
getValidations().sweep();
|
getValidations().expire();
|
||||||
getInboundLedgers().sweep();
|
getInboundLedgers().sweep();
|
||||||
m_acceptedLedgerCache.sweep();
|
m_acceptedLedgerCache.sweep();
|
||||||
family().treecache().sweep();
|
family().treecache().sweep();
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class STLedgerEntry;
|
|||||||
class TimeKeeper;
|
class TimeKeeper;
|
||||||
class TransactionMaster;
|
class TransactionMaster;
|
||||||
class TxQ;
|
class TxQ;
|
||||||
class Validations;
|
|
||||||
class ValidatorList;
|
class ValidatorList;
|
||||||
class ValidatorSite;
|
class ValidatorSite;
|
||||||
class Cluster;
|
class Cluster;
|
||||||
@@ -74,6 +74,13 @@ class SHAMapStore;
|
|||||||
|
|
||||||
using NodeCache = TaggedCache <SHAMapHash, Blob>;
|
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
|
class Application : public beast::PropertyStream::Source
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -128,7 +135,7 @@ public:
|
|||||||
virtual ManifestCache& validatorManifests () = 0;
|
virtual ManifestCache& validatorManifests () = 0;
|
||||||
virtual ManifestCache& publisherManifests () = 0;
|
virtual ManifestCache& publisherManifests () = 0;
|
||||||
virtual Cluster& cluster () = 0;
|
virtual Cluster& cluster () = 0;
|
||||||
virtual Validations& getValidations () = 0;
|
virtual RCLValidations& getValidations () = 0;
|
||||||
virtual NodeStore::Database& getNodeStore () = 0;
|
virtual NodeStore::Database& getNodeStore () = 0;
|
||||||
virtual InboundLedgers& getInboundLedgers () = 0;
|
virtual InboundLedgers& getInboundLedgers () = 0;
|
||||||
virtual InboundTransactions& getInboundTransactions () = 0;
|
virtual InboundTransactions& getInboundTransactions () = 0;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#define RIPPLE_APP_MISC_AMENDMENTTABLE_H_INCLUDED
|
#define RIPPLE_APP_MISC_AMENDMENTTABLE_H_INCLUDED
|
||||||
|
|
||||||
#include <ripple/app/ledger/Ledger.h>
|
#include <ripple/app/ledger/Ledger.h>
|
||||||
#include <ripple/app/misc/Validations.h>
|
#include <ripple/protocol/STValidation.h>
|
||||||
#include <ripple/core/ConfigSections.h>
|
#include <ripple/core/ConfigSections.h>
|
||||||
#include <ripple/protocol/Protocol.h>
|
#include <ripple/protocol/Protocol.h>
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ public:
|
|||||||
NetClock::time_point closeTime,
|
NetClock::time_point closeTime,
|
||||||
std::set <uint256> const& enabledAmendments,
|
std::set <uint256> const& enabledAmendments,
|
||||||
majorityAmendments_t const& majorityAmendments,
|
majorityAmendments_t const& majorityAmendments,
|
||||||
ValidationSet const& valSet) = 0;
|
std::vector<STValidation::pointer> const& valSet) = 0;
|
||||||
|
|
||||||
// Called by the consensus code when we need to
|
// Called by the consensus code when we need to
|
||||||
// add feature entries to a validation
|
// add feature entries to a validation
|
||||||
@@ -112,7 +112,7 @@ public:
|
|||||||
void
|
void
|
||||||
doVoting (
|
doVoting (
|
||||||
std::shared_ptr <ReadView const> const& lastClosedLedger,
|
std::shared_ptr <ReadView const> const& lastClosedLedger,
|
||||||
ValidationSet const& parentValidations,
|
std::vector<STValidation::pointer> const& parentValidations,
|
||||||
std::shared_ptr<SHAMap> const& initialPosition)
|
std::shared_ptr<SHAMap> const& initialPosition)
|
||||||
{
|
{
|
||||||
// Ask implementation what to do
|
// Ask implementation what to do
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
#include <ripple/ledger/ReadView.h>
|
#include <ripple/ledger/ReadView.h>
|
||||||
#include <ripple/shamap/SHAMap.h>
|
#include <ripple/shamap/SHAMap.h>
|
||||||
#include <ripple/app/misc/Validations.h>
|
#include <ripple/protocol/STValidation.h>
|
||||||
#include <ripple/basics/BasicConfig.h>
|
#include <ripple/basics/BasicConfig.h>
|
||||||
#include <ripple/protocol/SystemParameters.h>
|
#include <ripple/protocol/SystemParameters.h>
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
doVoting (std::shared_ptr<ReadView const> const& lastClosedLedger,
|
doVoting (std::shared_ptr<ReadView const> const& lastClosedLedger,
|
||||||
ValidationSet const& parentValidations,
|
std::vector<STValidation::pointer> const& parentValidations,
|
||||||
std::shared_ptr<SHAMap> const& initialPosition) = 0;
|
std::shared_ptr<SHAMap> const& initialPosition) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#include <ripple/protocol/st.h>
|
#include <ripple/protocol/st.h>
|
||||||
#include <ripple/app/misc/FeeVote.h>
|
#include <ripple/app/misc/FeeVote.h>
|
||||||
#include <ripple/app/main/Application.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/basics/BasicConfig.h>
|
||||||
#include <ripple/beast/utility/Journal.h>
|
#include <ripple/beast/utility/Journal.h>
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ public:
|
|||||||
|
|
||||||
void
|
void
|
||||||
doVoting (std::shared_ptr<ReadView const> const& lastClosedLedger,
|
doVoting (std::shared_ptr<ReadView const> const& lastClosedLedger,
|
||||||
ValidationSet const& parentValidations,
|
std::vector<STValidation::pointer> const& parentValidations,
|
||||||
std::shared_ptr<SHAMap> const& initialPosition) override;
|
std::shared_ptr<SHAMap> const& initialPosition) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,8 +149,8 @@ FeeVoteImpl::doValidation(
|
|||||||
void
|
void
|
||||||
FeeVoteImpl::doVoting(
|
FeeVoteImpl::doVoting(
|
||||||
std::shared_ptr<ReadView const> const& lastClosedLedger,
|
std::shared_ptr<ReadView const> const& lastClosedLedger,
|
||||||
ValidationSet const& set,
|
std::vector<STValidation::pointer> const& set,
|
||||||
std::shared_ptr<SHAMap> const& initialPosition)
|
std::shared_ptr<SHAMap> const& initialPosition)
|
||||||
{
|
{
|
||||||
// LCL must be flag ledger
|
// LCL must be flag ledger
|
||||||
assert ((lastClosedLedger->info().seq % 256) == 0);
|
assert ((lastClosedLedger->info().seq % 256) == 0);
|
||||||
@@ -164,33 +164,31 @@ FeeVoteImpl::doVoting(
|
|||||||
detail::VotableInteger<std::uint32_t> incReserveVote (
|
detail::VotableInteger<std::uint32_t> incReserveVote (
|
||||||
lastClosedLedger->fees().increment, target_.owner_reserve);
|
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
|
else
|
||||||
{
|
{
|
||||||
baseFeeVote.noVote ();
|
baseFeeVote.noVote ();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val.isFieldPresent (sfReserveBase))
|
if (val->isFieldPresent (sfReserveBase))
|
||||||
{
|
{
|
||||||
baseReserveVote.addVote (val.getFieldU32 (sfReserveBase));
|
baseReserveVote.addVote (val->getFieldU32 (sfReserveBase));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
baseReserveVote.noVote ();
|
baseReserveVote.noVote ();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val.isFieldPresent (sfReserveIncrement))
|
if (val->isFieldPresent (sfReserveIncrement))
|
||||||
{
|
{
|
||||||
incReserveVote.addVote (val.getFieldU32 (sfReserveIncrement));
|
incReserveVote.addVote (val->getFieldU32 (sfReserveIncrement));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <ripple/app/misc/NetworkOPs.h>
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
#include <ripple/consensus/Consensus.h>
|
#include <ripple/consensus/Consensus.h>
|
||||||
#include <ripple/app/consensus/RCLConsensus.h>
|
#include <ripple/app/consensus/RCLConsensus.h>
|
||||||
|
#include <ripple/app/consensus/RCLValidations.h>
|
||||||
#include <ripple/app/ledger/AcceptedLedger.h>
|
#include <ripple/app/ledger/AcceptedLedger.h>
|
||||||
#include <ripple/app/ledger/InboundLedgers.h>
|
#include <ripple/app/ledger/InboundLedgers.h>
|
||||||
#include <ripple/app/ledger/LedgerMaster.h>
|
#include <ripple/app/ledger/LedgerMaster.h>
|
||||||
@@ -1301,17 +1302,17 @@ bool NetworkOPsImp::checkLastClosedLedger (
|
|||||||
|
|
||||||
hash_map<uint256, ValidationCount> ledgers;
|
hash_map<uint256, ValidationCount> ledgers;
|
||||||
{
|
{
|
||||||
auto current = app_.getValidations ().getCurrentValidations (
|
auto current = app_.getValidations ().currentTrustedDistribution (
|
||||||
closedLedger, prevClosedLedger,
|
closedLedger, prevClosedLedger,
|
||||||
m_ledgerMaster.getValidLedgerIndex());
|
m_ledgerMaster.getValidLedgerIndex());
|
||||||
|
|
||||||
for (auto& it: current)
|
for (auto& it: current)
|
||||||
{
|
{
|
||||||
auto& vc = ledgers[it.first];
|
auto& vc = ledgers[it.first];
|
||||||
vc.trustedValidations += it.second.first;
|
vc.trustedValidations += it.second.count;
|
||||||
|
|
||||||
if (it.second.second > vc.highValidation)
|
if (it.second.highNode > vc.highValidation)
|
||||||
vc.highValidation = it.second.second;
|
vc.highValidation = it.second.highNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2103,7 +2104,7 @@ bool NetworkOPsImp::recvValidation (
|
|||||||
JLOG(m_journal.debug()) << "recvValidation " << val->getLedgerHash ()
|
JLOG(m_journal.debug()) << "recvValidation " << val->getLedgerHash ()
|
||||||
<< " from " << source;
|
<< " from " << source;
|
||||||
pubValidation (val);
|
pubValidation (val);
|
||||||
return app_.getValidations ().addValidation (val, source);
|
return handleNewValidation(app_, val, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value NetworkOPsImp::getConsensusInfo ()
|
Json::Value NetworkOPsImp::getConsensusInfo ()
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
#include <ripple/app/main/Application.h>
|
#include <ripple/app/main/Application.h>
|
||||||
#include <ripple/app/misc/AmendmentTable.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/DatabaseCon.h>
|
||||||
#include <ripple/core/ConfigSections.h>
|
#include <ripple/core/ConfigSections.h>
|
||||||
#include <ripple/protocol/JsonFields.h>
|
#include <ripple/protocol/JsonFields.h>
|
||||||
@@ -207,7 +207,7 @@ public:
|
|||||||
NetClock::time_point closeTime,
|
NetClock::time_point closeTime,
|
||||||
std::set<uint256> const& enabledAmendments,
|
std::set<uint256> const& enabledAmendments,
|
||||||
majorityAmendments_t const& majorityAmendments,
|
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,
|
NetClock::time_point closeTime,
|
||||||
std::set<uint256> const& enabledAmendments,
|
std::set<uint256> const& enabledAmendments,
|
||||||
majorityAmendments_t const& majorityAmendments,
|
majorityAmendments_t const& majorityAmendments,
|
||||||
ValidationSet const& valSet)
|
std::vector<STValidation::pointer> const& valSet)
|
||||||
{
|
{
|
||||||
JLOG (j_.trace()) <<
|
JLOG (j_.trace()) <<
|
||||||
"voting at " << closeTime.time_since_epoch().count() <<
|
"voting at " << closeTime.time_since_epoch().count() <<
|
||||||
@@ -422,16 +422,16 @@ AmendmentTableImpl::doVoting (
|
|||||||
auto vote = std::make_unique <AmendmentSet> ();
|
auto vote = std::make_unique <AmendmentSet> ();
|
||||||
|
|
||||||
// process validations for ledger before flag ledger
|
// 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;
|
std::set<uint256> ballot;
|
||||||
|
|
||||||
if (entry.second->isFieldPresent (sfAmendments))
|
if (val->isFieldPresent (sfAmendments))
|
||||||
{
|
{
|
||||||
auto const choices =
|
auto const choices =
|
||||||
entry.second->getFieldV256 (sfAmendments);
|
val->getFieldV256 (sfAmendments);
|
||||||
ballot.insert (choices.begin (), choices.end ());
|
ballot.insert (choices.begin (), choices.end ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,29 +55,6 @@ auto constexpr decreaseLedgerTimeResolutionEvery = 1;
|
|||||||
//! The number of seconds a ledger may remain idle before closing
|
//! The number of seconds a ledger may remain idle before closing
|
||||||
auto constexpr LEDGER_IDLE_INTERVAL = 15s;
|
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
|
//! The number of seconds we wait minimum to ensure participation
|
||||||
auto constexpr LEDGER_MIN_CONSENSUS = 1950ms;
|
auto constexpr LEDGER_MIN_CONSENSUS = 1950ms;
|
||||||
|
|
||||||
|
|||||||
726
src/ripple/consensus/Validations.h
Normal file
726
src/ripple/consensus/Validations.h
Normal 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
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
#include <ripple/app/misc/LoadFeeTrack.h>
|
#include <ripple/app/misc/LoadFeeTrack.h>
|
||||||
#include <ripple/app/misc/NetworkOPs.h>
|
#include <ripple/app/misc/NetworkOPs.h>
|
||||||
#include <ripple/app/misc/Transaction.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/misc/ValidatorList.h>
|
||||||
#include <ripple/app/tx/apply.h>
|
#include <ripple/app/tx/apply.h>
|
||||||
#include <ripple/protocol/digest.h>
|
#include <ripple/protocol/digest.h>
|
||||||
@@ -1576,7 +1576,10 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMValidation> const& m)
|
|||||||
val->setSeen (closeTime);
|
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";
|
JLOG(p_journal_.trace()) << "Validation: Not current";
|
||||||
fee_ = Resource::feeUnwantedData;
|
fee_ = Resource::feeUnwantedData;
|
||||||
|
|||||||
@@ -20,3 +20,5 @@
|
|||||||
|
|
||||||
#include <ripple/app/consensus/RCLConsensus.cpp>
|
#include <ripple/app/consensus/RCLConsensus.cpp>
|
||||||
#include <ripple/app/consensus/RCLCxPeerPos.cpp>
|
#include <ripple/app/consensus/RCLCxPeerPos.cpp>
|
||||||
|
#include <ripple/app/consensus/RCLValidations.cpp>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
#include <ripple/app/misc/HashRouter.cpp>
|
#include <ripple/app/misc/HashRouter.cpp>
|
||||||
#include <ripple/app/misc/NetworkOPs.cpp>
|
#include <ripple/app/misc/NetworkOPs.cpp>
|
||||||
#include <ripple/app/misc/SHAMapStoreImp.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/AccountTxPaging.cpp>
|
||||||
#include <ripple/app/misc/impl/AmendmentTable.cpp>
|
#include <ripple/app/misc/impl/AmendmentTable.cpp>
|
||||||
|
|||||||
@@ -378,7 +378,7 @@ public:
|
|||||||
auto const roundTime = weekTime (week);
|
auto const roundTime = weekTime (week);
|
||||||
|
|
||||||
// Build validations
|
// Build validations
|
||||||
ValidationSet validations;
|
std::vector<STValidation::pointer> validations;
|
||||||
validations.reserve (validators.size ());
|
validations.reserve (validators.size ());
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -402,7 +402,7 @@ public:
|
|||||||
v->setFieldV256 (sfAmendments, field);
|
v->setFieldV256 (sfAmendments, field);
|
||||||
|
|
||||||
v->setTrusted();
|
v->setTrusted();
|
||||||
validations [val] = v;
|
validations.emplace_back(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
ourVotes = table.doValidation (enabled);
|
ourVotes = table.doValidation (enabled);
|
||||||
|
|||||||
986
src/test/consensus/Validations_test.cpp
Normal file
986
src/test/consensus/Validations_test.cpp
Normal 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
|
||||||
@@ -134,7 +134,8 @@ public:
|
|||||||
return b.second < a.second;
|
return b.second < a.second;
|
||||||
});
|
});
|
||||||
|
|
||||||
top.resize(10);
|
if(top.size() > 10)
|
||||||
|
top.resize(10);
|
||||||
|
|
||||||
os_ << "Longest suite times:\n";
|
os_ << "Longest suite times:\n";
|
||||||
for(auto const& i : top)
|
for(auto const& i : top)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
This file is part of rippled: https://github.com/ripple/rippled
|
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
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
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/Consensus_test.cpp>
|
||||||
#include <test/consensus/LedgerTiming_test.cpp>
|
#include <test/consensus/LedgerTiming_test.cpp>
|
||||||
|
#include <test/consensus/Validations_test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user