mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 19:45:53 +00:00
Propagate validator lists (VLs or UNLs) over the peer network:
* Whenever a node downloads a new VL, send it to all peers that haven't already sent or received it. It also saves it to the database_dir as a Json text file named "cache." plus the public key of the list signer. Any files that exist for public keys provided in [validator_list_keys] will be loaded and processed if any download from [validator_list_sites] fails or no [validator_list_sites] are configured. * Whenever a node receives a broadcast VL message, it treats it as if it had downloaded it on it's own, broadcasting to other peers as described above. * Because nodes normally download the VL once every 5 minutes, a single node downloading a VL with an updated sequence number could potentially propagate across a large part of a well-connected network before any other nodes attempt to download, decreasing the amount of time that different parts of the network are using different VLs. * Send all of our current valid VLs to new peers on connection. This is probably the "noisiest" part of this change, but will give poorly connected or poorly networked nodes the best chance of syncing quickly. Nodes which have no http(s) access configured or available can get a VL with no extra effort. * Requests on the peer port to the /vl/<pubkey> endpoint will return that VL in the same JSON format as is used to download now, IF the node trusts and has a valid instance of that VL. * Upgrade protocol version to 2.1. VLs will only be sent to 2.1 and higher nodes. * Resolves #2953
This commit is contained in:
committed by
Manoj doshi
parent
3753840de5
commit
2c71802e38
@@ -1052,7 +1052,7 @@
|
|||||||
# [crawl]
|
# [crawl]
|
||||||
#
|
#
|
||||||
# List of options to control what data is reported through the /crawl endpoint
|
# List of options to control what data is reported through the /crawl endpoint
|
||||||
# See https://developers.ripple.com/peer-protocol.html#peer-crawler
|
# See https://xrpl.org/peer-crawler.html
|
||||||
#
|
#
|
||||||
# <flag>
|
# <flag>
|
||||||
#
|
#
|
||||||
@@ -1093,6 +1093,16 @@
|
|||||||
# counts = 0
|
# counts = 0
|
||||||
# unl = 1
|
# unl = 1
|
||||||
#
|
#
|
||||||
|
# [vl]
|
||||||
|
#
|
||||||
|
# Options to control what data is reported through the /vl endpoint
|
||||||
|
# See [...]
|
||||||
|
#
|
||||||
|
# enable = <flag>
|
||||||
|
#
|
||||||
|
# Enable or disable access to /vl requests. Default is '1' which
|
||||||
|
# enables access.
|
||||||
|
#
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# 9. Example Settings
|
# 9. Example Settings
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ public:
|
|||||||
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;
|
||||||
std::unique_ptr <HashRouter> mHashRouter;
|
std::unique_ptr <HashRouter> hashRouter_;
|
||||||
RCLValidations 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_;
|
||||||
@@ -377,7 +377,7 @@ public:
|
|||||||
std::unique_ptr <DatabaseCon> mTxnDB;
|
std::unique_ptr <DatabaseCon> mTxnDB;
|
||||||
std::unique_ptr <DatabaseCon> mLedgerDB;
|
std::unique_ptr <DatabaseCon> mLedgerDB;
|
||||||
std::unique_ptr <DatabaseCon> mWalletDB;
|
std::unique_ptr <DatabaseCon> mWalletDB;
|
||||||
std::unique_ptr <Overlay> m_overlay;
|
std::unique_ptr <Overlay> overlay_;
|
||||||
std::vector <std::unique_ptr<Stoppable>> websocketServers_;
|
std::vector <std::unique_ptr<Stoppable>> websocketServers_;
|
||||||
|
|
||||||
boost::asio::signal_set m_signals;
|
boost::asio::signal_set m_signals;
|
||||||
@@ -520,7 +520,8 @@ public:
|
|||||||
logs_->journal("ManifestCache")))
|
logs_->journal("ManifestCache")))
|
||||||
|
|
||||||
, validators_ (std::make_unique<ValidatorList> (
|
, validators_ (std::make_unique<ValidatorList> (
|
||||||
*validatorManifests_, *publisherManifests_, *timeKeeper_,
|
*validatorManifests_, *publisherManifests_,
|
||||||
|
*timeKeeper_, config_->legacy("database_path"),
|
||||||
logs_->journal("ValidatorList"), config_->VALIDATION_QUORUM))
|
logs_->journal("ValidatorList"), config_->VALIDATION_QUORUM))
|
||||||
|
|
||||||
, validatorSites_ (std::make_unique<ValidatorSite> (*this))
|
, validatorSites_ (std::make_unique<ValidatorSite> (*this))
|
||||||
@@ -531,7 +532,7 @@ public:
|
|||||||
|
|
||||||
, mFeeTrack (std::make_unique<LoadFeeTrack>(logs_->journal("LoadManager")))
|
, mFeeTrack (std::make_unique<LoadFeeTrack>(logs_->journal("LoadManager")))
|
||||||
|
|
||||||
, mHashRouter (std::make_unique<HashRouter>(
|
, hashRouter_ (std::make_unique<HashRouter>(
|
||||||
stopwatch(), HashRouter::getDefaultHoldTime (),
|
stopwatch(), HashRouter::getDefaultHoldTime (),
|
||||||
HashRouter::getDefaultRecoverLimit ()))
|
HashRouter::getDefaultRecoverLimit ()))
|
||||||
|
|
||||||
@@ -760,7 +761,7 @@ public:
|
|||||||
|
|
||||||
HashRouter& getHashRouter () override
|
HashRouter& getHashRouter () override
|
||||||
{
|
{
|
||||||
return *mHashRouter;
|
return *hashRouter_;
|
||||||
}
|
}
|
||||||
|
|
||||||
RCLValidations& getValidations () override
|
RCLValidations& getValidations () override
|
||||||
@@ -828,7 +829,8 @@ public:
|
|||||||
|
|
||||||
Overlay& overlay () override
|
Overlay& overlay () override
|
||||||
{
|
{
|
||||||
return *m_overlay;
|
assert(overlay_);
|
||||||
|
return *overlay_;
|
||||||
}
|
}
|
||||||
|
|
||||||
TxQ& getTxQ() override
|
TxQ& getTxQ() override
|
||||||
@@ -1480,10 +1482,10 @@ bool ApplicationImp::setup()
|
|||||||
// move the instantiation inside a conditional:
|
// move the instantiation inside a conditional:
|
||||||
//
|
//
|
||||||
// if (!config_.standalone())
|
// if (!config_.standalone())
|
||||||
m_overlay = make_Overlay (*this, setup_Overlay(*config_), *m_jobQueue,
|
overlay_ = make_Overlay (*this, setup_Overlay(*config_), *m_jobQueue,
|
||||||
*serverHandler_, *m_resourceManager, *m_resolver, get_io_service(),
|
*serverHandler_, *m_resourceManager, *m_resolver, get_io_service(),
|
||||||
*config_, m_collectorManager->collector ());
|
*config_, m_collectorManager->collector ());
|
||||||
add (*m_overlay); // add to PropertyStream
|
add (*overlay_); // add to PropertyStream
|
||||||
|
|
||||||
if (!config_->standalone())
|
if (!config_->standalone())
|
||||||
{
|
{
|
||||||
@@ -1675,7 +1677,7 @@ int ApplicationImp::fdRequired() const
|
|||||||
int needed = 128;
|
int needed = 128;
|
||||||
|
|
||||||
// 1.5 times the configured peer limit for peer connections:
|
// 1.5 times the configured peer limit for peer connections:
|
||||||
needed += static_cast<int>(0.5 + (1.5 * m_overlay->limit()));
|
needed += static_cast<int>(0.5 + (1.5 * overlay_->limit()));
|
||||||
|
|
||||||
// the number of fds needed by the backend (internally
|
// the number of fds needed by the backend (internally
|
||||||
// doubled if online delete is enabled).
|
// doubled if online delete is enabled).
|
||||||
@@ -2131,7 +2133,7 @@ ApplicationImp::journal (std::string const& name)
|
|||||||
|
|
||||||
bool ApplicationImp::nodeToShards()
|
bool ApplicationImp::nodeToShards()
|
||||||
{
|
{
|
||||||
assert(m_overlay);
|
assert(overlay_);
|
||||||
assert(!config_->standalone());
|
assert(!config_->standalone());
|
||||||
|
|
||||||
if (config_->section(ConfigSection::shardDatabase()).empty())
|
if (config_->section(ConfigSection::shardDatabase()).empty())
|
||||||
@@ -2152,7 +2154,7 @@ bool ApplicationImp::nodeToShards()
|
|||||||
|
|
||||||
bool ApplicationImp::validateShards()
|
bool ApplicationImp::validateShards()
|
||||||
{
|
{
|
||||||
assert(m_overlay);
|
assert(overlay_);
|
||||||
assert(!config_->standalone());
|
assert(!config_->standalone());
|
||||||
|
|
||||||
if (config_->section(ConfigSection::shardDatabase()).empty())
|
if (config_->section(ConfigSection::shardDatabase()).empty())
|
||||||
|
|||||||
@@ -197,8 +197,10 @@ public:
|
|||||||
boost::optional<std::set<PeerShortID>> shouldRelay(uint256 const& key);
|
boost::optional<std::set<PeerShortID>> shouldRelay(uint256 const& key);
|
||||||
|
|
||||||
/** Determines whether the hashed item should be recovered
|
/** Determines whether the hashed item should be recovered
|
||||||
|
from the open ledger into the next open ledger or the transaction
|
||||||
|
queue.
|
||||||
|
|
||||||
@return `bool` indicates whether the item should be relayed
|
@return `bool` indicates whether the item should be recovered
|
||||||
*/
|
*/
|
||||||
bool shouldRecover(uint256 const& key);
|
bool shouldRecover(uint256 const& key);
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,10 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
// predeclaration
|
||||||
|
class Overlay;
|
||||||
|
class HashRouter;
|
||||||
|
|
||||||
enum class ListDisposition
|
enum class ListDisposition
|
||||||
{
|
{
|
||||||
/// List is valid
|
/// List is valid
|
||||||
@@ -123,11 +127,17 @@ class ValidatorList
|
|||||||
std::size_t sequence;
|
std::size_t sequence;
|
||||||
TimeKeeper::time_point expiration;
|
TimeKeeper::time_point expiration;
|
||||||
std::string siteUri;
|
std::string siteUri;
|
||||||
|
std::string rawManifest;
|
||||||
|
std::string rawBlob;
|
||||||
|
std::string rawSignature;
|
||||||
|
std::uint32_t rawVersion;
|
||||||
|
uint256 hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
ManifestCache& validatorManifests_;
|
ManifestCache& validatorManifests_;
|
||||||
ManifestCache& publisherManifests_;
|
ManifestCache& publisherManifests_;
|
||||||
TimeKeeper& timeKeeper_;
|
TimeKeeper& timeKeeper_;
|
||||||
|
boost::filesystem::path const dataPath_;
|
||||||
beast::Journal const j_;
|
beast::Journal const j_;
|
||||||
std::shared_timed_mutex mutable mutex_;
|
std::shared_timed_mutex mutable mutex_;
|
||||||
|
|
||||||
@@ -147,16 +157,45 @@ class ValidatorList
|
|||||||
|
|
||||||
// Currently supported version of publisher list format
|
// Currently supported version of publisher list format
|
||||||
static constexpr std::uint32_t requiredListVersion = 1;
|
static constexpr std::uint32_t requiredListVersion = 1;
|
||||||
|
static const std::string filePrefix_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ValidatorList (
|
ValidatorList (
|
||||||
ManifestCache& validatorManifests,
|
ManifestCache& validatorManifests,
|
||||||
ManifestCache& publisherManifests,
|
ManifestCache& publisherManifests,
|
||||||
TimeKeeper& timeKeeper,
|
TimeKeeper& timeKeeper,
|
||||||
|
std::string const& databasePath,
|
||||||
beast::Journal j,
|
beast::Journal j,
|
||||||
boost::optional<std::size_t> minimumQuorum = boost::none);
|
boost::optional<std::size_t> minimumQuorum = boost::none);
|
||||||
~ValidatorList () = default;
|
~ValidatorList () = default;
|
||||||
|
|
||||||
|
/** Describes the result of processing a Validator List (UNL),
|
||||||
|
including some of the information from the list which can
|
||||||
|
be used by the caller to know which list publisher is
|
||||||
|
involved.
|
||||||
|
*/
|
||||||
|
struct PublisherListStats
|
||||||
|
{
|
||||||
|
explicit PublisherListStats(ListDisposition d)
|
||||||
|
: disposition(d)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PublisherListStats(ListDisposition d, PublicKey key,
|
||||||
|
bool avail, std::size_t seq)
|
||||||
|
: disposition(d)
|
||||||
|
, publisherKey(key)
|
||||||
|
, available(avail)
|
||||||
|
, sequence(seq)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ListDisposition disposition;
|
||||||
|
boost::optional<PublicKey> publisherKey;
|
||||||
|
bool available = false;
|
||||||
|
boost::optional<std::size_t> sequence;
|
||||||
|
};
|
||||||
|
|
||||||
/** Load configured trusted keys.
|
/** Load configured trusted keys.
|
||||||
|
|
||||||
@param localSigningKey This node's validation public key
|
@param localSigningKey This node's validation public key
|
||||||
@@ -180,6 +219,44 @@ public:
|
|||||||
std::vector<std::string> const& configKeys,
|
std::vector<std::string> const& configKeys,
|
||||||
std::vector<std::string> const& publisherKeys);
|
std::vector<std::string> const& publisherKeys);
|
||||||
|
|
||||||
|
/** Apply published list of public keys, then broadcast it to all
|
||||||
|
peers that have not seen it or sent it.
|
||||||
|
|
||||||
|
@param manifest base64-encoded publisher key manifest
|
||||||
|
|
||||||
|
@param blob base64-encoded json containing published validator list
|
||||||
|
|
||||||
|
@param signature Signature of the decoded blob
|
||||||
|
|
||||||
|
@param version Version of published list format
|
||||||
|
|
||||||
|
@param siteUri Uri of the site from which the list was validated
|
||||||
|
|
||||||
|
@param hash Hash of the data parameters
|
||||||
|
|
||||||
|
@param overlay Overlay object which will handle sending the message
|
||||||
|
|
||||||
|
@param hashRouter HashRouter object which will determine which
|
||||||
|
peers not to send to
|
||||||
|
|
||||||
|
@return `ListDisposition::accepted`, plus some of the publisher
|
||||||
|
information, if list was successfully applied
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
|
||||||
|
May be called concurrently
|
||||||
|
*/
|
||||||
|
PublisherListStats
|
||||||
|
applyListAndBroadcast (
|
||||||
|
std::string const& manifest,
|
||||||
|
std::string const& blob,
|
||||||
|
std::string const& signature,
|
||||||
|
std::uint32_t version,
|
||||||
|
std::string siteUri,
|
||||||
|
uint256 const& hash,
|
||||||
|
Overlay& overlay,
|
||||||
|
HashRouter& hashRouter);
|
||||||
|
|
||||||
/** Apply published list of public keys
|
/** Apply published list of public keys
|
||||||
|
|
||||||
@param manifest base64-encoded publisher key manifest
|
@param manifest base64-encoded publisher key manifest
|
||||||
@@ -190,19 +267,38 @@ public:
|
|||||||
|
|
||||||
@param version Version of published list format
|
@param version Version of published list format
|
||||||
|
|
||||||
@return `ListDisposition::accepted` if list was successfully applied
|
@param siteUri Uri of the site from which the list was validated
|
||||||
|
|
||||||
|
@param hash Optional hash of the data parameters.
|
||||||
|
Defaults to uninitialized
|
||||||
|
|
||||||
|
@return `ListDisposition::accepted`, plus some of the publisher
|
||||||
|
information, if list was successfully applied
|
||||||
|
|
||||||
@par Thread Safety
|
@par Thread Safety
|
||||||
|
|
||||||
May be called concurrently
|
May be called concurrently
|
||||||
*/
|
*/
|
||||||
ListDisposition
|
PublisherListStats
|
||||||
applyList (
|
applyList (
|
||||||
std::string const& manifest,
|
std::string const& manifest,
|
||||||
std::string const& blob,
|
std::string const& blob,
|
||||||
std::string const& signature,
|
std::string const& signature,
|
||||||
std::uint32_t version,
|
std::uint32_t version,
|
||||||
std::string siteUri);
|
std::string siteUri,
|
||||||
|
boost::optional<uint256> const& hash = {});
|
||||||
|
|
||||||
|
/* Attempt to read previously stored list files. Expected to only be
|
||||||
|
called when loading from URL fails.
|
||||||
|
|
||||||
|
@return A list of valid file:// URLs, if any.
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
|
||||||
|
May be called concurrently
|
||||||
|
*/
|
||||||
|
std::vector<std::string>
|
||||||
|
loadLists();
|
||||||
|
|
||||||
/** Update trusted nodes
|
/** Update trusted nodes
|
||||||
|
|
||||||
@@ -333,6 +429,47 @@ public:
|
|||||||
for_each_listed (
|
for_each_listed (
|
||||||
std::function<void(PublicKey const&, bool)> func) const;
|
std::function<void(PublicKey const&, bool)> func) const;
|
||||||
|
|
||||||
|
/** Invokes the callback once for every available publisher list's raw
|
||||||
|
data members
|
||||||
|
|
||||||
|
@note Undefined behavior results when calling ValidatorList members
|
||||||
|
from within the callback
|
||||||
|
|
||||||
|
The arguments passed into the lambda are:
|
||||||
|
|
||||||
|
@li The raw manifest string
|
||||||
|
|
||||||
|
@li The raw "blob" string containing the values for the validator list
|
||||||
|
|
||||||
|
@li The signature string used to sign the blob
|
||||||
|
|
||||||
|
@li The version number
|
||||||
|
|
||||||
|
@li The `PublicKey` of the blob signer (matches the value from
|
||||||
|
[validator_list_keys])
|
||||||
|
|
||||||
|
@li The sequence number of the "blob"
|
||||||
|
|
||||||
|
@li The precomputed hash of the original / raw elements
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
|
||||||
|
May be called concurrently
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
for_each_available (
|
||||||
|
std::function<void(std::string const& manifest,
|
||||||
|
std::string const& blob, std::string const& signature,
|
||||||
|
std::uint32_t version,
|
||||||
|
PublicKey const& pubKey, std::size_t sequence,
|
||||||
|
uint256 const& hash)> func) const;
|
||||||
|
|
||||||
|
/** Returns the current valid list for the given publisher key,
|
||||||
|
if available, as a Json object.
|
||||||
|
*/
|
||||||
|
boost::optional<Json::Value>
|
||||||
|
getAvailable(boost::beast::string_view const& pubKey);
|
||||||
|
|
||||||
/** Return the number of configured validator list sites. */
|
/** Return the number of configured validator list sites. */
|
||||||
std::size_t
|
std::size_t
|
||||||
count() const;
|
count() const;
|
||||||
@@ -371,6 +508,17 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/** Get the filename used for caching UNLs
|
||||||
|
*/
|
||||||
|
boost::filesystem::path
|
||||||
|
GetCacheFileName(PublicKey const& pubKey);
|
||||||
|
|
||||||
|
/** Write a JSON UNL to a cache file
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
CacheValidatorFile(PublicKey const& pubKey,
|
||||||
|
PublisherList const& publisher);
|
||||||
|
|
||||||
/** Check response for trusted valid published list
|
/** Check response for trusted valid published list
|
||||||
|
|
||||||
@return `ListDisposition::accepted` if list can be applied
|
@return `ListDisposition::accepted` if list can be applied
|
||||||
|
|||||||
@@ -244,6 +244,11 @@ private:
|
|||||||
detail::response_type& res,
|
detail::response_type& res,
|
||||||
std::size_t siteIdx,
|
std::size_t siteIdx,
|
||||||
std::lock_guard<std::mutex>& lock);
|
std::lock_guard<std::mutex>& lock);
|
||||||
|
|
||||||
|
/// If no sites are provided, or a site fails to load,
|
||||||
|
/// get a list of local cache files from the ValidatorList.
|
||||||
|
bool
|
||||||
|
missingSite();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -18,11 +18,15 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <ripple/app/misc/ValidatorList.h>
|
#include <ripple/app/misc/ValidatorList.h>
|
||||||
|
#include <ripple/app/misc/HashRouter.h>
|
||||||
#include <ripple/basics/base64.h>
|
#include <ripple/basics/base64.h>
|
||||||
|
#include <ripple/basics/FileUtilities.h>
|
||||||
#include <ripple/basics/Slice.h>
|
#include <ripple/basics/Slice.h>
|
||||||
#include <ripple/basics/StringUtilities.h>
|
#include <ripple/basics/StringUtilities.h>
|
||||||
#include <ripple/json/json_reader.h>
|
#include <ripple/json/json_reader.h>
|
||||||
|
#include <ripple/overlay/Overlay.h>
|
||||||
#include <ripple/protocol/jss.h>
|
#include <ripple/protocol/jss.h>
|
||||||
|
#include <ripple/protocol/messages.h>
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
#include <date/date.h>
|
#include <date/date.h>
|
||||||
@@ -54,15 +58,19 @@ to_string(ListDisposition disposition)
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string ValidatorList::filePrefix_ = "cache.";
|
||||||
|
|
||||||
ValidatorList::ValidatorList (
|
ValidatorList::ValidatorList (
|
||||||
ManifestCache& validatorManifests,
|
ManifestCache& validatorManifests,
|
||||||
ManifestCache& publisherManifests,
|
ManifestCache& publisherManifests,
|
||||||
TimeKeeper& timeKeeper,
|
TimeKeeper& timeKeeper,
|
||||||
|
std::string const& databasePath,
|
||||||
beast::Journal j,
|
beast::Journal j,
|
||||||
boost::optional<std::size_t> minimumQuorum)
|
boost::optional<std::size_t> minimumQuorum)
|
||||||
: validatorManifests_ (validatorManifests)
|
: validatorManifests_ (validatorManifests)
|
||||||
, publisherManifests_ (publisherManifests)
|
, publisherManifests_ (publisherManifests)
|
||||||
, timeKeeper_ (timeKeeper)
|
, timeKeeper_ (timeKeeper)
|
||||||
|
, dataPath_(databasePath)
|
||||||
, j_ (j)
|
, j_ (j)
|
||||||
, quorum_ (minimumQuorum.value_or(1)) // Genesis ledger quorum
|
, quorum_ (minimumQuorum.value_or(1)) // Genesis ledger quorum
|
||||||
, minimumQuorum_ (minimumQuorum)
|
, minimumQuorum_ (minimumQuorum)
|
||||||
@@ -192,16 +200,122 @@ ValidatorList::load (
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListDisposition
|
boost::filesystem::path
|
||||||
|
ValidatorList::GetCacheFileName(PublicKey const& pubKey)
|
||||||
|
{
|
||||||
|
return dataPath_ / (filePrefix_ + strHex(pubKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ValidatorList::CacheValidatorFile(PublicKey const& pubKey,
|
||||||
|
PublisherList const& publisher)
|
||||||
|
{
|
||||||
|
if (dataPath_.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
boost::filesystem::path const filename =
|
||||||
|
GetCacheFileName(pubKey);
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
|
||||||
|
Json::Value value(Json::objectValue);
|
||||||
|
|
||||||
|
value["manifest"] = publisher.rawManifest;
|
||||||
|
value["blob"] = publisher.rawBlob;
|
||||||
|
value["signature"] = publisher.rawSignature;
|
||||||
|
value["version"] = publisher.rawVersion;
|
||||||
|
|
||||||
|
writeFileContents(ec, filename, value.toStyledString());
|
||||||
|
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
// Log and ignore any file I/O exceptions
|
||||||
|
JLOG(j_.error()) <<
|
||||||
|
"Problem writing " <<
|
||||||
|
filename <<
|
||||||
|
" " <<
|
||||||
|
ec.value() <<
|
||||||
|
": " <<
|
||||||
|
ec.message();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidatorList::PublisherListStats
|
||||||
|
ValidatorList::applyListAndBroadcast(
|
||||||
|
std::string const& manifest,
|
||||||
|
std::string const& blob,
|
||||||
|
std::string const& signature,
|
||||||
|
std::uint32_t version,
|
||||||
|
std::string siteUri,
|
||||||
|
uint256 const& hash,
|
||||||
|
Overlay& overlay,
|
||||||
|
HashRouter& hashRouter)
|
||||||
|
{
|
||||||
|
auto const result = applyList(manifest, blob, signature,
|
||||||
|
version, std::move(siteUri), hash);
|
||||||
|
auto const disposition = result.disposition;
|
||||||
|
|
||||||
|
bool broadcast = disposition == ListDisposition::accepted ||
|
||||||
|
disposition == ListDisposition::same_sequence;
|
||||||
|
|
||||||
|
if (broadcast)
|
||||||
|
{
|
||||||
|
assert(result.available && result.publisherKey && result.sequence);
|
||||||
|
auto const toSkip = hashRouter.shouldRelay(hash);
|
||||||
|
|
||||||
|
if (toSkip)
|
||||||
|
{
|
||||||
|
protocol::TMValidatorList msg;
|
||||||
|
msg.set_manifest(manifest);
|
||||||
|
msg.set_blob(blob);
|
||||||
|
msg.set_signature(signature);
|
||||||
|
msg.set_version(version);
|
||||||
|
|
||||||
|
auto const& publisherKey = *result.publisherKey;
|
||||||
|
auto const sequence = *result.sequence;
|
||||||
|
|
||||||
|
// Can't use overlay.foreach here because we need to modify
|
||||||
|
// the peer, and foreach provides a const&
|
||||||
|
auto message =
|
||||||
|
std::make_shared<Message>(msg, protocol::mtVALIDATORLIST);
|
||||||
|
for (auto& peer : overlay.getActivePeers())
|
||||||
|
{
|
||||||
|
if (toSkip->count(peer->id()) == 0 &&
|
||||||
|
peer->supportsFeature(
|
||||||
|
ProtocolFeature::ValidatorListPropagation) &&
|
||||||
|
peer->publisherListSequence(publisherKey) < sequence)
|
||||||
|
{
|
||||||
|
peer->send(message);
|
||||||
|
|
||||||
|
JLOG(j_.debug())
|
||||||
|
<< "Sent validator list for " << strHex(publisherKey)
|
||||||
|
<< " with sequence " << sequence << " to "
|
||||||
|
<< peer->getRemoteAddress().to_string() << " ("
|
||||||
|
<< peer->id() << ")";
|
||||||
|
// Don't send it next time.
|
||||||
|
hashRouter.addSuppressionPeer(hash, peer->id());
|
||||||
|
peer->setPublisherListSequence(publisherKey, sequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidatorList::PublisherListStats
|
||||||
ValidatorList::applyList (
|
ValidatorList::applyList (
|
||||||
std::string const& manifest,
|
std::string const& manifest,
|
||||||
std::string const& blob,
|
std::string const& blob,
|
||||||
std::string const& signature,
|
std::string const& signature,
|
||||||
std::uint32_t version,
|
std::uint32_t version,
|
||||||
std::string siteUri)
|
std::string siteUri,
|
||||||
|
boost::optional<uint256> const& hash)
|
||||||
{
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
if (version != requiredListVersion)
|
if (version != requiredListVersion)
|
||||||
return ListDisposition::unsupported_version;
|
return PublisherListStats{ ListDisposition::unsupported_version };
|
||||||
|
|
||||||
std::unique_lock<std::shared_timed_mutex> lock{mutex_};
|
std::unique_lock<std::shared_timed_mutex> lock{mutex_};
|
||||||
|
|
||||||
@@ -209,16 +323,37 @@ ValidatorList::applyList (
|
|||||||
PublicKey pubKey;
|
PublicKey pubKey;
|
||||||
auto const result = verify (list, pubKey, manifest, blob, signature);
|
auto const result = verify (list, pubKey, manifest, blob, signature);
|
||||||
if (result != ListDisposition::accepted)
|
if (result != ListDisposition::accepted)
|
||||||
return result;
|
{
|
||||||
|
if (result == ListDisposition::same_sequence &&
|
||||||
|
publisherLists_.count(pubKey))
|
||||||
|
{
|
||||||
|
// We've seen this valid list already, so return
|
||||||
|
// what we know about it.
|
||||||
|
auto const& publisher = publisherLists_[pubKey];
|
||||||
|
return PublisherListStats{ result, pubKey,
|
||||||
|
publisher.available, publisher.sequence };
|
||||||
|
}
|
||||||
|
return PublisherListStats{ result };
|
||||||
|
}
|
||||||
|
|
||||||
// Update publisher's list
|
// Update publisher's list
|
||||||
Json::Value const& newList = list["validators"];
|
Json::Value const& newList = list["validators"];
|
||||||
publisherLists_[pubKey].available = true;
|
auto& publisher = publisherLists_[pubKey];
|
||||||
publisherLists_[pubKey].sequence = list["sequence"].asUInt ();
|
publisher.available = true;
|
||||||
publisherLists_[pubKey].expiration = TimeKeeper::time_point{
|
publisher.sequence = list["sequence"].asUInt ();
|
||||||
|
publisher.expiration = TimeKeeper::time_point{
|
||||||
TimeKeeper::duration{list["expiration"].asUInt()}};
|
TimeKeeper::duration{list["expiration"].asUInt()}};
|
||||||
publisherLists_[pubKey].siteUri = std::move(siteUri);
|
publisher.siteUri = std::move(siteUri);
|
||||||
std::vector<PublicKey>& publisherList = publisherLists_[pubKey].list;
|
publisher.rawManifest = manifest;
|
||||||
|
publisher.rawBlob = blob;
|
||||||
|
publisher.rawSignature = signature;
|
||||||
|
publisher.rawVersion = version;
|
||||||
|
if(hash)
|
||||||
|
publisher.hash = *hash;
|
||||||
|
std::vector<PublicKey>& publisherList = publisher.list;
|
||||||
|
|
||||||
|
PublisherListStats const applyResult{ result, pubKey,
|
||||||
|
publisher.available, publisher.sequence };
|
||||||
|
|
||||||
std::vector<PublicKey> oldList = publisherList;
|
std::vector<PublicKey> oldList = publisherList;
|
||||||
publisherList.clear ();
|
publisherList.clear ();
|
||||||
@@ -311,7 +446,65 @@ ValidatorList::applyList (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListDisposition::accepted;
|
// Cache the validator list in a file
|
||||||
|
CacheValidatorFile(pubKey, publisher);
|
||||||
|
|
||||||
|
return applyResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
ValidatorList::loadLists()
|
||||||
|
{
|
||||||
|
using namespace std::string_literals;
|
||||||
|
using namespace boost::filesystem;
|
||||||
|
using namespace boost::system::errc;
|
||||||
|
|
||||||
|
std::unique_lock<std::shared_timed_mutex> lock{mutex_};
|
||||||
|
|
||||||
|
std::vector<std::string> sites;
|
||||||
|
sites.reserve(publisherLists_.size());
|
||||||
|
for (auto const& [pubKey, publisher] : publisherLists_)
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
|
||||||
|
if (publisher.available)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
boost::filesystem::path const filename =
|
||||||
|
GetCacheFileName(pubKey);
|
||||||
|
|
||||||
|
auto const fullPath{ canonical(filename, ec) };
|
||||||
|
if (ec)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto size = file_size(fullPath, ec);
|
||||||
|
if (!ec && !size)
|
||||||
|
{
|
||||||
|
// Treat an empty file as a missing file, because
|
||||||
|
// nobody else is going to write it.
|
||||||
|
ec = make_error_code(no_such_file_or_directory);
|
||||||
|
}
|
||||||
|
if (ec)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string const prefix = [&fullPath]() {
|
||||||
|
#if _MSC_VER // MSVC: Windows paths need a leading / added
|
||||||
|
{
|
||||||
|
return fullPath.root_path() == "/"s ?
|
||||||
|
"file://" : "file:///";
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
(void)fullPath;
|
||||||
|
return "file://";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}();
|
||||||
|
sites.emplace_back(prefix + fullPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then let the ValidatorSites do the rest of the work.
|
||||||
|
return sites;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListDisposition
|
ListDisposition
|
||||||
@@ -594,6 +787,57 @@ ValidatorList::for_each_listed (
|
|||||||
func (v.first, trusted(v.first));
|
func (v.first, trusted(v.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ValidatorList::for_each_available (
|
||||||
|
std::function<void(std::string const& manifest,
|
||||||
|
std::string const& blob, std::string const& signature,
|
||||||
|
std::uint32_t version,
|
||||||
|
PublicKey const& pubKey, std::size_t sequence,
|
||||||
|
uint256 const& hash)> func) const
|
||||||
|
{
|
||||||
|
std::shared_lock<std::shared_timed_mutex> read_lock{mutex_};
|
||||||
|
|
||||||
|
for (auto const& [key, pl] : publisherLists_)
|
||||||
|
{
|
||||||
|
if (!pl.available)
|
||||||
|
continue;
|
||||||
|
func(pl.rawManifest, pl.rawBlob, pl.rawSignature, pl.rawVersion,
|
||||||
|
key, pl.sequence, pl.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<Json::Value>
|
||||||
|
ValidatorList::getAvailable(boost::beast::string_view const& pubKey)
|
||||||
|
{
|
||||||
|
std::shared_lock<std::shared_timed_mutex> read_lock{mutex_};
|
||||||
|
|
||||||
|
auto const keyBlob = strViewUnHex (pubKey);
|
||||||
|
|
||||||
|
if (! keyBlob || ! publicKeyType(makeSlice(*keyBlob)))
|
||||||
|
{
|
||||||
|
JLOG (j_.info()) <<
|
||||||
|
"Invalid requested validator list publisher key: " << pubKey;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto id = PublicKey(makeSlice(*keyBlob));
|
||||||
|
|
||||||
|
auto iter = publisherLists_.find(id);
|
||||||
|
|
||||||
|
if (iter == publisherLists_.end()
|
||||||
|
|| !iter->second.available)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
Json::Value value(Json::objectValue);
|
||||||
|
|
||||||
|
value["manifest"] = iter->second.rawManifest;
|
||||||
|
value["blob"] = iter->second.rawBlob;
|
||||||
|
value["signature"] = iter->second.rawSignature;
|
||||||
|
value["version"] = iter->second.rawVersion;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
ValidatorList::calculateQuorum (
|
ValidatorList::calculateQuorum (
|
||||||
std::size_t trusted, std::size_t seen)
|
std::size_t trusted, std::size_t seen)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <ripple/basics/base64.h>
|
#include <ripple/basics/base64.h>
|
||||||
#include <ripple/basics/Slice.h>
|
#include <ripple/basics/Slice.h>
|
||||||
#include <ripple/json/json_reader.h>
|
#include <ripple/json/json_reader.h>
|
||||||
|
#include <ripple/protocol/digest.h>
|
||||||
#include <ripple/protocol/jss.h>
|
#include <ripple/protocol/jss.h>
|
||||||
#include <boost/algorithm/clamp.hpp>
|
#include <boost/algorithm/clamp.hpp>
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
@@ -116,10 +117,23 @@ ValidatorSite::~ValidatorSite()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ValidatorSite::missingSite()
|
||||||
|
{
|
||||||
|
auto const sites = app_.validators().loadLists();
|
||||||
|
return sites.empty() || load(sites);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ValidatorSite::load (
|
ValidatorSite::load (
|
||||||
std::vector<std::string> const& siteURIs)
|
std::vector<std::string> const& siteURIs)
|
||||||
{
|
{
|
||||||
|
// If no sites are provided, act as if a site failed to load.
|
||||||
|
if (siteURIs.empty())
|
||||||
|
{
|
||||||
|
return missingSite();
|
||||||
|
}
|
||||||
|
|
||||||
JLOG (j_.debug()) <<
|
JLOG (j_.debug()) <<
|
||||||
"Loading configured validator list sites";
|
"Loading configured validator list sites";
|
||||||
|
|
||||||
@@ -374,54 +388,59 @@ ValidatorSite::parseJsonResponse (
|
|||||||
throw std::runtime_error{"missing fields"};
|
throw std::runtime_error{"missing fields"};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const disp = app_.validators().applyList (
|
auto const manifest = body["manifest"].asString ();
|
||||||
body["manifest"].asString (),
|
auto const blob = body["blob"].asString ();
|
||||||
body["blob"].asString (),
|
auto const signature = body["signature"].asString();
|
||||||
body["signature"].asString(),
|
auto const version = body["version"].asUInt();
|
||||||
body["version"].asUInt(),
|
auto const& uri = sites_[siteIdx].activeResource->uri;
|
||||||
sites_[siteIdx].activeResource->uri);
|
auto const hash = sha512Half(manifest, blob, signature, version);
|
||||||
|
auto const applyResult = app_.validators().applyListAndBroadcast (
|
||||||
|
manifest,
|
||||||
|
blob,
|
||||||
|
signature,
|
||||||
|
version,
|
||||||
|
uri,
|
||||||
|
hash,
|
||||||
|
app_.overlay(),
|
||||||
|
app_.getHashRouter());
|
||||||
|
auto const disp = applyResult.disposition;
|
||||||
|
|
||||||
sites_[siteIdx].lastRefreshStatus.emplace(
|
sites_[siteIdx].lastRefreshStatus.emplace(
|
||||||
Site::Status{clock_type::now(), disp, ""});
|
Site::Status{clock_type::now(), disp, ""});
|
||||||
|
|
||||||
if (ListDisposition::accepted == disp)
|
switch (disp)
|
||||||
{
|
{
|
||||||
|
case ListDisposition::accepted:
|
||||||
JLOG (j_.debug()) <<
|
JLOG (j_.debug()) <<
|
||||||
"Applied new validator list from " <<
|
"Applied new validator list from " <<
|
||||||
sites_[siteIdx].activeResource->uri;
|
uri;
|
||||||
}
|
break;
|
||||||
else if (ListDisposition::same_sequence == disp)
|
case ListDisposition::same_sequence:
|
||||||
{
|
|
||||||
JLOG (j_.debug()) <<
|
JLOG (j_.debug()) <<
|
||||||
"Validator list with current sequence from " <<
|
"Validator list with current sequence from " <<
|
||||||
sites_[siteIdx].activeResource->uri;
|
uri;
|
||||||
}
|
break;
|
||||||
else if (ListDisposition::stale == disp)
|
case ListDisposition::stale:
|
||||||
{
|
|
||||||
JLOG (j_.warn()) <<
|
JLOG (j_.warn()) <<
|
||||||
"Stale validator list from " <<
|
"Stale validator list from " <<
|
||||||
sites_[siteIdx].activeResource->uri;
|
uri;
|
||||||
}
|
break;
|
||||||
else if (ListDisposition::untrusted == disp)
|
case ListDisposition::untrusted:
|
||||||
{
|
|
||||||
JLOG (j_.warn()) <<
|
JLOG (j_.warn()) <<
|
||||||
"Untrusted validator list from " <<
|
"Untrusted validator list from " <<
|
||||||
sites_[siteIdx].activeResource->uri;
|
uri;
|
||||||
}
|
break;
|
||||||
else if (ListDisposition::invalid == disp)
|
case ListDisposition::invalid:
|
||||||
{
|
|
||||||
JLOG (j_.warn()) <<
|
JLOG (j_.warn()) <<
|
||||||
"Invalid validator list from " <<
|
"Invalid validator list from " <<
|
||||||
sites_[siteIdx].activeResource->uri;
|
uri;
|
||||||
}
|
break;
|
||||||
else if (ListDisposition::unsupported_version == disp)
|
case ListDisposition::unsupported_version:
|
||||||
{
|
|
||||||
JLOG (j_.warn()) <<
|
JLOG (j_.warn()) <<
|
||||||
"Unsupported version validator list from " <<
|
"Unsupported version validator list from " <<
|
||||||
sites_[siteIdx].activeResource->uri;
|
uri;
|
||||||
}
|
break;
|
||||||
else
|
default:
|
||||||
{
|
|
||||||
BOOST_ASSERT(false);
|
BOOST_ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,6 +528,10 @@ ValidatorSite::onSiteFetch(
|
|||||||
if (retry)
|
if (retry)
|
||||||
sites_[siteIdx].nextRefresh =
|
sites_[siteIdx].nextRefresh =
|
||||||
clock_type::now() + error_retry_interval;
|
clock_type::now() + error_retry_interval;
|
||||||
|
|
||||||
|
// See if there's a copy saved locally from last time we
|
||||||
|
// saw the list.
|
||||||
|
missingSite();
|
||||||
};
|
};
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
@@ -592,7 +615,7 @@ ValidatorSite::onTextFetch(
|
|||||||
sites_[siteIdx].activeResource->uri <<
|
sites_[siteIdx].activeResource->uri <<
|
||||||
" " <<
|
" " <<
|
||||||
ec.value() <<
|
ec.value() <<
|
||||||
":" <<
|
": " <<
|
||||||
ec.message();
|
ec.message();
|
||||||
throw std::runtime_error{"fetch error"};
|
throw std::runtime_error{"fetch error"};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,13 +27,14 @@
|
|||||||
namespace ripple
|
namespace ripple
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO: Should this one function have its own file, or can it
|
|
||||||
// be absorbed somewhere else?
|
|
||||||
|
|
||||||
std::string getFileContents(boost::system::error_code& ec,
|
std::string getFileContents(boost::system::error_code& ec,
|
||||||
boost::filesystem::path const& sourcePath,
|
boost::filesystem::path const& sourcePath,
|
||||||
boost::optional<std::size_t> maxSize = boost::none);
|
boost::optional<std::size_t> maxSize = boost::none);
|
||||||
|
|
||||||
|
void writeFileContents(boost::system::error_code& ec,
|
||||||
|
boost::filesystem::path const& destPath,
|
||||||
|
std::string const& contents);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/utility/string_view.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -63,7 +64,60 @@ inline static std::string sqlEscape (Blob const& vecSrc)
|
|||||||
|
|
||||||
uint64_t uintFromHex (std::string const& strSrc);
|
uint64_t uintFromHex (std::string const& strSrc);
|
||||||
|
|
||||||
boost::optional<Blob> strUnHex (std::string const& strSrc);
|
template <class Iterator>
|
||||||
|
boost::optional<Blob>
|
||||||
|
strUnHex(std::size_t strSize, Iterator begin, Iterator end)
|
||||||
|
{
|
||||||
|
Blob out;
|
||||||
|
|
||||||
|
out.reserve((strSize + 1) / 2);
|
||||||
|
|
||||||
|
auto iter = begin;
|
||||||
|
|
||||||
|
if (strSize & 1)
|
||||||
|
{
|
||||||
|
int c = charUnHex(*iter);
|
||||||
|
|
||||||
|
if (c < 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
out.push_back(c);
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (iter != end)
|
||||||
|
{
|
||||||
|
int cHigh = charUnHex(*iter);
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
if (cHigh < 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
int cLow = charUnHex(*iter);
|
||||||
|
++iter;
|
||||||
|
|
||||||
|
if (cLow < 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
out.push_back(static_cast<unsigned char>((cHigh << 4) | cLow));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {std::move(out)};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
boost::optional<Blob>
|
||||||
|
strUnHex (std::string const& strSrc)
|
||||||
|
{
|
||||||
|
return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
boost::optional<Blob>
|
||||||
|
strViewUnHex (boost::string_view const& strSrc)
|
||||||
|
{
|
||||||
|
return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend());
|
||||||
|
}
|
||||||
|
|
||||||
struct parsedURL
|
struct parsedURL
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,4 +60,28 @@ std::string getFileContents(boost::system::error_code& ec,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void writeFileContents(boost::system::error_code& ec,
|
||||||
|
boost::filesystem::path const& destPath,
|
||||||
|
std::string const& contents)
|
||||||
|
{
|
||||||
|
using namespace boost::filesystem;
|
||||||
|
using namespace boost::system::errc;
|
||||||
|
|
||||||
|
ofstream fileStream(destPath, std::ios::out | std::ios::trunc);
|
||||||
|
|
||||||
|
if (!fileStream)
|
||||||
|
{
|
||||||
|
ec = make_error_code(static_cast<errc_t>(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStream << contents;
|
||||||
|
|
||||||
|
if (fileStream.bad ())
|
||||||
|
{
|
||||||
|
ec = make_error_code(static_cast<errc_t>(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,45 +30,6 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
boost::optional<Blob> strUnHex (std::string const& strSrc)
|
|
||||||
{
|
|
||||||
Blob out;
|
|
||||||
|
|
||||||
out.reserve ((strSrc.size () + 1) / 2);
|
|
||||||
|
|
||||||
auto iter = strSrc.cbegin ();
|
|
||||||
|
|
||||||
if (strSrc.size () & 1)
|
|
||||||
{
|
|
||||||
int c = charUnHex (*iter);
|
|
||||||
|
|
||||||
if (c < 0)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
out.push_back(c);
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (iter != strSrc.cend ())
|
|
||||||
{
|
|
||||||
int cHigh = charUnHex (*iter);
|
|
||||||
++iter;
|
|
||||||
|
|
||||||
if (cHigh < 0)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
int cLow = charUnHex (*iter);
|
|
||||||
++iter;
|
|
||||||
|
|
||||||
if (cLow < 0)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
out.push_back (static_cast<unsigned char>((cHigh << 4) | cLow));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {std::move(out)};
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t uintFromHex (std::string const& strSrc)
|
uint64_t uintFromHex (std::string const& strSrc)
|
||||||
{
|
{
|
||||||
uint64_t uValue (0);
|
uint64_t uValue (0);
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public:
|
|||||||
int ipLimit = 0;
|
int ipLimit = 0;
|
||||||
std::uint32_t crawlOptions = 0;
|
std::uint32_t crawlOptions = 0;
|
||||||
boost::optional<std::uint32_t> networkID;
|
boost::optional<std::uint32_t> networkID;
|
||||||
|
bool vlEnabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
using PeerSequence = std::vector <std::shared_ptr<Peer>>;
|
using PeerSequence = std::vector <std::shared_ptr<Peer>>;
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ class Charge;
|
|||||||
// Maximum hops to attempt when crawling shards. cs = crawl shards
|
// Maximum hops to attempt when crawling shards. cs = crawl shards
|
||||||
static constexpr std::uint32_t csHopLimit = 3;
|
static constexpr std::uint32_t csHopLimit = 3;
|
||||||
|
|
||||||
|
enum class ProtocolFeature {
|
||||||
|
ValidatorListPropagation,
|
||||||
|
};
|
||||||
|
|
||||||
/** Represents a peer connection in the overlay. */
|
/** Represents a peer connection in the overlay. */
|
||||||
class Peer
|
class Peer
|
||||||
{
|
{
|
||||||
@@ -95,6 +99,17 @@ public:
|
|||||||
virtual
|
virtual
|
||||||
Json::Value json() = 0;
|
Json::Value json() = 0;
|
||||||
|
|
||||||
|
virtual bool
|
||||||
|
supportsFeature(ProtocolFeature f) const = 0;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
boost::optional<std::size_t>
|
||||||
|
publisherListSequence(PublicKey const&) const = 0;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
setPublisherListSequence(PublicKey const&, std::size_t const) = 0;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Ledger
|
// Ledger
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1016,7 +1016,7 @@ OverlayImpl::json ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
OverlayImpl::processRequest (http_request_type const& req,
|
OverlayImpl::processCrawl (http_request_type const& req,
|
||||||
Handoff& handoff)
|
Handoff& handoff)
|
||||||
{
|
{
|
||||||
if (req.target() != "/crawl" || setup_.crawlOptions == CrawlOptions::Disabled)
|
if (req.target() != "/crawl" || setup_.crawlOptions == CrawlOptions::Disabled)
|
||||||
@@ -1052,6 +1052,62 @@ OverlayImpl::processRequest (http_request_type const& req,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
OverlayImpl::processValidatorList (http_request_type const& req,
|
||||||
|
Handoff& handoff)
|
||||||
|
{
|
||||||
|
// If the target is in the form "/vl/<validator_list_public_key>",
|
||||||
|
// return the most recent validator list for that key.
|
||||||
|
if (!req.target().starts_with("/vl/") ||
|
||||||
|
!setup_.vlEnabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto key = req.target();
|
||||||
|
if (key.starts_with("/vl/"))
|
||||||
|
key.remove_prefix(strlen("/vl/"));
|
||||||
|
else
|
||||||
|
key.remove_prefix(strlen("/unl/"));
|
||||||
|
if(key.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// find the list
|
||||||
|
auto vl = app_.validators().getAvailable(key);
|
||||||
|
|
||||||
|
boost::beast::http::response<json_body> msg;
|
||||||
|
msg.version(req.version());
|
||||||
|
msg.insert("Server", BuildInfo::getFullVersionString());
|
||||||
|
msg.insert("Content-Type", "application/json");
|
||||||
|
msg.insert("Connection", "close");
|
||||||
|
|
||||||
|
if (!vl)
|
||||||
|
{
|
||||||
|
// 404 not found
|
||||||
|
msg.result(boost::beast::http::status::not_found);
|
||||||
|
msg.insert("Content-Length", "0");
|
||||||
|
|
||||||
|
msg.body() = Json::nullValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg.result(boost::beast::http::status::ok);
|
||||||
|
|
||||||
|
msg.body() = *vl;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.prepare_payload();
|
||||||
|
handoff.response = std::make_shared<SimpleWriter>(msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
OverlayImpl::processRequest (http_request_type const& req,
|
||||||
|
Handoff& handoff)
|
||||||
|
{
|
||||||
|
// Take advantage of || short-circuiting
|
||||||
|
return processCrawl(req, handoff) ||
|
||||||
|
processValidatorList(req, handoff);
|
||||||
|
}
|
||||||
|
|
||||||
Overlay::PeerSequence
|
Overlay::PeerSequence
|
||||||
OverlayImpl::getActivePeers()
|
OverlayImpl::getActivePeers()
|
||||||
{
|
{
|
||||||
@@ -1331,6 +1387,11 @@ setup_Overlay (BasicConfig const& config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto const& section = config.section("vl");
|
||||||
|
|
||||||
|
set(setup.vlEnabled, "enabled", section);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -385,6 +385,30 @@ private:
|
|||||||
http_request_type const& request, address_type remote_address,
|
http_request_type const& request, address_type remote_address,
|
||||||
std::string msg);
|
std::string msg);
|
||||||
|
|
||||||
|
/** Handles crawl requests. Crawl returns information about the
|
||||||
|
node and its peers so crawlers can map the network.
|
||||||
|
|
||||||
|
@return true if the request was handled.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
processCrawl (http_request_type const& req,
|
||||||
|
Handoff& handoff);
|
||||||
|
|
||||||
|
/** Handles validator list requests.
|
||||||
|
Using a /vl/<hex-encoded public key> URL, will retrieve the
|
||||||
|
latest valdiator list (or UNL) that this node has for that
|
||||||
|
public key, if the node trusts that public key.
|
||||||
|
|
||||||
|
@return true if the request was handled.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
processValidatorList (http_request_type const& req,
|
||||||
|
Handoff& handoff);
|
||||||
|
|
||||||
|
/** Handles non-peer protocol requests.
|
||||||
|
|
||||||
|
@return true if the request was handled.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
processRequest (http_request_type const& req,
|
processRequest (http_request_type const& req,
|
||||||
Handoff& handoff);
|
Handoff& handoff);
|
||||||
|
|||||||
@@ -405,6 +405,17 @@ PeerImp::json()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PeerImp::supportsFeature(ProtocolFeature f) const
|
||||||
|
{
|
||||||
|
switch (f)
|
||||||
|
{
|
||||||
|
case ProtocolFeature::ValidatorListPropagation:
|
||||||
|
return protocol_ >= make_protocol(2, 1);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -803,6 +814,36 @@ PeerImp::doProtocolStart()
|
|||||||
{
|
{
|
||||||
onReadMessage(error_code(), 0);
|
onReadMessage(error_code(), 0);
|
||||||
|
|
||||||
|
// Send all the validator lists that have been loaded
|
||||||
|
if (supportsFeature(ProtocolFeature::ValidatorListPropagation))
|
||||||
|
{
|
||||||
|
app_.validators().for_each_available(
|
||||||
|
[&](std::string const& manifest,
|
||||||
|
std::string const& blob, std::string const& signature,
|
||||||
|
std::uint32_t version,
|
||||||
|
PublicKey const& pubKey, std::size_t sequence,
|
||||||
|
uint256 const& hash)
|
||||||
|
{
|
||||||
|
protocol::TMValidatorList vl;
|
||||||
|
|
||||||
|
vl.set_manifest(manifest);
|
||||||
|
vl.set_blob(blob);
|
||||||
|
vl.set_signature(signature);
|
||||||
|
vl.set_version(version);
|
||||||
|
|
||||||
|
JLOG(p_journal_.debug()) << "Sending validator list for " <<
|
||||||
|
strHex(pubKey) << " with sequence " <<
|
||||||
|
sequence << " to " <<
|
||||||
|
remote_address_.to_string() << " (" << id_ << ")";
|
||||||
|
auto m = std::make_shared<Message>(vl, protocol::mtVALIDATORLIST);
|
||||||
|
send(m);
|
||||||
|
// Don't send it next time.
|
||||||
|
app_.getHashRouter().addSuppressionPeer(hash, id_);
|
||||||
|
setPublisherListSequence(pubKey, sequence);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protocol::TMManifests tm;
|
protocol::TMManifests tm;
|
||||||
|
|
||||||
app_.validatorManifests ().for_each_manifest (
|
app_.validatorManifests ().for_each_manifest (
|
||||||
@@ -1965,6 +2006,137 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMHaveTransactionSet> const& m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PeerImp::onMessage (std::shared_ptr <protocol::TMValidatorList> const& m)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!supportsFeature(ProtocolFeature::ValidatorListPropagation))
|
||||||
|
{
|
||||||
|
JLOG(p_journal_.debug())
|
||||||
|
<< "ValidatorList: received validator list from peer using "
|
||||||
|
<< "protocol version " << to_string(protocol_)
|
||||||
|
<< " which shouldn't support this feature.";
|
||||||
|
fee_ = Resource::feeUnwantedData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto const& manifest = m->manifest();
|
||||||
|
auto const& blob = m->blob();
|
||||||
|
auto const& signature = m->signature();
|
||||||
|
auto const version = m->version();
|
||||||
|
auto const hash = sha512Half(manifest, blob, signature, version);
|
||||||
|
|
||||||
|
JLOG(p_journal_.debug()) << "Received validator list from " <<
|
||||||
|
remote_address_.to_string() << " (" << id_ << ")";
|
||||||
|
|
||||||
|
if (! app_.getHashRouter ().addSuppressionPeer(hash, id_))
|
||||||
|
{
|
||||||
|
JLOG(p_journal_.debug()) <<
|
||||||
|
"ValidatorList: received duplicate validator list";
|
||||||
|
// Charging this fee here won't hurt the peer in the normal
|
||||||
|
// course of operation (ie. refresh every 5 minutes), but
|
||||||
|
// will add up if the peer is misbehaving.
|
||||||
|
fee_ = Resource::feeUnwantedData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const applyResult = app_.validators().applyListAndBroadcast (
|
||||||
|
manifest,
|
||||||
|
blob,
|
||||||
|
signature,
|
||||||
|
version,
|
||||||
|
remote_address_.to_string(),
|
||||||
|
hash,
|
||||||
|
app_.overlay(),
|
||||||
|
app_.getHashRouter());
|
||||||
|
auto const disp = applyResult.disposition;
|
||||||
|
|
||||||
|
JLOG(p_journal_.debug()) << "Processed validator list from " <<
|
||||||
|
(applyResult.publisherKey ? strHex(*applyResult.publisherKey) :
|
||||||
|
"unknown or invalid publisher") << " from " <<
|
||||||
|
remote_address_.to_string() << " (" << id_ << ") with result " <<
|
||||||
|
to_string(disp);
|
||||||
|
|
||||||
|
switch (disp)
|
||||||
|
{
|
||||||
|
case ListDisposition::accepted:
|
||||||
|
JLOG (p_journal_.debug()) <<
|
||||||
|
"Applied new validator list from peer " << remote_address_;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> sl(recentLock_);
|
||||||
|
|
||||||
|
assert(applyResult.sequence && applyResult.publisherKey);
|
||||||
|
auto const& pubKey = *applyResult.publisherKey;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (auto const iter = publisherListSequences_.find(pubKey);
|
||||||
|
iter != publisherListSequences_.end())
|
||||||
|
{
|
||||||
|
assert(iter->second < *applyResult.sequence);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
publisherListSequences_[pubKey] = *applyResult.sequence;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ListDisposition::same_sequence:
|
||||||
|
JLOG (p_journal_.warn()) <<
|
||||||
|
"Validator list with current sequence from peer " <<
|
||||||
|
remote_address_;
|
||||||
|
// Charging this fee here won't hurt the peer in the normal
|
||||||
|
// course of operation (ie. refresh every 5 minutes), but
|
||||||
|
// will add up if the peer is misbehaving.
|
||||||
|
fee_ = Resource::feeUnwantedData;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> sl(recentLock_);
|
||||||
|
assert(applyResult.sequence && applyResult.publisherKey);
|
||||||
|
assert(publisherListSequences_[*applyResult.publisherKey]
|
||||||
|
== *applyResult.sequence);
|
||||||
|
}
|
||||||
|
#endif // !NDEBUG
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ListDisposition::stale:
|
||||||
|
JLOG (p_journal_.warn()) <<
|
||||||
|
"Stale validator list from peer " << remote_address_;
|
||||||
|
// There are very few good reasons for a peer to send an
|
||||||
|
// old list, particularly more than once.
|
||||||
|
fee_ = Resource::feeBadData;
|
||||||
|
break;
|
||||||
|
case ListDisposition::untrusted:
|
||||||
|
JLOG (p_journal_.warn()) <<
|
||||||
|
"Untrusted validator list from peer " << remote_address_;
|
||||||
|
// Charging this fee here won't hurt the peer in the normal
|
||||||
|
// course of operation (ie. refresh every 5 minutes), but
|
||||||
|
// will add up if the peer is misbehaving.
|
||||||
|
fee_ = Resource::feeUnwantedData;
|
||||||
|
break;
|
||||||
|
case ListDisposition::invalid:
|
||||||
|
JLOG (p_journal_.warn()) <<
|
||||||
|
"Invalid validator list from peer " << remote_address_;
|
||||||
|
// This shouldn't ever happen with a well-behaved peer
|
||||||
|
fee_ = Resource::feeInvalidSignature;
|
||||||
|
break;
|
||||||
|
case ListDisposition::unsupported_version:
|
||||||
|
JLOG (p_journal_.warn()) <<
|
||||||
|
"Unsupported version validator list from peer " <<
|
||||||
|
remote_address_;
|
||||||
|
// During a version transition, this may be legitimate.
|
||||||
|
// If it happens frequently, that's probably bad.
|
||||||
|
fee_ = Resource::feeBadData;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
JLOG(p_journal_.warn()) <<
|
||||||
|
"ValidatorList: Exception, " << e.what() <<
|
||||||
|
" from peer " << remote_address_;
|
||||||
|
fee_ = Resource::feeBadData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PeerImp::onMessage (std::shared_ptr <protocol::TMValidation> const& m)
|
PeerImp::onMessage (std::shared_ptr <protocol::TMValidation> const& m)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -194,6 +194,9 @@ private:
|
|||||||
int large_sendq_ = 0;
|
int large_sendq_ = 0;
|
||||||
int no_ping_ = 0;
|
int no_ping_ = 0;
|
||||||
std::unique_ptr <LoadEvent> load_event_;
|
std::unique_ptr <LoadEvent> load_event_;
|
||||||
|
// The highest sequence of each PublisherList that has
|
||||||
|
// been sent to or received from this peer.
|
||||||
|
hash_map<PublicKey, std::size_t> publisherListSequences_;
|
||||||
|
|
||||||
std::mutex mutable shardInfoMutex_;
|
std::mutex mutable shardInfoMutex_;
|
||||||
hash_map<PublicKey, ShardInfo> shardInfo_;
|
hash_map<PublicKey, ShardInfo> shardInfo_;
|
||||||
@@ -342,6 +345,29 @@ public:
|
|||||||
Json::Value
|
Json::Value
|
||||||
json() override;
|
json() override;
|
||||||
|
|
||||||
|
bool
|
||||||
|
supportsFeature(ProtocolFeature f) const override;
|
||||||
|
|
||||||
|
boost::optional<std::size_t>
|
||||||
|
publisherListSequence(PublicKey const& pubKey) const override
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> sl(recentLock_);
|
||||||
|
|
||||||
|
auto iter = publisherListSequences_.find(pubKey);
|
||||||
|
if (iter != publisherListSequences_.end())
|
||||||
|
return iter->second;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setPublisherListSequence(PublicKey const& pubKey, std::size_t const seq)
|
||||||
|
override
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> sl(recentLock_);
|
||||||
|
|
||||||
|
publisherListSequences_[pubKey] = seq;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Ledger
|
// Ledger
|
||||||
//
|
//
|
||||||
@@ -488,6 +514,7 @@ public:
|
|||||||
void onMessage (std::shared_ptr <protocol::TMProposeSet> const& m);
|
void onMessage (std::shared_ptr <protocol::TMProposeSet> const& m);
|
||||||
void onMessage (std::shared_ptr <protocol::TMStatusChange> const& m);
|
void onMessage (std::shared_ptr <protocol::TMStatusChange> const& m);
|
||||||
void onMessage (std::shared_ptr <protocol::TMHaveTransactionSet> const& m);
|
void onMessage (std::shared_ptr <protocol::TMHaveTransactionSet> const& m);
|
||||||
|
void onMessage (std::shared_ptr <protocol::TMValidatorList> const& m);
|
||||||
void onMessage (std::shared_ptr <protocol::TMValidation> const& m);
|
void onMessage (std::shared_ptr <protocol::TMValidation> const& m);
|
||||||
void onMessage (std::shared_ptr <protocol::TMGetObjectByHash> const& m);
|
void onMessage (std::shared_ptr <protocol::TMGetObjectByHash> const& m);
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ protocolMessageName (int type)
|
|||||||
case protocol::mtPROPOSE_LEDGER: return "propose";
|
case protocol::mtPROPOSE_LEDGER: return "propose";
|
||||||
case protocol::mtSTATUS_CHANGE: return "status";
|
case protocol::mtSTATUS_CHANGE: return "status";
|
||||||
case protocol::mtHAVE_SET: return "have_set";
|
case protocol::mtHAVE_SET: return "have_set";
|
||||||
|
case protocol::mtVALIDATORLIST: return "validator_list";
|
||||||
case protocol::mtVALIDATION: return "validation";
|
case protocol::mtVALIDATION: return "validation";
|
||||||
case protocol::mtGET_OBJECTS: return "get_objects";
|
case protocol::mtGET_OBJECTS: return "get_objects";
|
||||||
default:
|
default:
|
||||||
@@ -230,6 +231,9 @@ invokeProtocolMessage (Buffers const& buffers, Handler& handler)
|
|||||||
case protocol::mtVALIDATION:
|
case protocol::mtVALIDATION:
|
||||||
success = detail::invoke<protocol::TMValidation>(*header, buffers, handler);
|
success = detail::invoke<protocol::TMValidation>(*header, buffers, handler);
|
||||||
break;
|
break;
|
||||||
|
case protocol::mtVALIDATORLIST:
|
||||||
|
success = detail::invoke<protocol::TMValidatorList> (*header, buffers, handler);
|
||||||
|
break;
|
||||||
case protocol::mtGET_OBJECTS:
|
case protocol::mtGET_OBJECTS:
|
||||||
success = detail::invoke<protocol::TMGetObjectByHash>(*header, buffers, handler);
|
success = detail::invoke<protocol::TMGetObjectByHash>(*header, buffers, handler);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ constexpr
|
|||||||
ProtocolVersion const supportedProtocolList[]
|
ProtocolVersion const supportedProtocolList[]
|
||||||
{
|
{
|
||||||
{ 1, 2 },
|
{ 1, 2 },
|
||||||
{ 2, 0 }
|
{ 2, 0 },
|
||||||
|
{ 2, 1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
// This ugly construct ensures that supportedProtocolList is sorted in strictly
|
// This ugly construct ensures that supportedProtocolList is sorted in strictly
|
||||||
@@ -130,10 +131,8 @@ parseProtocolVersions(boost::beast::string_view const& value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<ProtocolVersion>
|
boost::optional<ProtocolVersion>
|
||||||
negotiateProtocolVersion(boost::beast::string_view const& versions)
|
negotiateProtocolVersion(std::vector<ProtocolVersion> const& versions)
|
||||||
{
|
{
|
||||||
auto const them = parseProtocolVersions(versions);
|
|
||||||
|
|
||||||
boost::optional<ProtocolVersion> result;
|
boost::optional<ProtocolVersion> result;
|
||||||
|
|
||||||
// The protocol version we want to negotiate is the largest item in the
|
// The protocol version we want to negotiate is the largest item in the
|
||||||
@@ -148,13 +147,21 @@ negotiateProtocolVersion(boost::beast::string_view const& versions)
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::set_intersection(
|
std::set_intersection(
|
||||||
std::begin(them), std::end(them),
|
std::begin(versions), std::end(versions),
|
||||||
std::begin(supportedProtocolList), std::end(supportedProtocolList),
|
std::begin(supportedProtocolList), std::end(supportedProtocolList),
|
||||||
boost::make_function_output_iterator(pickVersion));
|
boost::make_function_output_iterator(pickVersion));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<ProtocolVersion>
|
||||||
|
negotiateProtocolVersion(boost::beast::string_view const& versions)
|
||||||
|
{
|
||||||
|
auto const them = parseProtocolVersions(versions);
|
||||||
|
|
||||||
|
return negotiateProtocolVersion(them);
|
||||||
|
}
|
||||||
|
|
||||||
std::string const&
|
std::string const&
|
||||||
supportedProtocolVersions()
|
supportedProtocolVersions()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ namespace ripple {
|
|||||||
using ProtocolVersion = std::pair<std::uint16_t, std::uint16_t>;
|
using ProtocolVersion = std::pair<std::uint16_t, std::uint16_t>;
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
constexpr
|
||||||
ProtocolVersion
|
ProtocolVersion
|
||||||
make_protocol(std::uint16_t major, std::uint16_t minor)
|
make_protocol(std::uint16_t major, std::uint16_t minor)
|
||||||
{
|
{
|
||||||
@@ -62,6 +63,10 @@ to_string(ProtocolVersion const& p);
|
|||||||
std::vector<ProtocolVersion>
|
std::vector<ProtocolVersion>
|
||||||
parseProtocolVersions(boost::beast::string_view const& s);
|
parseProtocolVersions(boost::beast::string_view const& s);
|
||||||
|
|
||||||
|
/** Given a list of supported protocol versions, choose the one we prefer. */
|
||||||
|
boost::optional<ProtocolVersion>
|
||||||
|
negotiateProtocolVersion(std::vector<ProtocolVersion> const& versions);
|
||||||
|
|
||||||
/** Given a list of supported protocol versions, choose the one we prefer. */
|
/** Given a list of supported protocol versions, choose the one we prefer. */
|
||||||
boost::optional<ProtocolVersion>
|
boost::optional<ProtocolVersion>
|
||||||
negotiateProtocolVersion(boost::beast::string_view const& versions);
|
negotiateProtocolVersion(boost::beast::string_view const& versions);
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ TrafficCount::category TrafficCount::categorize (
|
|||||||
if (type == protocol::mtTRANSACTION)
|
if (type == protocol::mtTRANSACTION)
|
||||||
return TrafficCount::category::transaction;
|
return TrafficCount::category::transaction;
|
||||||
|
|
||||||
|
if (type == protocol::mtVALIDATORLIST)
|
||||||
|
return TrafficCount::category::validatorlist;
|
||||||
|
|
||||||
if (type == protocol::mtVALIDATION)
|
if (type == protocol::mtVALIDATION)
|
||||||
return TrafficCount::category::validation;
|
return TrafficCount::category::validation;
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ public:
|
|||||||
transaction,
|
transaction,
|
||||||
proposal,
|
proposal,
|
||||||
validation,
|
validation,
|
||||||
|
validatorlist,
|
||||||
shards, // shard-related traffic
|
shards, // shard-related traffic
|
||||||
|
|
||||||
// TMHaveSet message:
|
// TMHaveSet message:
|
||||||
@@ -189,6 +190,7 @@ protected:
|
|||||||
{"transactions"}, // category::transaction
|
{"transactions"}, // category::transaction
|
||||||
{"proposals"}, // category::proposal
|
{"proposals"}, // category::proposal
|
||||||
{"validations"}, // category::validation
|
{"validations"}, // category::validation
|
||||||
|
{"validator_lists"}, // category::validatorlist
|
||||||
{"shards"}, // category::shards
|
{"shards"}, // category::shards
|
||||||
{"set_get"}, // category::get_set
|
{"set_get"}, // category::get_set
|
||||||
{"set_share"}, // category::share_set
|
{"set_share"}, // category::share_set
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ enum MessageType
|
|||||||
mtSHARD_INFO = 51;
|
mtSHARD_INFO = 51;
|
||||||
mtGET_PEER_SHARD_INFO = 52;
|
mtGET_PEER_SHARD_INFO = 52;
|
||||||
mtPEER_SHARD_INFO = 53;
|
mtPEER_SHARD_INFO = 53;
|
||||||
|
mtVALIDATORLIST = 54;
|
||||||
}
|
}
|
||||||
|
|
||||||
// token, iterations, target, challenge = issue demand for proof of work
|
// token, iterations, target, challenge = issue demand for proof of work
|
||||||
@@ -197,6 +198,14 @@ message TMHaveTransactionSet
|
|||||||
required bytes hash = 2;
|
required bytes hash = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validator list
|
||||||
|
message TMValidatorList
|
||||||
|
{
|
||||||
|
required bytes manifest = 1;
|
||||||
|
required bytes blob = 2;
|
||||||
|
required bytes signature = 3;
|
||||||
|
required uint32 version = 4;
|
||||||
|
}
|
||||||
|
|
||||||
// Used to sign a final closed ledger after reprocessing
|
// Used to sign a final closed ledger after reprocessing
|
||||||
message TMValidation
|
message TMValidation
|
||||||
|
|||||||
@@ -254,8 +254,11 @@ public:
|
|||||||
sort (getPopulatedManifests (m)));
|
sort (getPopulatedManifests (m)));
|
||||||
|
|
||||||
jtx::Env env (*this);
|
jtx::Env env (*this);
|
||||||
|
auto& app = env.app();
|
||||||
auto unl = std::make_unique<ValidatorList> (
|
auto unl = std::make_unique<ValidatorList> (
|
||||||
m, m, env.timeKeeper(), env.journal);
|
m, m, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
{
|
{
|
||||||
// save should not store untrusted master keys to db
|
// save should not store untrusted master keys to db
|
||||||
|
|||||||
@@ -163,15 +163,20 @@ private:
|
|||||||
|
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
jtx::Env env (*this);
|
jtx::Env env (*this);
|
||||||
|
auto& app = env.app();
|
||||||
{
|
{
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 1);
|
BEAST_EXPECT(trustedKeys->quorum () == 1);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::size_t minQuorum = 0;
|
std::size_t minQuorum = 0;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal, minQuorum);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal, minQuorum);
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == minQuorum);
|
BEAST_EXPECT(trustedKeys->quorum () == minQuorum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,6 +187,7 @@ private:
|
|||||||
testcase ("Config Load");
|
testcase ("Config Load");
|
||||||
|
|
||||||
jtx::Env env (*this);
|
jtx::Env env (*this);
|
||||||
|
auto& app = env.app();
|
||||||
PublicKey emptyLocalKey;
|
PublicKey emptyLocalKey;
|
||||||
std::vector<std::string> const emptyCfgKeys;
|
std::vector<std::string> const emptyCfgKeys;
|
||||||
std::vector<std::string> const emptyCfgPublishers;
|
std::vector<std::string> const emptyCfgPublishers;
|
||||||
@@ -230,7 +236,9 @@ private:
|
|||||||
{
|
{
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
// Correct (empty) configuration
|
// Correct (empty) configuration
|
||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
@@ -252,7 +260,9 @@ private:
|
|||||||
// load should add validator keys from config
|
// load should add validator keys from config
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
emptyLocalKey, cfgKeys, emptyCfgPublishers));
|
emptyLocalKey, cfgKeys, emptyCfgPublishers));
|
||||||
@@ -290,7 +300,9 @@ private:
|
|||||||
// local validator key on config list
|
// local validator key on config list
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
auto const localSigningPublic = parseBase58<PublicKey> (
|
auto const localSigningPublic = parseBase58<PublicKey> (
|
||||||
TokenType::NodePublic, cfgKeys.front());
|
TokenType::NodePublic, cfgKeys.front());
|
||||||
@@ -307,7 +319,9 @@ private:
|
|||||||
// local validator key not on config list
|
// local validator key not on config list
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
auto const localSigningPublic = randomNode();
|
auto const localSigningPublic = randomNode();
|
||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
@@ -322,7 +336,9 @@ private:
|
|||||||
// local validator key (with manifest) not on config list
|
// local validator key (with manifest) not on config list
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
manifests.applyManifest (*deserializeManifest(cfgManifest));
|
manifests.applyManifest (*deserializeManifest(cfgManifest));
|
||||||
|
|
||||||
@@ -338,7 +354,9 @@ private:
|
|||||||
{
|
{
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
// load should reject invalid validator list signing keys
|
// load should reject invalid validator list signing keys
|
||||||
std::vector<std::string> badPublishers(
|
std::vector<std::string> badPublishers(
|
||||||
@@ -375,7 +393,9 @@ private:
|
|||||||
ManifestCache valManifests;
|
ManifestCache valManifests;
|
||||||
ManifestCache pubManifests;
|
ManifestCache pubManifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
valManifests, pubManifests, env.timeKeeper(), env.journal);
|
valManifests, pubManifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
auto const pubRevokedSecret = randomSecretKey();
|
auto const pubRevokedSecret = randomSecretKey();
|
||||||
auto const pubRevokedPublic =
|
auto const pubRevokedPublic =
|
||||||
@@ -414,8 +434,11 @@ private:
|
|||||||
|
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
jtx::Env env (*this);
|
jtx::Env env (*this);
|
||||||
|
auto& app = env.app();
|
||||||
auto trustedKeys = std::make_unique<ValidatorList> (
|
auto trustedKeys = std::make_unique<ValidatorList> (
|
||||||
manifests, manifests, env.app().timeKeeper(), env.journal);
|
manifests, manifests, env.app().timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
auto const publisherSecret = randomSecretKey();
|
auto const publisherSecret = randomSecretKey();
|
||||||
auto const publisherPublic =
|
auto const publisherPublic =
|
||||||
@@ -453,7 +476,8 @@ private:
|
|||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::stale ==
|
BEAST_EXPECT(ListDisposition::stale ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest1, expiredblob, expiredSig, version, siteUri));
|
manifest1, expiredblob, expiredSig,
|
||||||
|
version, siteUri).disposition);
|
||||||
|
|
||||||
// apply single list
|
// apply single list
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
@@ -463,8 +487,9 @@ private:
|
|||||||
list1, sequence, expiration.time_since_epoch().count());
|
list1, sequence, expiration.time_since_epoch().count());
|
||||||
auto const sig1 = signList (blob1, pubSigningKeys1);
|
auto const sig1 = signList (blob1, pubSigningKeys1);
|
||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::accepted == trustedKeys->applyList (
|
BEAST_EXPECT(ListDisposition::accepted ==
|
||||||
manifest1, blob1, sig1, version, siteUri));
|
trustedKeys->applyList ( manifest1, blob1,
|
||||||
|
sig1, version, siteUri).disposition);
|
||||||
|
|
||||||
for (auto const& val : list1)
|
for (auto const& val : list1)
|
||||||
{
|
{
|
||||||
@@ -479,13 +504,13 @@ private:
|
|||||||
pubSigningKeys1.first, pubSigningKeys1.second, 1));
|
pubSigningKeys1.first, pubSigningKeys1.second, 1));
|
||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::untrusted == trustedKeys->applyList (
|
BEAST_EXPECT(ListDisposition::untrusted == trustedKeys->applyList (
|
||||||
untrustedManifest, blob1, sig1, version, siteUri));
|
untrustedManifest, blob1, sig1, version, siteUri).disposition);
|
||||||
|
|
||||||
// do not use list with unhandled version
|
// do not use list with unhandled version
|
||||||
auto const badVersion = 666;
|
auto const badVersion = 666;
|
||||||
BEAST_EXPECT(ListDisposition::unsupported_version ==
|
BEAST_EXPECT(ListDisposition::unsupported_version ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest1, blob1, sig1, badVersion, siteUri));
|
manifest1, blob1, sig1, badVersion, siteUri).disposition);
|
||||||
|
|
||||||
// apply list with highest sequence number
|
// apply list with highest sequence number
|
||||||
auto const sequence2 = 2;
|
auto const sequence2 = 2;
|
||||||
@@ -495,7 +520,7 @@ private:
|
|||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::accepted ==
|
BEAST_EXPECT(ListDisposition::accepted ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest1, blob2, sig2, version, siteUri));
|
manifest1, blob2, sig2, version, siteUri).disposition);
|
||||||
|
|
||||||
for (auto const& val : list1)
|
for (auto const& val : list1)
|
||||||
{
|
{
|
||||||
@@ -512,11 +537,11 @@ private:
|
|||||||
// do not re-apply lists with past or current sequence numbers
|
// do not re-apply lists with past or current sequence numbers
|
||||||
BEAST_EXPECT(ListDisposition::stale ==
|
BEAST_EXPECT(ListDisposition::stale ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest1, blob1, sig1, version, siteUri));
|
manifest1, blob1, sig1, version, siteUri).disposition);
|
||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::same_sequence ==
|
BEAST_EXPECT(ListDisposition::same_sequence ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest1, blob2, sig2, version, siteUri));
|
manifest1, blob2, sig2, version, siteUri).disposition);
|
||||||
|
|
||||||
// apply list with new publisher key updated by manifest
|
// apply list with new publisher key updated by manifest
|
||||||
auto const pubSigningKeys2 = randomKeyPair(KeyType::secp256k1);
|
auto const pubSigningKeys2 = randomKeyPair(KeyType::secp256k1);
|
||||||
@@ -531,7 +556,7 @@ private:
|
|||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::accepted ==
|
BEAST_EXPECT(ListDisposition::accepted ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest2, blob3, sig3, version, siteUri));
|
manifest2, blob3, sig3, version, siteUri).disposition);
|
||||||
|
|
||||||
auto const sequence4 = 4;
|
auto const sequence4 = 4;
|
||||||
auto const blob4 = makeList (
|
auto const blob4 = makeList (
|
||||||
@@ -539,7 +564,7 @@ private:
|
|||||||
auto const badSig = signList (blob4, pubSigningKeys1);
|
auto const badSig = signList (blob4, pubSigningKeys1);
|
||||||
BEAST_EXPECT(ListDisposition::invalid ==
|
BEAST_EXPECT(ListDisposition::invalid ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest1, blob4, badSig, version, siteUri));
|
manifest1, blob4, badSig, version, siteUri).disposition);
|
||||||
|
|
||||||
// do not apply list with revoked publisher key
|
// do not apply list with revoked publisher key
|
||||||
// applied list is removed due to revoked publisher key
|
// applied list is removed due to revoked publisher key
|
||||||
@@ -554,7 +579,7 @@ private:
|
|||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::untrusted ==
|
BEAST_EXPECT(ListDisposition::untrusted ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
maxManifest, blob5, sig5, version, siteUri));
|
maxManifest, blob5, sig5, version, siteUri).disposition);
|
||||||
|
|
||||||
BEAST_EXPECT(! trustedKeys->trustedPublisher(publisherPublic));
|
BEAST_EXPECT(! trustedKeys->trustedPublisher(publisherPublic));
|
||||||
for (auto const& val : list1)
|
for (auto const& val : list1)
|
||||||
@@ -574,8 +599,11 @@ private:
|
|||||||
PublicKey emptyLocalKeyOuter;
|
PublicKey emptyLocalKeyOuter;
|
||||||
ManifestCache manifestsOuter;
|
ManifestCache manifestsOuter;
|
||||||
jtx::Env env (*this);
|
jtx::Env env (*this);
|
||||||
|
auto& app = env.app();
|
||||||
auto trustedKeysOuter = std::make_unique <ValidatorList> (
|
auto trustedKeysOuter = std::make_unique <ValidatorList> (
|
||||||
manifestsOuter, manifestsOuter, env.timeKeeper(), env.journal);
|
manifestsOuter, manifestsOuter, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
std::vector<std::string> cfgPublishersOuter;
|
std::vector<std::string> cfgPublishersOuter;
|
||||||
hash_set<NodeID> activeValidatorsOuter;
|
hash_set<NodeID> activeValidatorsOuter;
|
||||||
@@ -722,7 +750,9 @@ private:
|
|||||||
{
|
{
|
||||||
// Make quorum unattainable if lists from any publishers are unavailable
|
// Make quorum unattainable if lists from any publishers are unavailable
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifestsOuter, manifestsOuter, env.timeKeeper(), env.journal);
|
manifestsOuter, manifestsOuter, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
auto const publisherSecret = randomSecretKey();
|
auto const publisherSecret = randomSecretKey();
|
||||||
auto const publisherPublic =
|
auto const publisherPublic =
|
||||||
derivePublicKey(KeyType::ed25519, publisherSecret);
|
derivePublicKey(KeyType::ed25519, publisherSecret);
|
||||||
@@ -746,7 +776,9 @@ private:
|
|||||||
std::size_t const minQuorum = 1;
|
std::size_t const minQuorum = 1;
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal, minQuorum);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal, minQuorum);
|
||||||
|
|
||||||
std::size_t n = 10;
|
std::size_t n = 10;
|
||||||
std::vector<std::string> cfgKeys;
|
std::vector<std::string> cfgKeys;
|
||||||
@@ -786,7 +818,9 @@ private:
|
|||||||
{
|
{
|
||||||
// Remove expired published list
|
// Remove expired published list
|
||||||
auto trustedKeys = std::make_unique<ValidatorList> (
|
auto trustedKeys = std::make_unique<ValidatorList> (
|
||||||
manifestsOuter, manifestsOuter, env.app().timeKeeper(), env.journal);
|
manifestsOuter, manifestsOuter, env.app().timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
PublicKey emptyLocalKey;
|
PublicKey emptyLocalKey;
|
||||||
std::vector<std::string> emptyCfgKeys;
|
std::vector<std::string> emptyCfgKeys;
|
||||||
@@ -819,7 +853,7 @@ private:
|
|||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::accepted ==
|
BEAST_EXPECT(ListDisposition::accepted ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest, blob, sig, version, siteUri));
|
manifest, blob, sig, version, siteUri).disposition);
|
||||||
|
|
||||||
TrustChanges changes =
|
TrustChanges changes =
|
||||||
trustedKeys->updateTrusted(activeValidators);
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
@@ -853,7 +887,7 @@ private:
|
|||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::accepted ==
|
BEAST_EXPECT(ListDisposition::accepted ==
|
||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest, blob2, sig2, version, siteUri));
|
manifest, blob2, sig2, version, siteUri).disposition);
|
||||||
|
|
||||||
changes = trustedKeys->updateTrusted (activeValidators);
|
changes = trustedKeys->updateTrusted (activeValidators);
|
||||||
BEAST_EXPECT(changes.removed.empty());
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
@@ -872,7 +906,9 @@ private:
|
|||||||
{
|
{
|
||||||
// Test 1-9 configured validators
|
// Test 1-9 configured validators
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifestsOuter, manifestsOuter, env.timeKeeper(), env.journal);
|
manifestsOuter, manifestsOuter, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
std::vector<std::string> cfgPublishers;
|
std::vector<std::string> cfgPublishers;
|
||||||
hash_set<NodeID> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
@@ -903,7 +939,9 @@ private:
|
|||||||
{
|
{
|
||||||
// Test 2-9 configured validators as validator
|
// Test 2-9 configured validators as validator
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifestsOuter, manifestsOuter, env.timeKeeper(), env.journal);
|
manifestsOuter, manifestsOuter, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
auto const localKey = randomNode();
|
auto const localKey = randomNode();
|
||||||
std::vector<std::string> cfgPublishers;
|
std::vector<std::string> cfgPublishers;
|
||||||
@@ -943,7 +981,9 @@ private:
|
|||||||
// Trusted set should include all validators from multiple lists
|
// Trusted set should include all validators from multiple lists
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
hash_set<NodeID> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
std::vector<Validator> valKeys;
|
std::vector<Validator> valKeys;
|
||||||
@@ -983,8 +1023,9 @@ private:
|
|||||||
valKeys, sequence, expiration.time_since_epoch().count());
|
valKeys, sequence, expiration.time_since_epoch().count());
|
||||||
auto const sig = signList (blob, pubSigningKeys);
|
auto const sig = signList (blob, pubSigningKeys);
|
||||||
|
|
||||||
BEAST_EXPECT(ListDisposition::accepted == trustedKeys->applyList (
|
BEAST_EXPECT(ListDisposition::accepted ==
|
||||||
manifest, blob, sig, version, siteUri));
|
trustedKeys->applyList (manifest, blob, sig, version,
|
||||||
|
siteUri).disposition);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply multiple published lists
|
// Apply multiple published lists
|
||||||
@@ -1016,6 +1057,7 @@ private:
|
|||||||
std::string const siteUri = "testExpires.test";
|
std::string const siteUri = "testExpires.test";
|
||||||
|
|
||||||
jtx::Env env(*this);
|
jtx::Env env(*this);
|
||||||
|
auto& app = env.app();
|
||||||
|
|
||||||
auto toStr = [](PublicKey const& publicKey) {
|
auto toStr = [](PublicKey const& publicKey) {
|
||||||
return toBase58(TokenType::NodePublic, publicKey);
|
return toBase58(TokenType::NodePublic, publicKey);
|
||||||
@@ -1025,7 +1067,9 @@ private:
|
|||||||
{
|
{
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique<ValidatorList>(
|
auto trustedKeys = std::make_unique<ValidatorList>(
|
||||||
manifests, manifests, env.timeKeeper(), env.journal);
|
manifests, manifests, env.timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
// Empty list has no expiration
|
// Empty list has no expiration
|
||||||
BEAST_EXPECT(trustedKeys->expires() == boost::none);
|
BEAST_EXPECT(trustedKeys->expires() == boost::none);
|
||||||
@@ -1044,7 +1088,9 @@ private:
|
|||||||
{
|
{
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
auto trustedKeys = std::make_unique<ValidatorList>(
|
auto trustedKeys = std::make_unique<ValidatorList>(
|
||||||
manifests, manifests, env.app().timeKeeper(), env.journal);
|
manifests, manifests, env.app().timeKeeper(),
|
||||||
|
app.config().legacy("database_path"),
|
||||||
|
env.journal);
|
||||||
|
|
||||||
std::vector<Validator> validators = {randomValidator()};
|
std::vector<Validator> validators = {randomValidator()};
|
||||||
hash_set<NodeID> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
@@ -1104,7 +1150,8 @@ private:
|
|||||||
// Apply first list
|
// Apply first list
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
ListDisposition::accepted == trustedKeys->applyList(
|
ListDisposition::accepted == trustedKeys->applyList(
|
||||||
prep1.manifest, prep1.blob, prep1.sig, prep1.version, siteUri));
|
prep1.manifest, prep1.blob, prep1.sig,
|
||||||
|
prep1.version, siteUri).disposition);
|
||||||
|
|
||||||
// One list still hasn't published, so expiration is still unknown
|
// One list still hasn't published, so expiration is still unknown
|
||||||
BEAST_EXPECT(trustedKeys->expires() == boost::none);
|
BEAST_EXPECT(trustedKeys->expires() == boost::none);
|
||||||
@@ -1112,7 +1159,8 @@ private:
|
|||||||
// Apply second list
|
// Apply second list
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
ListDisposition::accepted == trustedKeys->applyList(
|
ListDisposition::accepted == trustedKeys->applyList(
|
||||||
prep2.manifest, prep2.blob, prep2.sig, prep2.version, siteUri));
|
prep2.manifest, prep2.blob, prep2.sig,
|
||||||
|
prep2.version, siteUri).disposition);
|
||||||
|
|
||||||
// We now have loaded both lists, so expiration is known
|
// We now have loaded both lists, so expiration is known
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
|
|||||||
@@ -36,11 +36,14 @@ public:
|
|||||||
"This file is very short. That's all we need.";
|
"This file is very short. That's all we need.";
|
||||||
|
|
||||||
FileDirGuard file(*this, "test_file", "test.txt",
|
FileDirGuard file(*this, "test_file", "test.txt",
|
||||||
expectedContents);
|
"This is temporary text that should get overwritten");
|
||||||
|
|
||||||
error_code ec;
|
error_code ec;
|
||||||
auto const path = file.file();
|
auto const path = file.file();
|
||||||
|
|
||||||
|
writeFileContents(ec, path, expectedContents);
|
||||||
|
BEAST_EXPECT(!ec);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Test with no max
|
// Test with no max
|
||||||
auto const good = getFileContents(ec, path);
|
auto const good = getFileContents(ec, path);
|
||||||
|
|||||||
Reference in New Issue
Block a user