mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-25 13:35:54 +00:00
Add validator list RPC commands (RIPD-1541):
In support of dynamic validator list, this changeset: 1. Adds a new `validator_list_expires` field to `server_info` that indicates when the current validator list will become stale. 2. Adds a new admin only `validator_lists` RPC that returns the current list of known validators and the most recent published validator lists. 3. Adds a new admin only `validator_sites` RPC that returns the list of configured validator publisher sites and when they were most recently queried.
This commit is contained in:
@@ -21,11 +21,33 @@
|
||||
#include <ripple/basics/Slice.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/json/json_reader.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <beast/core/detail/base64.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
std::string
|
||||
to_string(ListDisposition disposition)
|
||||
{
|
||||
switch (disposition)
|
||||
{
|
||||
case ListDisposition::accepted:
|
||||
return "accepted";
|
||||
case ListDisposition::same_sequence:
|
||||
return "same_sequence";
|
||||
case ListDisposition::unsupported_version:
|
||||
return "unsupported_version";
|
||||
case ListDisposition::untrusted:
|
||||
return "untrusted";
|
||||
case ListDisposition::stale:
|
||||
return "stale";
|
||||
case ListDisposition::invalid:
|
||||
return "invalid";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
ValidatorList::ValidatorList (
|
||||
ManifestCache& validatorManifests,
|
||||
ManifestCache& publisherManifests,
|
||||
@@ -150,8 +172,15 @@ ValidatorList::load (
|
||||
JLOG (j_.warn()) << "Duplicate node identity: " << match[1];
|
||||
continue;
|
||||
}
|
||||
publisherLists_[local].list.emplace_back (std::move(*id));
|
||||
publisherLists_[local].available = true;
|
||||
auto it = publisherLists_.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(local),
|
||||
std::forward_as_tuple());
|
||||
// Config listed keys never expire
|
||||
if (it.second)
|
||||
it.first->second.expiration = TimeKeeper::time_point::max();
|
||||
it.first->second.list.emplace_back(std::move(*id));
|
||||
it.first->second.available = true;
|
||||
++count;
|
||||
}
|
||||
|
||||
@@ -169,7 +198,7 @@ ValidatorList::applyList (
|
||||
std::string const& signature,
|
||||
std::uint32_t version)
|
||||
{
|
||||
if (version != 1)
|
||||
if (version != requiredListVersion)
|
||||
return ListDisposition::unsupported_version;
|
||||
|
||||
boost::unique_lock<boost::shared_mutex> lock{mutex_};
|
||||
@@ -184,7 +213,8 @@ ValidatorList::applyList (
|
||||
Json::Value const& newList = list["validators"];
|
||||
publisherLists_[pubKey].available = true;
|
||||
publisherLists_[pubKey].sequence = list["sequence"].asUInt ();
|
||||
publisherLists_[pubKey].expiration = list["expiration"].asUInt ();
|
||||
publisherLists_[pubKey].expiration = TimeKeeper::time_point{
|
||||
TimeKeeper::duration{list["expiration"].asUInt()}};
|
||||
std::vector<PublicKey>& publisherList = publisherLists_[pubKey].list;
|
||||
|
||||
std::vector<PublicKey> oldList = publisherList;
|
||||
@@ -328,11 +358,14 @@ ValidatorList::verify (
|
||||
list.isMember("expiration") && list["expiration"].isInt() &&
|
||||
list.isMember("validators") && list["validators"].isArray())
|
||||
{
|
||||
auto const sequence = list["sequence"].asUInt ();
|
||||
auto const expiration = list["expiration"].asUInt ();
|
||||
if (sequence <= publisherLists_[pubKey].sequence ||
|
||||
expiration <= timeKeeper_.now().time_since_epoch().count())
|
||||
auto const sequence = list["sequence"].asUInt();
|
||||
auto const expiration = TimeKeeper::time_point{
|
||||
TimeKeeper::duration{list["expiration"].asUInt()}};
|
||||
if (sequence < publisherLists_[pubKey].sequence ||
|
||||
expiration <= timeKeeper_.now())
|
||||
return ListDisposition::stale;
|
||||
else if (sequence == publisherLists_[pubKey].sequence)
|
||||
return ListDisposition::same_sequence;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -427,6 +460,103 @@ ValidatorList::removePublisherList (PublicKey const& publisherKey)
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::optional<TimeKeeper::time_point>
|
||||
ValidatorList::expires() const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> read_lock{mutex_};
|
||||
boost::optional<TimeKeeper::time_point> res{boost::none};
|
||||
for (auto const& p : publisherLists_)
|
||||
{
|
||||
// Unfetched
|
||||
if (p.second.expiration == TimeKeeper::time_point{})
|
||||
return boost::none;
|
||||
|
||||
// Earliest
|
||||
if (!res || p.second.expiration < *res)
|
||||
res = p.second.expiration;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
ValidatorList::getJson() const
|
||||
{
|
||||
Json::Value res(Json::objectValue);
|
||||
|
||||
boost::shared_lock<boost::shared_mutex> read_lock{mutex_};
|
||||
|
||||
res[jss::validation_quorum] = static_cast<Json::UInt>(quorum());
|
||||
|
||||
if (auto when = expires())
|
||||
{
|
||||
if (*when == TimeKeeper::time_point::max())
|
||||
res[jss::validator_list_expires] = "never";
|
||||
else
|
||||
res[jss::validator_list_expires] = to_string(*when);
|
||||
}
|
||||
else
|
||||
res[jss::validator_list_expires] = "unknown";
|
||||
|
||||
// Local static keys
|
||||
PublicKey local;
|
||||
Json::Value& jLocalStaticKeys =
|
||||
(res[jss::local_static_keys] = Json::arrayValue);
|
||||
auto it = publisherLists_.find(local);
|
||||
if (it != publisherLists_.end())
|
||||
{
|
||||
for (auto const& key : it->second.list)
|
||||
jLocalStaticKeys.append(
|
||||
toBase58(TokenType::TOKEN_NODE_PUBLIC, key));
|
||||
}
|
||||
|
||||
// Publisher lists
|
||||
Json::Value& jPublisherLists =
|
||||
(res[jss::publisher_lists] = Json::arrayValue);
|
||||
for (auto const& p : publisherLists_)
|
||||
{
|
||||
if(local == p.first)
|
||||
continue;
|
||||
Json::Value& curr = jPublisherLists.append(Json::objectValue);
|
||||
curr[jss::pubkey_publisher] = strHex(p.first);
|
||||
curr[jss::available] = p.second.available;
|
||||
if(p.second.expiration != TimeKeeper::time_point{})
|
||||
{
|
||||
curr[jss::seq] = static_cast<Json::UInt>(p.second.sequence);
|
||||
curr[jss::expiration] = to_string(p.second.expiration);
|
||||
curr[jss::version] = requiredListVersion;
|
||||
}
|
||||
Json::Value& keys = (curr[jss::list] = Json::arrayValue);
|
||||
for (auto const& key : p.second.list)
|
||||
{
|
||||
keys.append(toBase58(TokenType::TOKEN_NODE_PUBLIC, key));
|
||||
}
|
||||
}
|
||||
|
||||
// Trusted validator keys
|
||||
Json::Value& jValidatorKeys =
|
||||
(res[jss::trusted_validator_keys] = Json::arrayValue);
|
||||
for (auto const& k : trustedKeys_)
|
||||
{
|
||||
jValidatorKeys.append(toBase58(TokenType::TOKEN_NODE_PUBLIC, k));
|
||||
}
|
||||
|
||||
// signing keys
|
||||
Json::Value& jSigningKeys = (res[jss::signing_keys] = Json::objectValue);
|
||||
validatorManifests_.for_each_manifest(
|
||||
[&jSigningKeys, this](Manifest const& manifest) {
|
||||
|
||||
auto it = keyListings_.find(manifest.masterKey);
|
||||
if (it != keyListings_.end())
|
||||
{
|
||||
jSigningKeys[toBase58(
|
||||
TokenType::TOKEN_NODE_PUBLIC, manifest.masterKey)] =
|
||||
toBase58(TokenType::TOKEN_NODE_PUBLIC, manifest.signingKey);
|
||||
}
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
ValidatorList::for_each_listed (
|
||||
std::function<void(PublicKey const&, bool)> func) const
|
||||
|
||||
Reference in New Issue
Block a user