mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +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:
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
|
||||
Reference in New Issue
Block a user