mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 11:35:53 +00:00
Fetch validator lists from remote sites:
Validator lists from configured remote sites are fetched at a regular interval. Fetched lists are expected to be in JSON format and contain the following fields: * "manifest": Base64-encoded serialization of a manifest containing the validator publisher's master and signing public keys. * "blob": Base64-encoded JSON string containing a "sequence", "expiration" and "validators" field. "expiration" contains the Ripple timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when the list expires. "validators" contains an array of objects with a "validation_public_key" field. * "signature": Hex-encoded signature of the blob using the publisher's signing key. * "version": 1 * "refreshInterval" (optional)
This commit is contained in:
@@ -1035,6 +1035,14 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\CanonicalTXSet.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\CanonicalTXSet.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\detail\Work.h">
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\detail\WorkBase.h">
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\detail\WorkPlain.h">
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\detail\WorkSSL.h">
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\FeeVote.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\FeeVote.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\misc\FeeVoteImpl.cpp">
|
<ClCompile Include="..\..\src\ripple\app\misc\FeeVoteImpl.cpp">
|
||||||
@@ -1077,6 +1085,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\ripple\app\misc\impl\ValidatorSite.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\LoadFeeTrack.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\LoadFeeTrack.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\Manifest.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\Manifest.h">
|
||||||
@@ -1109,6 +1121,8 @@
|
|||||||
</ClInclude>
|
</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>
|
||||||
<ClCompile Include="..\..\src\ripple\app\paths\AccountCurrencies.cpp">
|
<ClCompile Include="..\..\src\ripple\app\paths\AccountCurrencies.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>
|
||||||
@@ -4275,6 +4289,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\app\ValidatorSite_test.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\test\basics\base_uint_test.cpp">
|
<ClCompile Include="..\..\src\test\basics\base_uint_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>
|
||||||
|
|||||||
@@ -121,6 +121,9 @@
|
|||||||
<Filter Include="ripple\app\misc">
|
<Filter Include="ripple\app\misc">
|
||||||
<UniqueIdentifier>{5A1509B2-871B-A7AC-1E60-544D3F398741}</UniqueIdentifier>
|
<UniqueIdentifier>{5A1509B2-871B-A7AC-1E60-544D3F398741}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="ripple\app\misc\detail">
|
||||||
|
<UniqueIdentifier>{2919FCCC-A707-22B8-FFB4-89494A8AC070}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
<Filter Include="ripple\app\misc\impl">
|
<Filter Include="ripple\app\misc\impl">
|
||||||
<UniqueIdentifier>{C4BDB9F8-7DB7-E304-D286-098085D5D16E}</UniqueIdentifier>
|
<UniqueIdentifier>{C4BDB9F8-7DB7-E304-D286-098085D5D16E}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
@@ -1545,6 +1548,18 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\misc\CanonicalTXSet.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\CanonicalTXSet.h">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\detail\Work.h">
|
||||||
|
<Filter>ripple\app\misc\detail</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\detail\WorkBase.h">
|
||||||
|
<Filter>ripple\app\misc\detail</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\detail\WorkPlain.h">
|
||||||
|
<Filter>ripple\app\misc\detail</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\detail\WorkSSL.h">
|
||||||
|
<Filter>ripple\app\misc\detail</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\FeeVote.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\FeeVote.h">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -1581,6 +1596,9 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorList.cpp">
|
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorList.cpp">
|
||||||
<Filter>ripple\app\misc\impl</Filter>
|
<Filter>ripple\app\misc\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\misc\impl\ValidatorSite.cpp">
|
||||||
|
<Filter>ripple\app\misc\impl</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\LoadFeeTrack.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\LoadFeeTrack.h">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -1620,6 +1638,9 @@
|
|||||||
<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>
|
||||||
|
<ClInclude Include="..\..\src\ripple\app\misc\ValidatorSite.h">
|
||||||
|
<Filter>ripple\app\misc</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\paths\AccountCurrencies.cpp">
|
<ClCompile Include="..\..\src\ripple\app\paths\AccountCurrencies.cpp">
|
||||||
<Filter>ripple\app\paths</Filter>
|
<Filter>ripple\app\paths</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -5040,6 +5061,9 @@
|
|||||||
<ClCompile Include="..\..\src\test\app\ValidatorList_test.cpp">
|
<ClCompile Include="..\..\src\test\app\ValidatorList_test.cpp">
|
||||||
<Filter>test\app</Filter>
|
<Filter>test\app</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\test\app\ValidatorSite_test.cpp">
|
||||||
|
<Filter>test\app</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\test\basics\base_uint_test.cpp">
|
<ClCompile Include="..\..\src\test\basics\base_uint_test.cpp">
|
||||||
<Filter>test\basics</Filter>
|
<Filter>test\basics</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -607,13 +607,15 @@
|
|||||||
# needed to accept consensus.
|
# needed to accept consensus.
|
||||||
#
|
#
|
||||||
# The contents of the file should include a [validators] and/or
|
# The contents of the file should include a [validators] and/or
|
||||||
# [validator_list_keys] entries.
|
# [validator_list_sites] and [validator_list_keys] entries.
|
||||||
# [validators] should be followed by a list of validation public keys of
|
# [validators] should be followed by a list of validation public keys of
|
||||||
# nodes, one per line.
|
# nodes, one per line.
|
||||||
|
# [validator_list_sites] should be followed by a list of URIs each serving a
|
||||||
|
# list of recommended validators.
|
||||||
# [validator_list_keys] should be followed by a list of keys belonging to
|
# [validator_list_keys] should be followed by a list of keys belonging to
|
||||||
# trusted validator list publishers. Validator lists will only be
|
# trusted validator list publishers. Validator lists fetched from configured
|
||||||
# considered if the list is accompanied by a valid signature from a trusted
|
# sites will only be considered if the list is accompanied by a valid
|
||||||
# publisher key.
|
# signature from a trusted publisher key.
|
||||||
#
|
#
|
||||||
# Specify the file by its name or path.
|
# Specify the file by its name or path.
|
||||||
# Unless an absolute path is specified, it will be considered relative to
|
# Unless an absolute path is specified, it will be considered relative to
|
||||||
|
|||||||
@@ -23,13 +23,20 @@
|
|||||||
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
|
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
|
||||||
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt
|
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt
|
||||||
#
|
#
|
||||||
|
# [validator_list_sites]
|
||||||
#
|
#
|
||||||
|
# List of URIs serving lists of recommended validators.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# https://ripple.com/validators
|
||||||
|
# http://127.0.0.1:8000
|
||||||
#
|
#
|
||||||
# [validator_list_keys]
|
# [validator_list_keys]
|
||||||
#
|
#
|
||||||
# List of keys belonging to trusted validator list publishers.
|
# List of keys belonging to trusted validator list publishers.
|
||||||
# Validator lists will only be considered if the list is accompanied by a
|
# Validator lists fetched from configured sites will only be considered
|
||||||
# valid signature from a trusted publisher key.
|
# if the list is accompanied by a valid signature from a trusted
|
||||||
|
# publisher key.
|
||||||
# Validator list keys should be hex-encoded.
|
# Validator list keys should be hex-encoded.
|
||||||
#
|
#
|
||||||
# Examples:
|
# Examples:
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
#include <ripple/app/misc/TxQ.h>
|
#include <ripple/app/misc/TxQ.h>
|
||||||
#include <ripple/app/misc/Validations.h>
|
#include <ripple/app/misc/Validations.h>
|
||||||
#include <ripple/app/misc/ValidatorList.h>
|
#include <ripple/app/misc/ValidatorList.h>
|
||||||
|
#include <ripple/app/misc/ValidatorSite.h>
|
||||||
#include <ripple/app/paths/Pathfinder.h>
|
#include <ripple/app/paths/Pathfinder.h>
|
||||||
#include <ripple/app/paths/PathRequests.h>
|
#include <ripple/app/paths/PathRequests.h>
|
||||||
#include <ripple/app/tx/apply.h>
|
#include <ripple/app/tx/apply.h>
|
||||||
@@ -351,6 +352,7 @@ public:
|
|||||||
std::unique_ptr <ManifestCache> validatorManifests_;
|
std::unique_ptr <ManifestCache> validatorManifests_;
|
||||||
std::unique_ptr <ManifestCache> publisherManifests_;
|
std::unique_ptr <ManifestCache> publisherManifests_;
|
||||||
std::unique_ptr <ValidatorList> validators_;
|
std::unique_ptr <ValidatorList> validators_;
|
||||||
|
std::unique_ptr <ValidatorSite> validatorSites_;
|
||||||
std::unique_ptr <ServerHandler> serverHandler_;
|
std::unique_ptr <ServerHandler> serverHandler_;
|
||||||
std::unique_ptr <AmendmentTable> m_amendmentTable;
|
std::unique_ptr <AmendmentTable> m_amendmentTable;
|
||||||
std::unique_ptr <LoadFeeTrack> mFeeTrack;
|
std::unique_ptr <LoadFeeTrack> mFeeTrack;
|
||||||
@@ -486,6 +488,9 @@ public:
|
|||||||
*validatorManifests_, *publisherManifests_, *timeKeeper_,
|
*validatorManifests_, *publisherManifests_, *timeKeeper_,
|
||||||
logs_->journal("ValidatorList"), config_->VALIDATION_QUORUM))
|
logs_->journal("ValidatorList"), config_->VALIDATION_QUORUM))
|
||||||
|
|
||||||
|
, validatorSites_ (std::make_unique<ValidatorSite> (
|
||||||
|
get_io_service (), *validators_, logs_->journal("ValidatorSite")))
|
||||||
|
|
||||||
, serverHandler_ (make_ServerHandler (*this, *m_networkOPs, get_io_service (),
|
, serverHandler_ (make_ServerHandler (*this, *m_networkOPs, get_io_service (),
|
||||||
*m_jobQueue, *m_networkOPs, *m_resourceManager, *m_collectorManager))
|
*m_jobQueue, *m_networkOPs, *m_resourceManager, *m_collectorManager))
|
||||||
|
|
||||||
@@ -701,6 +706,11 @@ public:
|
|||||||
return *validators_;
|
return *validators_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidatorSite& validatorSites () override
|
||||||
|
{
|
||||||
|
return *validatorSites_;
|
||||||
|
}
|
||||||
|
|
||||||
ManifestCache& validatorManifests() override
|
ManifestCache& validatorManifests() override
|
||||||
{
|
{
|
||||||
return *validatorManifests_;
|
return *validatorManifests_;
|
||||||
@@ -866,6 +876,8 @@ public:
|
|||||||
|
|
||||||
mValidations->flush ();
|
mValidations->flush ();
|
||||||
|
|
||||||
|
validatorSites_->stop ();
|
||||||
|
|
||||||
// TODO Store manifests in manifests.sqlite instead of wallet.db
|
// TODO Store manifests in manifests.sqlite instead of wallet.db
|
||||||
validatorManifests_->save (getWalletDB (), "ValidatorManifests",
|
validatorManifests_->save (getWalletDB (), "ValidatorManifests",
|
||||||
[this](PublicKey const& pubKey)
|
[this](PublicKey const& pubKey)
|
||||||
@@ -1112,6 +1124,14 @@ bool ApplicationImp::setup()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!validatorSites_->load (
|
||||||
|
config().section (SECTION_VALIDATOR_LIST_SITES).values ()))
|
||||||
|
{
|
||||||
|
JLOG(m_journal.fatal()) <<
|
||||||
|
"Invalid entry in [" << SECTION_VALIDATOR_LIST_SITES << "]";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_nodeStore->tune (config_->getSize (siNodeCacheSize), config_->getSize (siNodeCacheAge));
|
m_nodeStore->tune (config_->getSize (siNodeCacheSize), config_->getSize (siNodeCacheAge));
|
||||||
m_ledgerMaster->tune (config_->getSize (siLedgerSize), config_->getSize (siLedgerAge));
|
m_ledgerMaster->tune (config_->getSize (siLedgerSize), config_->getSize (siLedgerAge));
|
||||||
family().treecache().setTargetSize (config_->getSize (siTreeCacheSize));
|
family().treecache().setTargetSize (config_->getSize (siTreeCacheSize));
|
||||||
@@ -1133,6 +1153,8 @@ bool ApplicationImp::setup()
|
|||||||
*config_);
|
*config_);
|
||||||
add (*m_overlay); // add to PropertyStream
|
add (*m_overlay); // add to PropertyStream
|
||||||
|
|
||||||
|
validatorSites_->start ();
|
||||||
|
|
||||||
// start first consensus round
|
// start first consensus round
|
||||||
if (! m_networkOPs->beginConsensus(m_ledgerMaster->getClosedLedger()->info().hash))
|
if (! m_networkOPs->beginConsensus(m_ledgerMaster->getClosedLedger()->info().hash))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class TransactionMaster;
|
|||||||
class TxQ;
|
class TxQ;
|
||||||
class Validations;
|
class Validations;
|
||||||
class ValidatorList;
|
class ValidatorList;
|
||||||
|
class ValidatorSite;
|
||||||
class Cluster;
|
class Cluster;
|
||||||
|
|
||||||
class DatabaseCon;
|
class DatabaseCon;
|
||||||
@@ -121,6 +122,7 @@ public:
|
|||||||
virtual Overlay& overlay () = 0;
|
virtual Overlay& overlay () = 0;
|
||||||
virtual TxQ& getTxQ() = 0;
|
virtual TxQ& getTxQ() = 0;
|
||||||
virtual ValidatorList& validators () = 0;
|
virtual ValidatorList& validators () = 0;
|
||||||
|
virtual ValidatorSite& validatorSites () = 0;
|
||||||
virtual ManifestCache& validatorManifests () = 0;
|
virtual ManifestCache& validatorManifests () = 0;
|
||||||
virtual ManifestCache& publisherManifests () = 0;
|
virtual ManifestCache& publisherManifests () = 0;
|
||||||
virtual Cluster& cluster () = 0;
|
virtual Cluster& cluster () = 0;
|
||||||
|
|||||||
169
src/ripple/app/misc/ValidatorSite.h
Normal file
169
src/ripple/app/misc/ValidatorSite.h
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2016 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_VALIDATORSITE_H_INCLUDED
|
||||||
|
#define RIPPLE_APP_MISC_VALIDATORSITE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/app/misc/ValidatorList.h>
|
||||||
|
#include <ripple/app/misc/detail/Work.h>
|
||||||
|
#include <ripple/basics/Log.h>
|
||||||
|
#include <ripple/basics/StringUtilities.h>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Validator Sites
|
||||||
|
---------------
|
||||||
|
|
||||||
|
This class manages the set of configured remote sites used to fetch the
|
||||||
|
latest published recommended validator lists.
|
||||||
|
|
||||||
|
Lists are fetched at a regular interval.
|
||||||
|
Fetched lists are expected to be in JSON format and contain the following
|
||||||
|
fields:
|
||||||
|
|
||||||
|
@li @c "blob": Base64-encoded JSON string containing a @c "sequence", @c
|
||||||
|
"expiration", and @c "validators" field. @c "expiration" contains the
|
||||||
|
Ripple timestamp (seconds since January 1st, 2000 (00:00 UTC)) for when
|
||||||
|
the list expires. @c "validators" contains an array of objects with a
|
||||||
|
@c "validation_public_key" field.
|
||||||
|
@c "validation_public_key" should be the hex-encoded master public key.
|
||||||
|
|
||||||
|
@li @c "manifest": Base64-encoded serialization of a manifest containing the
|
||||||
|
publisher's master and signing public keys.
|
||||||
|
|
||||||
|
@li @c "signature": Hex-encoded signature of the blob using the publisher's
|
||||||
|
signing key.
|
||||||
|
|
||||||
|
@li @c "version": 1
|
||||||
|
|
||||||
|
@li @c "refreshInterval" (optional)
|
||||||
|
*/
|
||||||
|
class ValidatorSite
|
||||||
|
{
|
||||||
|
friend class Work;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
using clock_type = std::chrono::system_clock;
|
||||||
|
|
||||||
|
struct Site
|
||||||
|
{
|
||||||
|
std::string uri;
|
||||||
|
parsedURL pUrl;
|
||||||
|
std::chrono::minutes refreshInterval;
|
||||||
|
clock_type::time_point nextRefresh;
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::asio::io_service& ios_;
|
||||||
|
ValidatorList& validators_;
|
||||||
|
beast::Journal j_;
|
||||||
|
std::mutex mutable sites_mutex_;
|
||||||
|
std::mutex mutable state_mutex_;
|
||||||
|
|
||||||
|
std::condition_variable cv_;
|
||||||
|
std::weak_ptr<detail::Work> work_;
|
||||||
|
boost::asio::basic_waitable_timer<clock_type> timer_;
|
||||||
|
|
||||||
|
// A list is currently being fetched from a site
|
||||||
|
std::atomic<bool> fetching_;
|
||||||
|
|
||||||
|
// One or more lists are due to be fetched
|
||||||
|
std::atomic<bool> pending_;
|
||||||
|
std::atomic<bool> stopping_;
|
||||||
|
|
||||||
|
// The configured list of URIs for fetching lists
|
||||||
|
std::vector<Site> sites_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ValidatorSite (
|
||||||
|
boost::asio::io_service& ios,
|
||||||
|
ValidatorList& validators,
|
||||||
|
beast::Journal j);
|
||||||
|
~ValidatorSite ();
|
||||||
|
|
||||||
|
/** Load configured site URIs.
|
||||||
|
|
||||||
|
@param siteURIs List of URIs to fetch published validator lists
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
|
||||||
|
May be called concurrently
|
||||||
|
|
||||||
|
@return `false` if an entry is invalid or unparsable
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
load (
|
||||||
|
std::vector<std::string> const& siteURIs);
|
||||||
|
|
||||||
|
/** Start fetching lists from sites
|
||||||
|
|
||||||
|
This does nothing if list fetching has already started
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
|
||||||
|
May be called concurrently
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
start ();
|
||||||
|
|
||||||
|
/** Wait for current fetches from sites to complete
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
|
||||||
|
May be called concurrently
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
join ();
|
||||||
|
|
||||||
|
/** Stop fetching lists from sites
|
||||||
|
|
||||||
|
This blocks until list fetching has stopped
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
|
||||||
|
May be called concurrently
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
stop ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Queue next site to be fetched
|
||||||
|
void
|
||||||
|
setTimer ();
|
||||||
|
|
||||||
|
/// Fetch site whose time has come
|
||||||
|
void
|
||||||
|
onTimer (
|
||||||
|
std::size_t siteIdx,
|
||||||
|
error_code const& ec);
|
||||||
|
|
||||||
|
/// Store latest list fetched from site
|
||||||
|
void
|
||||||
|
onSiteFetch (
|
||||||
|
boost::system::error_code const& ec,
|
||||||
|
detail::response_type&& res,
|
||||||
|
std::size_t siteIdx);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
47
src/ripple/app/misc/detail/Work.h
Normal file
47
src/ripple/app/misc/detail/Work.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2016 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_DETAIL_WORK_H_INCLUDED
|
||||||
|
#define RIPPLE_APP_MISC_DETAIL_WORK_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/string_body.hpp>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
using response_type =
|
||||||
|
beast::http::response<beast::http::string_body>;
|
||||||
|
|
||||||
|
class Work
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Work() = default;
|
||||||
|
|
||||||
|
virtual void run() = 0;
|
||||||
|
|
||||||
|
virtual void cancel() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
} // ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
222
src/ripple/app/misc/detail/WorkBase.h
Normal file
222
src/ripple/app/misc/detail/WorkBase.h
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2016 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_DETAIL_WORKBASE_H_INCLUDED
|
||||||
|
#define RIPPLE_APP_MISC_DETAIL_WORKBASE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/app/misc/detail/Work.h>
|
||||||
|
#include <ripple/protocol/BuildInfo.h>
|
||||||
|
#include <beast/core/placeholders.hpp>
|
||||||
|
#include <beast/core/streambuf.hpp>
|
||||||
|
#include <beast/http/empty_body.hpp>
|
||||||
|
#include <beast/http/read.hpp>
|
||||||
|
#include <beast/http/write.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <class Impl>
|
||||||
|
class WorkBase
|
||||||
|
: public Work
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using callback_type =
|
||||||
|
std::function<void(error_code const&, response_type&&)>;
|
||||||
|
protected:
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using resolver_type = boost::asio::ip::tcp::resolver;
|
||||||
|
using query_type = resolver_type::query;
|
||||||
|
using request_type =
|
||||||
|
beast::http::request<beast::http::empty_body>;
|
||||||
|
|
||||||
|
std::string host_;
|
||||||
|
std::string path_;
|
||||||
|
std::string port_;
|
||||||
|
callback_type cb_;
|
||||||
|
boost::asio::io_service& ios_;
|
||||||
|
boost::asio::io_service::strand strand_;
|
||||||
|
resolver_type resolver_;
|
||||||
|
socket_type socket_;
|
||||||
|
request_type req_;
|
||||||
|
response_type res_;
|
||||||
|
beast::streambuf read_buf_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WorkBase(
|
||||||
|
std::string const& host, std::string const& path,
|
||||||
|
std::string const& port,
|
||||||
|
boost::asio::io_service& ios, callback_type cb);
|
||||||
|
~WorkBase();
|
||||||
|
|
||||||
|
Impl&
|
||||||
|
impl()
|
||||||
|
{
|
||||||
|
return *static_cast<Impl*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
void cancel() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
fail(error_code const& ec);
|
||||||
|
|
||||||
|
void
|
||||||
|
onResolve(error_code const& ec, resolver_type::iterator it);
|
||||||
|
|
||||||
|
void
|
||||||
|
onStart();
|
||||||
|
|
||||||
|
void
|
||||||
|
onRequest(error_code const& ec);
|
||||||
|
|
||||||
|
void
|
||||||
|
onResponse(error_code const& ec);
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
WorkBase<Impl>::WorkBase(std::string const& host,
|
||||||
|
std::string const& path, std::string const& port,
|
||||||
|
boost::asio::io_service& ios, callback_type cb)
|
||||||
|
: host_(host)
|
||||||
|
, path_(path)
|
||||||
|
, port_(port)
|
||||||
|
, cb_(std::move(cb))
|
||||||
|
, ios_(ios)
|
||||||
|
, strand_(ios)
|
||||||
|
, resolver_(ios)
|
||||||
|
, socket_(ios)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
WorkBase<Impl>::~WorkBase()
|
||||||
|
{
|
||||||
|
if (cb_)
|
||||||
|
cb_ (make_error_code(boost::system::errc::not_a_socket),
|
||||||
|
std::move(res_));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
void
|
||||||
|
WorkBase<Impl>::run()
|
||||||
|
{
|
||||||
|
if (! strand_.running_in_this_thread())
|
||||||
|
return ios_.post(strand_.wrap (std::bind(
|
||||||
|
&WorkBase::run, impl().shared_from_this())));
|
||||||
|
|
||||||
|
resolver_.async_resolve(
|
||||||
|
query_type{host_, port_},
|
||||||
|
strand_.wrap (std::bind(&WorkBase::onResolve, impl().shared_from_this(),
|
||||||
|
beast::asio::placeholders::error,
|
||||||
|
beast::asio::placeholders::iterator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
void
|
||||||
|
WorkBase<Impl>::cancel()
|
||||||
|
{
|
||||||
|
if (! strand_.running_in_this_thread())
|
||||||
|
{
|
||||||
|
return ios_.post(strand_.wrap (std::bind(
|
||||||
|
&WorkBase::cancel, impl().shared_from_this())));
|
||||||
|
}
|
||||||
|
|
||||||
|
error_code ec;
|
||||||
|
resolver_.cancel();
|
||||||
|
socket_.cancel (ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
void
|
||||||
|
WorkBase<Impl>::fail(error_code const& ec)
|
||||||
|
{
|
||||||
|
if (cb_)
|
||||||
|
{
|
||||||
|
cb_(ec, std::move(res_));
|
||||||
|
cb_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
void
|
||||||
|
WorkBase<Impl>::onResolve(error_code const& ec, resolver_type::iterator it)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return fail(ec);
|
||||||
|
|
||||||
|
socket_.async_connect(*it,
|
||||||
|
strand_.wrap (std::bind(&Impl::onConnect, impl().shared_from_this(),
|
||||||
|
beast::asio::placeholders::error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
void
|
||||||
|
WorkBase<Impl>::onStart()
|
||||||
|
{
|
||||||
|
req_.method = "GET";
|
||||||
|
req_.url = path_.empty() ? "/" : path_;
|
||||||
|
req_.version = 11;
|
||||||
|
req_.fields.replace (
|
||||||
|
"Host", host_ + ":" + port_);
|
||||||
|
req_.fields.replace ("User-Agent", BuildInfo::getFullVersionString());
|
||||||
|
beast::http::prepare (req_);
|
||||||
|
|
||||||
|
beast::http::async_write(impl().stream(), req_,
|
||||||
|
strand_.wrap (std::bind (&WorkBase::onRequest,
|
||||||
|
impl().shared_from_this(), beast::asio::placeholders::error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
void
|
||||||
|
WorkBase<Impl>::onRequest(error_code const& ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return fail(ec);
|
||||||
|
|
||||||
|
beast::http::async_read (impl().stream(), read_buf_, res_,
|
||||||
|
strand_.wrap (std::bind (&WorkBase::onResponse,
|
||||||
|
impl().shared_from_this(), beast::asio::placeholders::error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
void
|
||||||
|
WorkBase<Impl>::onResponse(error_code const& ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return fail(ec);
|
||||||
|
|
||||||
|
assert(cb_);
|
||||||
|
cb_(ec, std::move(res_));
|
||||||
|
cb_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
} // ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
76
src/ripple/app/misc/detail/WorkPlain.h
Normal file
76
src/ripple/app/misc/detail/WorkPlain.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2016 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_DETAIL_WORKPLAIN_H_INCLUDED
|
||||||
|
#define RIPPLE_APP_MISC_DETAIL_WORKPLAIN_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/app/misc/detail/WorkBase.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// Work over TCP/IP
|
||||||
|
class WorkPlain : public WorkBase<WorkPlain>
|
||||||
|
, public std::enable_shared_from_this<WorkPlain>
|
||||||
|
{
|
||||||
|
friend class WorkBase<WorkPlain>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WorkPlain(
|
||||||
|
std::string const& host,
|
||||||
|
std::string const& path, std::string const& port,
|
||||||
|
boost::asio::io_service& ios, callback_type cb);
|
||||||
|
~WorkPlain() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
onConnect(error_code const& ec);
|
||||||
|
|
||||||
|
socket_type&
|
||||||
|
stream()
|
||||||
|
{
|
||||||
|
return socket_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
WorkPlain::WorkPlain(
|
||||||
|
std::string const& host,
|
||||||
|
std::string const& path, std::string const& port,
|
||||||
|
boost::asio::io_service& ios, callback_type cb)
|
||||||
|
: WorkBase (host, path, port, ios, cb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WorkPlain::onConnect(error_code const& ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return fail(ec);
|
||||||
|
|
||||||
|
onStart ();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
} // ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
137
src/ripple/app/misc/detail/WorkSSL.h
Normal file
137
src/ripple/app/misc/detail/WorkSSL.h
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2016 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_DETAIL_WORKSSL_H_INCLUDED
|
||||||
|
#define RIPPLE_APP_MISC_DETAIL_WORKSSL_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/app/misc/detail/WorkBase.h>
|
||||||
|
#include <ripple/basics/contract.h>
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class SSLContext : public boost::asio::ssl::context
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SSLContext()
|
||||||
|
: boost::asio::ssl::context(boost::asio::ssl::context::sslv23)
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
set_default_verify_paths (ec);
|
||||||
|
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
Throw<std::runtime_error> (
|
||||||
|
boost::str (boost::format (
|
||||||
|
"Failed to set_default_verify_paths: %s") %
|
||||||
|
ec.message ()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Work over SSL
|
||||||
|
class WorkSSL : public WorkBase<WorkSSL>
|
||||||
|
, public std::enable_shared_from_this<WorkSSL>
|
||||||
|
{
|
||||||
|
friend class WorkBase<WorkSSL>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using stream_type = boost::asio::ssl::stream<socket_type&>;
|
||||||
|
|
||||||
|
SSLContext context_;
|
||||||
|
stream_type stream_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WorkSSL(
|
||||||
|
std::string const& host,
|
||||||
|
std::string const& path, std::string const& port,
|
||||||
|
boost::asio::io_service& ios, callback_type cb);
|
||||||
|
~WorkSSL() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream_type&
|
||||||
|
stream()
|
||||||
|
{
|
||||||
|
return stream_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
onConnect(error_code const& ec);
|
||||||
|
|
||||||
|
void
|
||||||
|
onHandshake(error_code const& ec);
|
||||||
|
|
||||||
|
static bool
|
||||||
|
rfc2818_verify (
|
||||||
|
std::string const& domain,
|
||||||
|
bool preverified,
|
||||||
|
boost::asio::ssl::verify_context& ctx)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
boost::asio::ssl::rfc2818_verification (domain) (preverified, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
WorkSSL::WorkSSL(
|
||||||
|
std::string const& host,
|
||||||
|
std::string const& path, std::string const& port,
|
||||||
|
boost::asio::io_service& ios, callback_type cb)
|
||||||
|
: WorkBase (host, path, port, ios, cb)
|
||||||
|
, context_()
|
||||||
|
, stream_ (socket_, context_)
|
||||||
|
{
|
||||||
|
stream_.set_verify_mode (boost::asio::ssl::verify_peer);
|
||||||
|
stream_.set_verify_callback (
|
||||||
|
std::bind (
|
||||||
|
&WorkSSL::rfc2818_verify, host_,
|
||||||
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WorkSSL::onConnect(error_code const& ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return fail(ec);
|
||||||
|
|
||||||
|
stream_.async_handshake(
|
||||||
|
boost::asio::ssl::stream_base::client,
|
||||||
|
strand_.wrap (boost::bind(&WorkSSL::onHandshake, shared_from_this(),
|
||||||
|
boost::asio::placeholders::error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WorkSSL::onHandshake(error_code const& ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
return fail(ec);
|
||||||
|
|
||||||
|
onStart ();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
} // ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
280
src/ripple/app/misc/impl/ValidatorSite.cpp
Normal file
280
src/ripple/app/misc/impl/ValidatorSite.cpp
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2016 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 <ripple/app/misc/detail/WorkPlain.h>
|
||||||
|
#include <ripple/app/misc/detail/WorkSSL.h>
|
||||||
|
#include <ripple/app/misc/ValidatorList.h>
|
||||||
|
#include <ripple/app/misc/ValidatorSite.h>
|
||||||
|
#include <ripple/basics/Slice.h>
|
||||||
|
#include <ripple/json/json_reader.h>
|
||||||
|
#include <beast/core/detail/base64.hpp>
|
||||||
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
// default site query frequency - 5 minutes
|
||||||
|
auto constexpr DEFAULT_REFRESH_INTERVAL = std::chrono::minutes{5};
|
||||||
|
|
||||||
|
ValidatorSite::ValidatorSite (
|
||||||
|
boost::asio::io_service& ios,
|
||||||
|
ValidatorList& validators,
|
||||||
|
beast::Journal j)
|
||||||
|
: ios_ (ios)
|
||||||
|
, validators_ (validators)
|
||||||
|
, j_ (j)
|
||||||
|
, timer_ (ios_)
|
||||||
|
, fetching_ (false)
|
||||||
|
, pending_ (false)
|
||||||
|
, stopping_ (false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidatorSite::~ValidatorSite()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock{state_mutex_};
|
||||||
|
if (timer_.expires_at().time_since_epoch().count())
|
||||||
|
{
|
||||||
|
if (! stopping_)
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cv_.wait(lock, [&]{ return ! fetching_; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ValidatorSite::load (
|
||||||
|
std::vector<std::string> const& siteURIs)
|
||||||
|
{
|
||||||
|
JLOG (j_.debug()) <<
|
||||||
|
"Loading configured validator list sites";
|
||||||
|
|
||||||
|
std::lock_guard <std::mutex> lock{sites_mutex_};
|
||||||
|
|
||||||
|
for (auto uri : siteURIs)
|
||||||
|
{
|
||||||
|
parsedURL pUrl;
|
||||||
|
if (! parseUrl (pUrl, uri) ||
|
||||||
|
(pUrl.scheme != "http" && pUrl.scheme != "https"))
|
||||||
|
{
|
||||||
|
JLOG (j_.error()) <<
|
||||||
|
"Invalid validator site uri: " << uri;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! pUrl.port)
|
||||||
|
pUrl.port = (pUrl.scheme == "https") ? 443 : 80;
|
||||||
|
|
||||||
|
sites_.push_back ({
|
||||||
|
uri, pUrl, DEFAULT_REFRESH_INTERVAL, clock_type::now()});
|
||||||
|
}
|
||||||
|
|
||||||
|
JLOG (j_.debug()) <<
|
||||||
|
"Loaded " << siteURIs.size() << " sites";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ValidatorSite::start ()
|
||||||
|
{
|
||||||
|
std::lock_guard <std::mutex> lock{state_mutex_};
|
||||||
|
if (! timer_.expires_at().time_since_epoch().count())
|
||||||
|
setTimer ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ValidatorSite::join ()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock{state_mutex_};
|
||||||
|
cv_.wait(lock, [&]{ return ! pending_; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ValidatorSite::stop()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock{state_mutex_};
|
||||||
|
stopping_ = true;
|
||||||
|
cv_.wait(lock, [&]{ return ! fetching_; });
|
||||||
|
|
||||||
|
if(auto sp = work_.lock())
|
||||||
|
sp->cancel();
|
||||||
|
|
||||||
|
error_code ec;
|
||||||
|
timer_.cancel(ec);
|
||||||
|
stopping_ = false;
|
||||||
|
pending_ = false;
|
||||||
|
cv_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ValidatorSite::setTimer ()
|
||||||
|
{
|
||||||
|
std::lock_guard <std::mutex> lock{sites_mutex_};
|
||||||
|
auto next = sites_.end();
|
||||||
|
|
||||||
|
for (auto it = sites_.begin (); it != sites_.end (); ++it)
|
||||||
|
if (next == sites_.end () || it->nextRefresh < next->nextRefresh)
|
||||||
|
next = it;
|
||||||
|
|
||||||
|
if (next != sites_.end ())
|
||||||
|
{
|
||||||
|
pending_ = next->nextRefresh <= clock_type::now();
|
||||||
|
cv_.notify_all();
|
||||||
|
timer_.expires_at (next->nextRefresh);
|
||||||
|
timer_.async_wait (std::bind (&ValidatorSite::onTimer, this,
|
||||||
|
std::distance (sites_.begin (), next),
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ValidatorSite::onTimer (
|
||||||
|
std::size_t siteIdx,
|
||||||
|
error_code const& ec)
|
||||||
|
{
|
||||||
|
if (ec == boost::asio::error::operation_aborted)
|
||||||
|
return;
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
JLOG(j_.error()) <<
|
||||||
|
"ValidatorSite::onTimer: " << ec.message();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard <std::mutex> lock{sites_mutex_};
|
||||||
|
sites_[siteIdx].nextRefresh =
|
||||||
|
clock_type::now() + DEFAULT_REFRESH_INTERVAL;
|
||||||
|
|
||||||
|
assert(! fetching_);
|
||||||
|
fetching_ = true;
|
||||||
|
|
||||||
|
std::shared_ptr<detail::Work> sp;
|
||||||
|
if (sites_[siteIdx].pUrl.scheme == "https")
|
||||||
|
{
|
||||||
|
sp = std::make_shared<detail::WorkSSL>(
|
||||||
|
sites_[siteIdx].pUrl.domain,
|
||||||
|
sites_[siteIdx].pUrl.path,
|
||||||
|
std::to_string(*sites_[siteIdx].pUrl.port),
|
||||||
|
ios_,
|
||||||
|
[this, siteIdx](error_code const& err, detail::response_type&& resp)
|
||||||
|
{
|
||||||
|
onSiteFetch (err, std::move(resp), siteIdx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sp = std::make_shared<detail::WorkPlain>(
|
||||||
|
sites_[siteIdx].pUrl.domain,
|
||||||
|
sites_[siteIdx].pUrl.path,
|
||||||
|
std::to_string(*sites_[siteIdx].pUrl.port),
|
||||||
|
ios_,
|
||||||
|
[this, siteIdx](error_code const& err, detail::response_type&& resp)
|
||||||
|
{
|
||||||
|
onSiteFetch (err, std::move(resp), siteIdx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
work_ = sp;
|
||||||
|
sp->run ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ValidatorSite::onSiteFetch(
|
||||||
|
boost::system::error_code const& ec,
|
||||||
|
detail::response_type&& res,
|
||||||
|
std::size_t siteIdx)
|
||||||
|
{
|
||||||
|
if (! ec && res.status != 200)
|
||||||
|
{
|
||||||
|
std::lock_guard <std::mutex> lock{sites_mutex_};
|
||||||
|
JLOG (j_.warn()) <<
|
||||||
|
"Request for validator list at " <<
|
||||||
|
sites_[siteIdx].uri << " returned " << res.status;
|
||||||
|
}
|
||||||
|
else if (! ec)
|
||||||
|
{
|
||||||
|
std::lock_guard <std::mutex> lock{sites_mutex_};
|
||||||
|
Json::Reader r;
|
||||||
|
Json::Value body;
|
||||||
|
if (r.parse(res.body.data(), body) &&
|
||||||
|
body.isObject () &&
|
||||||
|
body.isMember("blob") && body["blob"].isString () &&
|
||||||
|
body.isMember("manifest") && body["manifest"].isString () &&
|
||||||
|
body.isMember("signature") && body["signature"].isString() &&
|
||||||
|
body.isMember("version") && body["version"].isInt())
|
||||||
|
{
|
||||||
|
|
||||||
|
auto const disp = validators_.applyList (
|
||||||
|
body["manifest"].asString (),
|
||||||
|
body["blob"].asString (),
|
||||||
|
body["signature"].asString(),
|
||||||
|
body["version"].asUInt());
|
||||||
|
|
||||||
|
if (ListDisposition::accepted == disp)
|
||||||
|
{
|
||||||
|
JLOG (j_.debug()) <<
|
||||||
|
"Applied new validator list from " <<
|
||||||
|
sites_[siteIdx].uri;
|
||||||
|
}
|
||||||
|
else if (ListDisposition::stale == disp)
|
||||||
|
{
|
||||||
|
JLOG (j_.warn()) <<
|
||||||
|
"Stale validator list from " << sites_[siteIdx].uri;
|
||||||
|
}
|
||||||
|
else if (ListDisposition::untrusted == disp)
|
||||||
|
{
|
||||||
|
JLOG (j_.warn()) <<
|
||||||
|
"Untrusted validator list from " <<
|
||||||
|
sites_[siteIdx].uri;
|
||||||
|
}
|
||||||
|
else if (ListDisposition::invalid == disp)
|
||||||
|
{
|
||||||
|
JLOG (j_.warn()) <<
|
||||||
|
"Invalid validator list from " <<
|
||||||
|
sites_[siteIdx].uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.isMember ("refresh_interval") &&
|
||||||
|
body["refresh_interval"].isNumeric ())
|
||||||
|
{
|
||||||
|
sites_[siteIdx].refreshInterval =
|
||||||
|
std::chrono::minutes{body["refresh_interval"].asUInt ()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JLOG (j_.warn()) <<
|
||||||
|
"Unable to parse JSON response from " <<
|
||||||
|
sites_[siteIdx].uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard <std::mutex> lock{state_mutex_};
|
||||||
|
fetching_ = false;
|
||||||
|
if (! stopping_)
|
||||||
|
setTimer ();
|
||||||
|
cv_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // ripple
|
||||||
@@ -64,6 +64,7 @@ struct ConfigSection
|
|||||||
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
|
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
|
||||||
#define SECTION_VALIDATOR_KEYS "validator_keys"
|
#define SECTION_VALIDATOR_KEYS "validator_keys"
|
||||||
#define SECTION_VALIDATOR_LIST_KEYS "validator_list_keys"
|
#define SECTION_VALIDATOR_LIST_KEYS "validator_list_keys"
|
||||||
|
#define SECTION_VALIDATOR_LIST_SITES "validator_list_sites"
|
||||||
#define SECTION_VALIDATORS "validators"
|
#define SECTION_VALIDATORS "validators"
|
||||||
#define SECTION_VALIDATION_MANIFEST "validation_manifest"
|
#define SECTION_VALIDATION_MANIFEST "validation_manifest"
|
||||||
#define SECTION_VETO_AMENDMENTS "veto_amendments"
|
#define SECTION_VETO_AMENDMENTS "veto_amendments"
|
||||||
|
|||||||
@@ -503,6 +503,13 @@ void Config::loadFromString (std::string const& fileContents)
|
|||||||
if (valKeyEntries)
|
if (valKeyEntries)
|
||||||
section (SECTION_VALIDATOR_KEYS).append (*valKeyEntries);
|
section (SECTION_VALIDATOR_KEYS).append (*valKeyEntries);
|
||||||
|
|
||||||
|
auto valSiteEntries = getIniFileSection(
|
||||||
|
iniFile,
|
||||||
|
SECTION_VALIDATOR_LIST_SITES);
|
||||||
|
|
||||||
|
if (valSiteEntries)
|
||||||
|
section (SECTION_VALIDATOR_LIST_SITES).append (*valSiteEntries);
|
||||||
|
|
||||||
auto valListKeys = getIniFileSection(
|
auto valListKeys = getIniFileSection(
|
||||||
iniFile,
|
iniFile,
|
||||||
SECTION_VALIDATOR_LIST_KEYS);
|
SECTION_VALIDATOR_LIST_KEYS);
|
||||||
@@ -523,6 +530,14 @@ void Config::loadFromString (std::string const& fileContents)
|
|||||||
// Consolidate [validator_keys] and [validators]
|
// Consolidate [validator_keys] and [validators]
|
||||||
section (SECTION_VALIDATORS).append (
|
section (SECTION_VALIDATORS).append (
|
||||||
section (SECTION_VALIDATOR_KEYS).lines ());
|
section (SECTION_VALIDATOR_KEYS).lines ());
|
||||||
|
|
||||||
|
if (! section (SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
|
||||||
|
section (SECTION_VALIDATOR_LIST_KEYS).lines().empty())
|
||||||
|
{
|
||||||
|
Throw<std::runtime_error> (
|
||||||
|
"[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
|
||||||
|
"] config section is missing");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,3 +33,4 @@
|
|||||||
#include <ripple/app/misc/impl/Transaction.cpp>
|
#include <ripple/app/misc/impl/Transaction.cpp>
|
||||||
#include <ripple/app/misc/impl/TxQ.cpp>
|
#include <ripple/app/misc/impl/TxQ.cpp>
|
||||||
#include <ripple/app/misc/impl/ValidatorList.cpp>
|
#include <ripple/app/misc/impl/ValidatorList.cpp>
|
||||||
|
#include <ripple/app/misc/impl/ValidatorSite.cpp>
|
||||||
|
|||||||
392
src/test/app/ValidatorSite_test.cpp
Normal file
392
src/test/app/ValidatorSite_test.cpp
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright 2016 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 <beast/core/placeholders.hpp>
|
||||||
|
#include <beast/core/detail/base64.hpp>
|
||||||
|
#include <beast/http.hpp>
|
||||||
|
#include <ripple/app/misc/ValidatorSite.h>
|
||||||
|
#include <ripple/basics/Slice.h>
|
||||||
|
#include <ripple/basics/strHex.h>
|
||||||
|
#include <ripple/protocol/digest.h>
|
||||||
|
#include <ripple/protocol/HashPrefix.h>
|
||||||
|
#include <ripple/protocol/PublicKey.h>
|
||||||
|
#include <ripple/protocol/SecretKey.h>
|
||||||
|
#include <ripple/protocol/Sign.h>
|
||||||
|
#include <test/jtx.h>
|
||||||
|
#include <boost/utility/in_place_factory.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class http_sync_server
|
||||||
|
{
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
|
using req_type = beast::http::request<beast::http::string_body>;
|
||||||
|
using resp_type = beast::http::response<beast::http::string_body>;
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
|
||||||
|
std::string list_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
http_sync_server(endpoint_type const& ep,
|
||||||
|
boost::asio::io_service& ios,
|
||||||
|
std::pair<PublicKey, SecretKey> keys,
|
||||||
|
std::string const& manifest,
|
||||||
|
int sequence,
|
||||||
|
std::size_t expiration,
|
||||||
|
int version,
|
||||||
|
std::vector <PublicKey> const& validators)
|
||||||
|
: sock_(ios)
|
||||||
|
, acceptor_(ios)
|
||||||
|
{
|
||||||
|
std::string data =
|
||||||
|
"{\"sequence\":" + std::to_string(sequence) +
|
||||||
|
",\"expiration\":" + std::to_string(expiration) +
|
||||||
|
",\"validators\":[";
|
||||||
|
|
||||||
|
for (auto const& val : validators)
|
||||||
|
{
|
||||||
|
data += "{\"validation_public_key\":\"" + strHex (val) + "\"},";
|
||||||
|
}
|
||||||
|
data.pop_back();
|
||||||
|
data += "]}";
|
||||||
|
std::string blob = beast::detail::base64_encode(data);
|
||||||
|
|
||||||
|
list_ = "{\"blob\":\"" + blob + "\"";
|
||||||
|
|
||||||
|
auto const sig = sign(keys.first, keys.second, makeSlice(data));
|
||||||
|
|
||||||
|
list_ += ",\"signature\":\"" + strHex(sig) + "\"";
|
||||||
|
list_ += ",\"manifest\":\"" + manifest + "\"";
|
||||||
|
list_ += ",\"version\":" + std::to_string(version) + '}';
|
||||||
|
|
||||||
|
acceptor_.open(ep.protocol());
|
||||||
|
error_code ec;
|
||||||
|
acceptor_.set_option(
|
||||||
|
boost::asio::ip::tcp::acceptor::reuse_address(true), ec);
|
||||||
|
acceptor_.bind(ep);
|
||||||
|
acceptor_.listen(boost::asio::socket_base::max_connections);
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&http_sync_server::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
~http_sync_server()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
acceptor_.close(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct lambda
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
http_sync_server& self;
|
||||||
|
socket_type sock;
|
||||||
|
boost::asio::io_service::work work;
|
||||||
|
|
||||||
|
lambda(int id_, http_sync_server& self_,
|
||||||
|
socket_type&& sock_)
|
||||||
|
: id(id_)
|
||||||
|
, self(self_)
|
||||||
|
, sock(std::move(sock_))
|
||||||
|
, work(sock.get_io_service())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
self.do_peer(id, std::move(sock));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
on_accept(error_code ec)
|
||||||
|
{
|
||||||
|
if(! acceptor_.is_open())
|
||||||
|
return;
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
static int id_ = 0;
|
||||||
|
std::thread{lambda{++id_, *this, std::move(sock_)}}.detach();
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&http_sync_server::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
do_peer(int id, socket_type&& sock0)
|
||||||
|
{
|
||||||
|
socket_type sock(std::move(sock0));
|
||||||
|
beast::streambuf sb;
|
||||||
|
error_code ec;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
req_type req;
|
||||||
|
beast::http::read(sock, sb, req, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
auto path = req.url;
|
||||||
|
if(path != "/validators")
|
||||||
|
{
|
||||||
|
resp_type res;
|
||||||
|
res.status = 404;
|
||||||
|
res.reason = "Not Found";
|
||||||
|
res.version = req.version;
|
||||||
|
res.fields.insert("Server", "http_sync_server");
|
||||||
|
res.fields.insert("Content-Type", "text/html");
|
||||||
|
res.body = "The file '" + path + "' was not found";
|
||||||
|
prepare(res);
|
||||||
|
write(sock, res, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
resp_type res;
|
||||||
|
res.status = 200;
|
||||||
|
res.reason = "OK";
|
||||||
|
res.version = req.version;
|
||||||
|
res.fields.insert("Server", "http_sync_server");
|
||||||
|
res.fields.insert("Content-Type", "application/json");
|
||||||
|
|
||||||
|
res.body = list_;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
prepare(res);
|
||||||
|
}
|
||||||
|
catch(std::exception const& e)
|
||||||
|
{
|
||||||
|
res = {};
|
||||||
|
res.status = 500;
|
||||||
|
res.reason = "Internal Error";
|
||||||
|
res.version = req.version;
|
||||||
|
res.fields.insert("Server", "http_sync_server");
|
||||||
|
res.fields.insert("Content-Type", "text/html");
|
||||||
|
res.body =
|
||||||
|
std::string{"An internal error occurred"} + e.what();
|
||||||
|
prepare(res);
|
||||||
|
}
|
||||||
|
write(sock, res, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ValidatorSite_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static
|
||||||
|
PublicKey
|
||||||
|
randomNode ()
|
||||||
|
{
|
||||||
|
return derivePublicKey (KeyType::secp256k1, randomSecretKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
makeManifestString (
|
||||||
|
PublicKey const& pk,
|
||||||
|
SecretKey const& sk,
|
||||||
|
PublicKey const& spk,
|
||||||
|
SecretKey const& ssk,
|
||||||
|
int seq)
|
||||||
|
{
|
||||||
|
STObject st(sfGeneric);
|
||||||
|
st[sfSequence] = seq;
|
||||||
|
st[sfPublicKey] = pk;
|
||||||
|
st[sfSigningPubKey] = spk;
|
||||||
|
|
||||||
|
sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk);
|
||||||
|
sign(st, HashPrefix::manifest, *publicKeyType(pk), sk,
|
||||||
|
sfMasterSignature);
|
||||||
|
|
||||||
|
Serializer s;
|
||||||
|
st.add(s);
|
||||||
|
|
||||||
|
return beast::detail::base64_encode (std::string(
|
||||||
|
static_cast<char const*> (s.data()), s.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testConfigLoad ()
|
||||||
|
{
|
||||||
|
testcase ("Config Load");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
Env env (*this);
|
||||||
|
auto trustedSites = std::make_unique<ValidatorSite> (
|
||||||
|
env.app().getIOService(), env.app().validators(), beast::Journal());
|
||||||
|
|
||||||
|
// load should accept empty sites list
|
||||||
|
std::vector<std::string> emptyCfgSites;
|
||||||
|
BEAST_EXPECT(trustedSites->load (emptyCfgSites));
|
||||||
|
|
||||||
|
// load should accept valid validator site uris
|
||||||
|
std::vector<std::string> cfgSites({
|
||||||
|
"http://ripple.com/",
|
||||||
|
"http://ripple.com/validators",
|
||||||
|
"http://ripple.com:8080/validators",
|
||||||
|
"http://207.261.33.37/validators",
|
||||||
|
"http://207.261.33.37:8080/validators",
|
||||||
|
"https://ripple.com/validators",
|
||||||
|
"https://ripple.com:443/validators"});
|
||||||
|
BEAST_EXPECT(trustedSites->load (cfgSites));
|
||||||
|
|
||||||
|
// load should reject validator site uris with invalid schemes
|
||||||
|
std::vector<std::string> badSites(
|
||||||
|
{"ftp://ripple.com/validators"});
|
||||||
|
BEAST_EXPECT(!trustedSites->load (badSites));
|
||||||
|
|
||||||
|
badSites[0] = "wss://ripple.com/validators";
|
||||||
|
BEAST_EXPECT(!trustedSites->load (badSites));
|
||||||
|
|
||||||
|
badSites[0] = "ripple.com/validators";
|
||||||
|
BEAST_EXPECT(!trustedSites->load (badSites));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testFetchList ()
|
||||||
|
{
|
||||||
|
testcase ("Fetch list");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
Env env (*this);
|
||||||
|
auto& ioService = env.app ().getIOService ();
|
||||||
|
auto& trustedKeys = env.app ().validators ();
|
||||||
|
|
||||||
|
beast::Journal journal;
|
||||||
|
|
||||||
|
PublicKey emptyLocalKey;
|
||||||
|
std::vector<std::string> emptyCfgKeys;
|
||||||
|
|
||||||
|
auto const publisherSecret1 = randomSecretKey();
|
||||||
|
auto const publisherPublic1 =
|
||||||
|
derivePublicKey(KeyType::ed25519, publisherSecret1);
|
||||||
|
auto const pubSigningKeys1 = randomKeyPair(KeyType::secp256k1);
|
||||||
|
|
||||||
|
auto const manifest1 = makeManifestString (
|
||||||
|
publisherPublic1, publisherSecret1,
|
||||||
|
pubSigningKeys1.first, pubSigningKeys1.second, 1);
|
||||||
|
|
||||||
|
auto const publisherSecret2 = randomSecretKey();
|
||||||
|
auto const publisherPublic2 =
|
||||||
|
derivePublicKey(KeyType::ed25519, publisherSecret2);
|
||||||
|
auto const pubSigningKeys2 = randomKeyPair(KeyType::secp256k1);
|
||||||
|
|
||||||
|
auto const manifest2 = makeManifestString (
|
||||||
|
publisherPublic2, publisherSecret2,
|
||||||
|
pubSigningKeys2.first, pubSigningKeys2.second, 1);
|
||||||
|
|
||||||
|
std::vector<std::string> cfgPublishers({
|
||||||
|
strHex(publisherPublic1),
|
||||||
|
strHex(publisherPublic2)});
|
||||||
|
|
||||||
|
BEAST_EXPECT(trustedKeys.load (
|
||||||
|
emptyLocalKey, emptyCfgKeys, cfgPublishers));
|
||||||
|
|
||||||
|
auto constexpr listSize = 20;
|
||||||
|
std::vector<PublicKey> list1;
|
||||||
|
list1.reserve (listSize);
|
||||||
|
while (list1.size () < listSize)
|
||||||
|
list1.push_back (randomNode());
|
||||||
|
|
||||||
|
std::vector<PublicKey> list2;
|
||||||
|
list2.reserve (listSize);
|
||||||
|
while (list2.size () < listSize)
|
||||||
|
list2.push_back (randomNode());
|
||||||
|
|
||||||
|
std::uint16_t constexpr port1 = 7475;
|
||||||
|
std::uint16_t constexpr port2 = 7476;
|
||||||
|
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
|
||||||
|
endpoint_type ep1{address_type::from_string("127.0.0.1"), port1};
|
||||||
|
endpoint_type ep2{address_type::from_string("127.0.0.1"), port2};
|
||||||
|
|
||||||
|
auto const sequence = 1;
|
||||||
|
auto const version = 1;
|
||||||
|
NetClock::time_point const expiration =
|
||||||
|
env.timeKeeper().now() + 3600s;
|
||||||
|
|
||||||
|
http_sync_server server1(
|
||||||
|
ep1, ioService, pubSigningKeys1, manifest1, sequence,
|
||||||
|
expiration.time_since_epoch().count(), version, list1);
|
||||||
|
|
||||||
|
http_sync_server server2(
|
||||||
|
ep2, ioService, pubSigningKeys2, manifest2, sequence,
|
||||||
|
expiration.time_since_epoch().count(), version, list2);
|
||||||
|
|
||||||
|
{
|
||||||
|
// fetch single site
|
||||||
|
std::vector<std::string> cfgSites(
|
||||||
|
{"http://127.0.0.1:" + std::to_string(port1) + "/validators"});
|
||||||
|
|
||||||
|
auto sites = std::make_unique<ValidatorSite> (
|
||||||
|
env.app().getIOService(), env.app().validators(), journal);
|
||||||
|
|
||||||
|
sites->load (cfgSites);
|
||||||
|
sites->start();
|
||||||
|
sites->join();
|
||||||
|
|
||||||
|
for (auto const& val : list1)
|
||||||
|
BEAST_EXPECT(trustedKeys.listed (val));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// fetch multiple sites
|
||||||
|
std::vector<std::string> cfgSites({
|
||||||
|
"http://127.0.0.1:" + std::to_string(port1) + "/validators",
|
||||||
|
"http://127.0.0.1:" + std::to_string(port2) + "/validators"});
|
||||||
|
|
||||||
|
auto sites = std::make_unique<ValidatorSite> (
|
||||||
|
env.app().getIOService(), env.app().validators(), journal);
|
||||||
|
|
||||||
|
sites->load (cfgSites);
|
||||||
|
sites->start();
|
||||||
|
sites->join();
|
||||||
|
|
||||||
|
for (auto const& val : list1)
|
||||||
|
BEAST_EXPECT(trustedKeys.listed (val));
|
||||||
|
|
||||||
|
for (auto const& val : list2)
|
||||||
|
BEAST_EXPECT(trustedKeys.listed (val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
testConfigLoad ();
|
||||||
|
testFetchList ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(ValidatorSite, app, ripple);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // ripple
|
||||||
@@ -300,6 +300,10 @@ nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
|
|||||||
nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
||||||
nHUPDdcdb2Y5DZAJne4c2iabFuAP3F34xZUgYQT2NH7qfkdapgnz
|
nHUPDdcdb2Y5DZAJne4c2iabFuAP3F34xZUgYQT2NH7qfkdapgnz
|
||||||
|
|
||||||
|
[validator_list_sites]
|
||||||
|
recommendedripplevalidators.com
|
||||||
|
moreripplevalidators.net
|
||||||
|
|
||||||
[validator_list_keys]
|
[validator_list_keys]
|
||||||
03E74EE14CB525AFBB9F1B7D86CD58ECC4B91452294B42AB4E78F260BD905C091D
|
03E74EE14CB525AFBB9F1B7D86CD58ECC4B91452294B42AB4E78F260BD905C091D
|
||||||
030775A669685BD6ABCEBD80385921C7851783D991A8055FD21D2F3966C96F1B56
|
030775A669685BD6ABCEBD80385921C7851783D991A8055FD21D2F3966C96F1B56
|
||||||
@@ -527,19 +531,50 @@ nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
|||||||
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 5);
|
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 5);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// load validator list keys from config
|
// load validator list sites and keys from config
|
||||||
Config c;
|
Config c;
|
||||||
std::string toLoad(R"rippleConfig(
|
std::string toLoad(R"rippleConfig(
|
||||||
|
[validator_list_sites]
|
||||||
|
ripplevalidators.com
|
||||||
|
trustthesevalidators.gov
|
||||||
|
|
||||||
[validator_list_keys]
|
[validator_list_keys]
|
||||||
021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
|
021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
|
||||||
)rippleConfig");
|
)rippleConfig");
|
||||||
c.loadFromString (toLoad);
|
c.loadFromString (toLoad);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ().size () == 2);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ()[0] ==
|
||||||
|
"ripplevalidators.com");
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ()[1] ==
|
||||||
|
"trustthesevalidators.gov");
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 1);
|
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 1);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
c.section (SECTION_VALIDATOR_LIST_KEYS).values ()[0] ==
|
c.section (SECTION_VALIDATOR_LIST_KEYS).values ()[0] ==
|
||||||
"021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566");
|
"021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566");
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// load should throw if [validator_list_sites] is configured but
|
||||||
|
// [validator_list_keys] is not
|
||||||
|
Config c;
|
||||||
|
std::string toLoad(R"rippleConfig(
|
||||||
|
[validator_list_sites]
|
||||||
|
ripplevalidators.com
|
||||||
|
trustthesevalidators.gov
|
||||||
|
)rippleConfig");
|
||||||
|
std::string error;
|
||||||
|
auto const expectedError =
|
||||||
|
"[validator_list_keys] config section is missing";
|
||||||
|
try {
|
||||||
|
c.loadFromString (toLoad);
|
||||||
|
} catch (std::runtime_error& e) {
|
||||||
|
error = e.what();
|
||||||
|
}
|
||||||
|
BEAST_EXPECT(error == expectedError);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
// load from specified [validators_file] absolute path
|
// load from specified [validators_file] absolute path
|
||||||
detail::ValidatorsTxtGuard const vtg (
|
detail::ValidatorsTxtGuard const vtg (
|
||||||
@@ -550,6 +585,8 @@ nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
|||||||
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
||||||
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
|
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
|
||||||
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ().size () == 2);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
||||||
}
|
}
|
||||||
@@ -566,6 +603,8 @@ nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
|||||||
auto const& c (rcg.config ());
|
auto const& c (rcg.config ());
|
||||||
BEAST_EXPECT(c.legacy ("validators_file") == valFileName);
|
BEAST_EXPECT(c.legacy ("validators_file") == valFileName);
|
||||||
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ().size () == 2);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
||||||
}
|
}
|
||||||
@@ -582,6 +621,8 @@ nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
|||||||
auto const& c (rcg.config ());
|
auto const& c (rcg.config ());
|
||||||
BEAST_EXPECT(c.legacy ("validators_file") == valFilePath);
|
BEAST_EXPECT(c.legacy ("validators_file") == valFilePath);
|
||||||
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ().size () == 2);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
||||||
}
|
}
|
||||||
@@ -596,6 +637,8 @@ nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
|||||||
auto const& c (rcg.config ());
|
auto const& c (rcg.config ());
|
||||||
BEAST_EXPECT(c.legacy ("validators_file").empty ());
|
BEAST_EXPECT(c.legacy ("validators_file").empty ());
|
||||||
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ().size () == 2);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
||||||
}
|
}
|
||||||
@@ -614,6 +657,8 @@ nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
|
|||||||
auto const& c (rcg.config ());
|
auto const& c (rcg.config ());
|
||||||
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
|
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
|
||||||
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 8);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ().size () == 2);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 2);
|
||||||
}
|
}
|
||||||
@@ -635,6 +680,10 @@ n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
|
|||||||
nHB1X37qrniVugfQcuBTAjswphC1drx7QjFFojJPZwKHHnt8kU7v
|
nHB1X37qrniVugfQcuBTAjswphC1drx7QjFFojJPZwKHHnt8kU7v
|
||||||
nHUkAWDR4cB8AgPg7VXMX6et8xRTQb2KJfgv1aBEXozwrawRKgMB
|
nHUkAWDR4cB8AgPg7VXMX6et8xRTQb2KJfgv1aBEXozwrawRKgMB
|
||||||
|
|
||||||
|
[validator_list_sites]
|
||||||
|
ripplevalidators.com
|
||||||
|
trustthesevalidators.gov
|
||||||
|
|
||||||
[validator_list_keys]
|
[validator_list_keys]
|
||||||
021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
|
021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
|
||||||
)rippleConfig");
|
)rippleConfig");
|
||||||
@@ -645,6 +694,8 @@ nHUkAWDR4cB8AgPg7VXMX6et8xRTQb2KJfgv1aBEXozwrawRKgMB
|
|||||||
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
|
||||||
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
|
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
|
||||||
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 15);
|
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 15);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
c.section (SECTION_VALIDATOR_LIST_SITES).values ().size () == 4);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 3);
|
c.section (SECTION_VALIDATOR_LIST_KEYS).values ().size () == 3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,5 +45,6 @@
|
|||||||
#include <test/app/TrustAndBalance_test.cpp>
|
#include <test/app/TrustAndBalance_test.cpp>
|
||||||
#include <test/app/TxQ_test.cpp>
|
#include <test/app/TxQ_test.cpp>
|
||||||
#include <test/app/ValidatorList_test.cpp>
|
#include <test/app/ValidatorList_test.cpp>
|
||||||
|
#include <test/app/ValidatorSite_test.cpp>
|
||||||
#include <test/app/SetTrust_test.cpp>
|
#include <test/app/SetTrust_test.cpp>
|
||||||
#include <test/app/Ticket_test.cpp>
|
#include <test/app/Ticket_test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user