mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-23 04:25:51 +00:00
Properly handle expired validator lists when validating (RIPD-1661):
A validator that was configured to use a published validator list could exhibit aberrent behavior if that validator list expired. This commit introduces additional logic that makes validators operating with an expired validator list bow out of the consensus process instead of continuing to publish validations. Normal operation will resume once a non-expired validator list becomes available. This commit also enhances status reporting when using the `server_info` and `validators` commands. Before, only the expiration time of the list would be returned; now, its current status is also reported in a format that is clearer.
This commit is contained in:
committed by
Mike Ellery
parent
63c3fc30d8
commit
b36e11bc49
@@ -806,12 +806,26 @@ RCLConsensus::peerProposal(
|
||||
bool
|
||||
RCLConsensus::Adaptor::preStartRound(RCLCxLedger const & prevLgr)
|
||||
{
|
||||
// We have a key and do not want out of sync validations after a restart,
|
||||
// We have a key, we do not want out of sync validations after a restart
|
||||
// and are not amendment blocked.
|
||||
validating_ = valPublic_.size() != 0 &&
|
||||
prevLgr.seq() >= app_.getMaxDisallowedLedger() &&
|
||||
!app_.getOPs().isAmendmentBlocked();
|
||||
|
||||
// If we are not running in standalone mode and there's a configured UNL,
|
||||
// check to make sure that it's not expired.
|
||||
if (validating_ && !app_.config().standalone() && app_.validators().count())
|
||||
{
|
||||
auto const when = app_.validators().expires();
|
||||
|
||||
if (!when || *when < app_.timeKeeper().now())
|
||||
{
|
||||
JLOG(j_.error()) << "Voluntarily bowing out of consensus process "
|
||||
"because of an expired validator list.";
|
||||
validating_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
const bool synced = app_.getOPs().getOperatingMode() == NetworkOPs::omFULL;
|
||||
|
||||
if (validating_)
|
||||
|
||||
@@ -2103,25 +2103,44 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin, bool counters)
|
||||
|
||||
if (admin)
|
||||
{
|
||||
if (auto when = app_.validators().expires())
|
||||
auto when = app_.validators().expires();
|
||||
|
||||
if (!human)
|
||||
{
|
||||
if (human)
|
||||
{
|
||||
if(*when == TimeKeeper::time_point::max())
|
||||
info[jss::validator_list_expires] = "never";
|
||||
else
|
||||
info[jss::validator_list_expires] = to_string(*when);
|
||||
}
|
||||
else
|
||||
if (when)
|
||||
info[jss::validator_list_expires] =
|
||||
static_cast<Json::UInt>(when->time_since_epoch().count());
|
||||
else
|
||||
info[jss::validator_list_expires] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (human)
|
||||
info[jss::validator_list_expires] = "unknown";
|
||||
auto& x = (info[jss::validator_list] = Json::objectValue);
|
||||
|
||||
x[jss::count] = static_cast<Json::UInt>(app_.validators().count());
|
||||
|
||||
if (when)
|
||||
{
|
||||
if (*when == TimeKeeper::time_point::max())
|
||||
{
|
||||
x[jss::expiration] = "never";
|
||||
x[jss::status] = "active";
|
||||
}
|
||||
else
|
||||
info[jss::validator_list_expires] = 0;
|
||||
{
|
||||
x[jss::expiration] = to_string(*when);
|
||||
|
||||
if (*when > app_.timeKeeper().now())
|
||||
x[jss::status] = "active";
|
||||
else
|
||||
x[jss::status] = "expired";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
x[jss::status] = "unknown";
|
||||
x[jss::expiration] = "unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
info[jss::io_latency_ms] = static_cast<Json::UInt> (
|
||||
|
||||
@@ -331,6 +331,10 @@ public:
|
||||
for_each_listed (
|
||||
std::function<void(PublicKey const&, bool)> func) const;
|
||||
|
||||
/** Return the number of configured validator list sites. */
|
||||
std::size_t
|
||||
count() const;
|
||||
|
||||
/** Return the time when the validator list will expire
|
||||
|
||||
@note This may be a time in the past if a published list has not
|
||||
|
||||
@@ -459,6 +459,13 @@ ValidatorList::removePublisherList (PublicKey const& publisherKey)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ValidatorList::count() const
|
||||
{
|
||||
std::shared_lock<std::shared_timed_mutex> read_lock{mutex_};
|
||||
return publisherLists_.size();
|
||||
}
|
||||
|
||||
boost::optional<TimeKeeper::time_point>
|
||||
ValidatorList::expires() const
|
||||
{
|
||||
@@ -486,19 +493,34 @@ ValidatorList::getJson() const
|
||||
|
||||
res[jss::validation_quorum] = static_cast<Json::UInt>(quorum());
|
||||
|
||||
{
|
||||
auto& x = (res[jss::validator_list] = Json::objectValue);
|
||||
|
||||
x[jss::count] = static_cast<Json::UInt>(count());
|
||||
|
||||
if (auto when = expires())
|
||||
{
|
||||
if (*when == TimeKeeper::time_point::max())
|
||||
{
|
||||
res[jss::validator_list_expires] = "never";
|
||||
x[jss::expiration] = "never";
|
||||
x[jss::status] = "active";
|
||||
}
|
||||
else
|
||||
{
|
||||
res[jss::validator_list_expires] = to_string(*when);
|
||||
x[jss::expiration] = to_string(*when);
|
||||
|
||||
if (*when > timeKeeper_.now())
|
||||
x[jss::status] = "active";
|
||||
else
|
||||
x[jss::status] = "expired";
|
||||
}
|
||||
}
|
||||
else
|
||||
res[jss::validator_list_expires] = "unknown";
|
||||
{
|
||||
x[jss::status] = "unknown";
|
||||
x[jss::expiration] = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Local static keys
|
||||
PublicKey local;
|
||||
|
||||
@@ -481,6 +481,7 @@ JSS ( validate ); // in: DownloadShard
|
||||
JSS ( validated ); // out: NetworkOPs, RPCHelpers, AccountTx*
|
||||
// Tx
|
||||
JSS ( validator_list_expires ); // out: NetworkOps, ValidatorList
|
||||
JSS ( validator_list ); // out: NetworkOps, ValidatorList
|
||||
JSS ( validated_ledger ); // out: NetworkOPs
|
||||
JSS ( validated_ledgers ); // out: NetworkOPs
|
||||
JSS ( validation_key ); // out: ValidationCreate, ValidationSeed
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
auto const jrr = env.rpc("server_info")[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::status] == "success");
|
||||
BEAST_EXPECT(jrr[jss::info].isMember(
|
||||
jss::validator_list_expires) == isAdmin);
|
||||
jss::validator_list) == isAdmin);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -145,7 +145,8 @@ public:
|
||||
{
|
||||
auto const jrr = env.rpc("server_info")[jss::result];
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::info][jss::validator_list_expires] == "never");
|
||||
jrr[jss::info][jss::validator_list][jss::expiration] ==
|
||||
"never");
|
||||
}
|
||||
{
|
||||
auto const jrr = env.rpc("server_state")[jss::result];
|
||||
@@ -156,7 +157,7 @@ public:
|
||||
// All our keys are in the response
|
||||
{
|
||||
auto const jrr = env.rpc("validators")[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::validator_list_expires] == "never");
|
||||
BEAST_EXPECT(jrr[jss::validator_list][jss::expiration] == "never");
|
||||
BEAST_EXPECT(jrr[jss::validation_quorum].asUInt() == keys.size());
|
||||
BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == keys.size());
|
||||
BEAST_EXPECT(jrr[jss::publisher_lists].size() == 0);
|
||||
@@ -226,7 +227,8 @@ public:
|
||||
{
|
||||
auto const jrr = env.rpc("server_info")[jss::result];
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::info][jss::validator_list_expires] == "unknown");
|
||||
jrr[jss::info][jss::validator_list][jss::expiration] ==
|
||||
"unknown");
|
||||
}
|
||||
{
|
||||
auto const jrr = env.rpc("server_state")[jss::result];
|
||||
@@ -239,7 +241,8 @@ public:
|
||||
std::numeric_limits<std::uint32_t>::max());
|
||||
BEAST_EXPECT(jrr[jss::local_static_keys].size() == 0);
|
||||
BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == 0);
|
||||
BEAST_EXPECT(jrr[jss::validator_list_expires] == "unknown");
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::validator_list][jss::expiration] == "unknown");
|
||||
|
||||
if (BEAST_EXPECT(jrr[jss::publisher_lists].size() == 1))
|
||||
{
|
||||
@@ -312,7 +315,8 @@ public:
|
||||
|
||||
{
|
||||
auto const jrr = env.rpc("server_info")[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::info][jss::validator_list_expires] ==
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::info][jss::validator_list][jss::expiration] ==
|
||||
to_string(expiration));
|
||||
}
|
||||
{
|
||||
@@ -325,7 +329,8 @@ public:
|
||||
auto const jrr = env.rpc("validators")[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::validation_quorum].asUInt() == 2);
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::validator_list_expires] == to_string(expiration));
|
||||
jrr[jss::validator_list][jss::expiration] ==
|
||||
to_string(expiration));
|
||||
BEAST_EXPECT(jrr[jss::local_static_keys].size() == 0);
|
||||
|
||||
BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() ==
|
||||
|
||||
Reference in New Issue
Block a user