Compare commits

...

12 Commits

15 changed files with 239 additions and 81 deletions

View File

@@ -1230,10 +1230,10 @@ Consensus<Adaptor>::shouldPause(std::unique_ptr<std::stringstream> const& clog)
auto const& parms = adaptor_.parms();
std::uint32_t const ahead(
previousLedger_.seq() - std::min(adaptor_.getValidLedgerIndex(), previousLedger_.seq()));
auto [quorum, trustedKeys] = adaptor_.getQuorumKeys();
std::size_t const totalValidators = trustedKeys.size();
std::size_t const laggards = adaptor_.laggards(previousLedger_.seq(), trustedKeys);
std::size_t const offline = trustedKeys.size();
auto [quorum, trustedNodes] = adaptor_.getQuorumKeys();
std::size_t const totalValidators = trustedNodes.size();
std::size_t const laggards = adaptor_.laggards(previousLedger_.seq(), trustedNodes);
std::size_t const offline = trustedNodes.size();
std::stringstream vars;
vars << " consensuslog (working seq: " << previousLedger_.seq() << ", "

View File

@@ -236,7 +236,6 @@ to_string(ValStatus m)
* struct Validation
* {
* using NodeID = ...;
* using NodeKey = ...;
*
* // Ledger ID associated with this validation
* Ledger::ID ledgerID() const;
@@ -250,9 +249,6 @@ to_string(ValStatus m)
* // When the validation was first observed by this node
* NetClock::time_point seenTime() const;
*
* // Signing key of node that published the validation
* NodeKey key() const;
*
* // Whether the publishing node was trusted at the time the validation
* // arrived
* bool trusted() const;
@@ -303,7 +299,6 @@ class Validations
using ID = Ledger::ID;
using Seq = Ledger::Seq;
using NodeID = Validation::NodeID;
using NodeKey = Validation::NodeKey;
using WrappedValidationType =
std::decay_t<std::invoke_result_t<decltype(&Validation::unwrap), Validation>>;
@@ -1120,22 +1115,22 @@ public:
* redundant by checking the list of proposers.
*
* @param seq Our current sequence number.
* @param trustedKeys Public keys of trusted proposers.
* @param trustedNodes NodeIDs of trusted proposers.
* @return Quantity of laggards.
*/
std::size_t
laggards(Seq const seq, HashSet<NodeKey>& trustedKeys)
laggards(Seq const seq, HashSet<NodeID>& trustedNodes)
{
std::size_t laggards = 0;
current(
std::scoped_lock{mutex_},
[](std::size_t) {},
[&](NodeID const&, Validation const& v) {
[&](NodeID const& node, Validation const& v) {
if (adaptor_.now() < v.seenTime() + parms_.validationFRESHNESS &&
trustedKeys.find(v.key()) != trustedKeys.end())
trustedNodes.contains(node))
{
trustedKeys.erase(v.key());
trustedNodes.erase(node);
if (seq > v.seq())
++laggards;
}

View File

@@ -12,13 +12,13 @@
#include <xrpl/nodestore/Database.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STValidation.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/shamap/SHAMap.h>
#include <xrpl/shamap/SHAMapItem.h>
#include <xrpl/shamap/SHAMapTreeNode.h>
@@ -127,7 +127,7 @@ public:
// Called when the set of trusted validators changes.
virtual void
trustChanged(HashSet<PublicKey> const& allTrusted) = 0;
trustChanged(HashSet<NodeID> const& allTrusted) = 0;
// Called by the consensus code when we need to
// inject pseudo-transactions

View File

@@ -47,9 +47,10 @@ class STValidation final : public STObject, public CountedObject<STValidation>
// The public key associated with the key used to sign this validation
PublicKey const signingPubKey_;
// The ID of the validator that issued this validation. For validators
// that use manifests this will be derived from the master public key.
NodeID const nodeID_;
// The ID of the validator that issued this validation. handleNewValidation
// derives it from the master public key when the signer is trusted or
// listed, and from the signing key otherwise.
NodeID nodeID_;
NetClock::time_point seenTime_;
@@ -152,6 +153,9 @@ public:
void
setSeen(NetClock::time_point s);
void
setNodeID(NodeID const& nodeID);
[[nodiscard]] Blob
getSerialized() const;
@@ -296,4 +300,11 @@ STValidation::setSeen(NetClock::time_point s)
seenTime_ = s;
}
inline void
STValidation::setNodeID(NodeID const& nodeID)
{
XRPL_ASSERT(nodeID.isNonZero(), "xrpl::STValidation::setNodeID : nonzero node");
nodeID_ = nodeID;
}
} // namespace xrpl

View File

@@ -1,11 +1,15 @@
#include <test/jtx/Env.h>
#include <test/jtx/TrustedPublisherServer.h>
#include <test/jtx/envconfig.h>
#include <test/unit_test/SuiteJournal.h>
#include <xrpld/app/consensus/RCLValidations.h>
#include <xrpld/app/main/Application.h>
#include <xrpld/core/Config.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/base64.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/contract.h>
@@ -13,6 +17,7 @@
#include <xrpl/config/BasicConfig.h>
#include <xrpl/config/Constants.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/json_writer.h>
#include <xrpl/ledger/AmendmentTable.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/Feature.h>
@@ -21,10 +26,15 @@
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STValidation.h>
#include <xrpl/protocol/STVector256.h>
#include <xrpl/protocol/SecretKey.h>
#include <xrpl/protocol/Serializer.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol/tokens.h>
#include <xrpl/server/Manifest.h>
#include <algorithm>
#include <cassert>
@@ -467,12 +477,12 @@ public:
{
std::vector<std::pair<PublicKey, SecretKey>> ret;
ret.reserve(num);
HashSet<PublicKey> trustedValidators;
HashSet<NodeID> trustedValidators;
trustedValidators.reserve(num);
for (int i = 0; i < num; ++i)
{
auto const& back = ret.emplace_back(randomKeyPair(KeyType::Secp256k1));
trustedValidators.insert(back.first);
trustedValidators.insert(calcNodeID(back.first));
}
table->trustChanged(trustedValidators);
return ret;
@@ -976,10 +986,10 @@ public:
[](std::vector<std::pair<PublicKey, SecretKey>> const& validators,
std::unique_ptr<AmendmentTable> const& table) {
// We need a HashSet to pass to trustChanged.
HashSet<PublicKey> trustedValidators;
HashSet<NodeID> trustedValidators;
trustedValidators.reserve(validators.size());
std::ranges::for_each(validators, [&trustedValidators](auto const& val) {
trustedValidators.insert(val.first);
trustedValidators.insert(calcNodeID(val.first));
});
// Tell the AmendmentTable that the UNL changed.
@@ -1160,6 +1170,114 @@ public:
}
}
// A trusted validator that rotates its signing key keeps its amendment
// vote without a restart or a change to the trusted set.
void
testSigningKeyRotation(FeatureBitset const& feat)
{
testcase("signingKeyRotation");
auto const amendment = fixCleanup3_5_0;
auto const masterKeys = randomKeyPair(KeyType::Ed25519);
auto cfg = test::jtx::envconfig();
cfg->section(Sections::kValidators)
.append(toBase58(TokenType::NodePublic, masterKeys.first));
test::jtx::Env env{*this, std::move(cfg), feat - amendment};
auto& table = env.app().getAmendmentTable();
auto const rotate = [&](std::pair<PublicKey, SecretKey> const& signingKeys, int seq) {
auto m = deserializeManifest(base64Decode(
test::TrustedPublisherServer::makeManifestString(
masterKeys.first,
masterKeys.second,
signingKeys.first,
signingKeys.second,
seq)));
if (!BEAST_EXPECT(m))
return;
BEAST_EXPECT(
env.app().getValidatorManifests().applyManifest(
std::move(*m), ManifestRateLimitCapPolicy::Uncapped) ==
ManifestDisposition::Accepted);
env.close();
};
// Tally the given validations at the given hour and report the amendment's votes
auto const tally = [&](std::chrono::hours hour,
std::vector<std::shared_ptr<STValidation>> const& validations) {
table.doVoting(env.current()->rules(), hourTime(hour), {}, {}, validations);
return table.getJson(amendment, true)[to_string(amendment)];
};
// A validation signed by signingKeys, carrying the master key's NodeID
auto const validation = [&](std::pair<PublicKey, SecretKey> const& signingKeys, bool yes) {
return std::make_shared<STValidation>(
NetClock::time_point{},
signingKeys.first,
signingKeys.second,
calcNodeID(masterKeys.first),
[&amendment, yes](STValidation& v) {
if (yes)
{
v.setFieldV256(
sfAmendments,
STVector256(sfAmendments, std::vector<UInt256>{amendment}));
}
v.setFieldU32(sfLedgerSequence, 6180339);
});
};
auto const signingKeys1 = randomKeyPair(KeyType::Secp256k1);
rotate(signingKeys1, 1);
auto json = tally(std::chrono::hours(1), {validation(signingKeys1, true)});
BEAST_EXPECT(json[jss::validations].asInt() == 1);
BEAST_EXPECT(json[jss::count].asInt() == 1);
// The recorded vote carries across a rotation
auto const signingKeys2 = randomKeyPair(KeyType::Secp256k1);
rotate(signingKeys2, 2);
json = tally(std::chrono::hours(2), {});
BEAST_EXPECT(json[jss::validations].asInt() == 1);
BEAST_EXPECT(json[jss::count].asInt() == 1);
// The new key's vote is recorded after the old vote would have expired
json = tally(std::chrono::hours(30), {validation(signingKeys2, false)});
BEAST_EXPECT(json[jss::validations].asInt() == 1);
BEAST_EXPECT(json[jss::count].asInt() == 0);
// A validation received before its manifest is applied and handled after it
// carries the master key's NodeID and counts
auto const signingKeys3 = randomKeyPair(KeyType::Secp256k1);
auto const closed = env.closed();
auto const blob =
std::make_shared<STValidation>(
env.app().getTimeKeeper().closeTime(),
signingKeys3.first,
signingKeys3.second,
calcNodeID(masterKeys.first),
[&amendment, &closed](STValidation& v) {
v.setFieldV256(
sfAmendments, STVector256(sfAmendments, std::vector<UInt256>{amendment}));
v.setFieldH256(sfLedgerHash, closed->header().hash);
v.setFieldU32(sfLedgerSequence, closed->seq());
})
->getSerialized();
SerialIter sit(makeSlice(blob));
auto const received = std::make_shared<STValidation>(
sit,
[](PublicKey const& pk) { return calcNodeID(pk); },
STValidation::DeserializeOptions{
.checkSignature = true, .requireCanonicalOrder = true});
BEAST_EXPECT(received->getNodeID() == calcNodeID(signingKeys3.first));
rotate(signingKeys3, 3);
handleNewValidation(env.app(), received, "test", BypassAccept::Yes, env.journal);
BEAST_EXPECT(received->isTrusted());
BEAST_EXPECT(received->getNodeID() == calcNodeID(masterKeys.first));
json = tally(std::chrono::hours(31), {received});
BEAST_EXPECT(json[jss::validations].asInt() == 1);
BEAST_EXPECT(json[jss::count].asInt() == 1);
}
void
testHasUnsupported()
{
@@ -1208,6 +1326,7 @@ public:
testLostMajority(feat);
testChangedUNL(feat);
testValidatorFlapping(feat);
testSigningKeyRotation(feat);
}
void

View File

@@ -1218,6 +1218,8 @@ private:
BEAST_EXPECT(changes.added == asNodeIDs({masterPublic}));
BEAST_EXPECT(changes.removed.empty());
BEAST_EXPECT(trustedKeysOuter->quorum() == std::ceil((maxKeys + 1) * 0.8f));
BEAST_EXPECT(
trustedKeysOuter->getQuorumKeys().second.contains(calcNodeID(masterPublic)));
BEAST_EXPECT(trustedKeysOuter->listed(masterPublic));
BEAST_EXPECT(trustedKeysOuter->trusted(masterPublic));
BEAST_EXPECT(!trustedKeysOuter->listed(signingPublic1));
@@ -1253,6 +1255,18 @@ private:
BEAST_EXPECT(!trustedKeysOuter->listed(signingPublic1));
BEAST_EXPECT(!trustedKeysOuter->trusted(signingPublic1));
// A signing key rotation leaves the trusted set and its NodeIDs unchanged
changes = trustedKeysOuter->updateTrusted(
activeValidatorsOuter,
env.timeKeeper().now(),
env.app().getOPs(),
env.app().getOverlay(),
env.app().getHashRouter());
BEAST_EXPECT(changes.added.empty());
BEAST_EXPECT(changes.removed.empty());
BEAST_EXPECT(
trustedKeysOuter->getQuorumKeys().second.contains(calcNodeID(masterPublic)));
// Should not trust keys from revoked master public key
auto const signingKeysMax = randomKeyPair(KeyType::Secp256k1);
auto const signingPublicMax = signingKeysMax.first;
@@ -1282,6 +1296,8 @@ private:
BEAST_EXPECT(changes.removed == asNodeIDs({masterPublic}));
BEAST_EXPECT(changes.added.empty());
BEAST_EXPECT(trustedKeysOuter->quorum() == std::ceil(maxKeys * 0.8f));
BEAST_EXPECT(
!trustedKeysOuter->getQuorumKeys().second.contains(calcNodeID(masterPublic)));
BEAST_EXPECT(trustedKeysOuter->listed(masterPublic));
BEAST_EXPECT(!trustedKeysOuter->trusted(masterPublic));
BEAST_EXPECT(!trustedKeysOuter->listed(signingPublicMax));

View File

@@ -547,6 +547,31 @@ TEST(ValidationsTest, get_current_public_keys)
EXPECT_TRUE(harness.vals().getCurrentNodeIDs().empty());
}
TEST(ValidationsTest, laggards_after_key_rotation)
{
SCOPED_TRACE("Laggards after key rotation");
LedgerHistoryHelper h;
Ledger const ledgerA = h["a"];
Ledger const ledgerAB = h["ab"];
TestHarness harness(h.oracle);
Node a = harness.makeNode();
Node const b = harness.makeNode();
Node const c = harness.makeNode();
// a rotates its signing key and validates behind; b is current; c is silent
a.advanceKey();
EXPECT_TRUE(ValStatus::Current == harness.add(a.validate(ledgerA)));
EXPECT_TRUE(ValStatus::Current == harness.add(b.validate(ledgerAB)));
HashSet<PeerID> trusted = {a.nodeID(), b.nodeID(), c.nodeID()};
EXPECT_TRUE(harness.vals().laggards(ledgerAB.seq(), trusted) == 1);
// Nodes heard from leave the set; the silent node stays as offline
EXPECT_TRUE(trusted == HashSet<PeerID>{c.nodeID()});
}
TEST(ValidationsTest, trusted_by_ledger_functions)
{
// Test the Validations functions that calculate a value by ledger ID

View File

@@ -880,17 +880,17 @@ struct Peer
return earliestAllowedSeq();
}
std::pair<std::size_t, HashSet<NodeKeyT>>
std::pair<std::size_t, HashSet<NodeIDT>>
getQuorumKeys()
{
HashSet<NodeKeyT> keys;
HashSet<NodeIDT> nodes;
for (auto const p : trustGraph.trustedPeers(this))
keys.insert(p->key);
return {quorum, keys};
nodes.insert(p->id);
return {quorum, nodes};
}
std::size_t
laggards(Ledger::Seq const seq, HashSet<NodeKeyT>& trusted)
laggards(Ledger::Seq const seq, HashSet<NodeIDT>& trusted)
{
return validations.laggards(seq, trusted);
}

View File

@@ -1023,7 +1023,7 @@ RCLConsensus::Adaptor::getValidLedgerIndex() const
return ledgerMaster_.getValidLedgerIndex();
}
std::pair<std::size_t, HashSet<RCLConsensus::Adaptor::NodeKeyT>>
std::pair<std::size_t, HashSet<RCLConsensus::Adaptor::NodeIDT>>
RCLConsensus::Adaptor::getQuorumKeys() const
{
return app_.getValidators().getQuorumKeys();
@@ -1032,9 +1032,9 @@ RCLConsensus::Adaptor::getQuorumKeys() const
std::size_t
RCLConsensus::Adaptor::laggards(
LedgerT::Seq const seq,
HashSet<RCLConsensus::Adaptor::NodeKeyT>& trustedKeys) const
HashSet<RCLConsensus::Adaptor::NodeIDT>& trustedNodes) const
{
return app_.getValidations().laggards(seq, trustedKeys);
return app_.getValidations().laggards(seq, trustedNodes);
}
bool

View File

@@ -18,7 +18,6 @@
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/CanonicalTXSet.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/RippleLedgerHash.h>
#include <xrpl/protocol/UintTypes.h>
@@ -89,7 +88,6 @@ class RCLConsensus
public:
using LedgerT = RCLCxLedger;
using NodeIDT = NodeID;
using NodeKeyT = PublicKey;
using TxSetT = RCLTxSet;
using PeerPositionT = RCLCxPeerPos;
@@ -144,11 +142,11 @@ class RCLConsensus
LedgerIndex
getValidLedgerIndex() const;
std::pair<std::size_t, HashSet<NodeKeyT>>
std::pair<std::size_t, HashSet<NodeIDT>>
getQuorumKeys() const;
std::size_t
laggards(LedgerT::Seq const seq, HashSet<NodeKeyT>& trustedKeys) const;
laggards(LedgerT::Seq const seq, HashSet<NodeIDT>& trustedNodes) const;
/**
* Whether I am a validator.

View File

@@ -169,8 +169,11 @@ handleNewValidation(
auto& validations = app.getValidations();
// masterKey is seated only if validator is trusted or listed
auto const outcome = validations.add(calcNodeID(masterKey.value_or(signingKey)), val);
// masterKey is seated only if validator is trusted or listed; a manifest
// applied since deserialization can change the NodeID set then
auto const nodeID = calcNodeID(masterKey.value_or(signingKey));
val->setNodeID(nodeID);
auto const outcome = validations.add(nodeID, val);
if (outcome == ValStatus::Current)
{

View File

@@ -270,7 +270,8 @@ using RCLValidations = Validations<RCLValidationsAdaptor>;
* Handle a new validation
*
* Also sets the trust status of a validation based on the validating node's
* public key and this node's current UNL.
* public key and this node's current UNL, and sets its NodeID to the one it
* is stored under, from the master key a manifest now maps its signer to.
*
* @param app Application object containing validations and ledgerMaster
* @param val The validation to add

View File

@@ -259,11 +259,6 @@ class ValidatorList
// Minimum number of lists on which a trusted validator must appear on
std::size_t listThreshold_{1};
// The current list of trusted signing keys. For those validators using
// a manifest, the signing key is the ephemeral key. For the ones using
// a seed, the signing key is the same as the master key.
HashSet<PublicKey> trustedSigningKeys_;
std::optional<PublicKey> localPubKey_;
// The below variable contains the Publisher list specified in the local
@@ -689,18 +684,17 @@ public:
json::Value
getJson() const;
using QuorumKeys = std::pair<std::size_t const, HashSet<PublicKey>>;
using QuorumKeys = std::pair<std::size_t const, HashSet<NodeID>>;
/**
* Get the quorum and all of the trusted keys.
* Get the quorum and the NodeIDs of all trusted validators.
*
* @return quorum and keys.
* A NodeID derives from the master key, so a validator keeps it across
* signing key rotations.
*
* @return quorum and NodeIDs.
*/
QuorumKeys
getQuorumKeys() const
{
SharedLock const readLock{mutex_};
return {quorum_, trustedSigningKeys_};
}
getQuorumKeys() const;
/**
* get the trusted master public keys

View File

@@ -19,6 +19,7 @@
#include <xrpl/protocol/STValidation.h>
#include <xrpl/protocol/SystemParameters.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol/tokens.h>
#include <xrpl/server/Wallet.h>
@@ -107,8 +108,9 @@ parseSection(Section const& section)
class TrustedVotes
{
private:
// Associates each trusted validator with the last votes we saw from them
// and an expiration for that record.
// Associates each trusted validator's NodeID, which a signing key rotation
// does not change, with the last votes we saw from them and an expiration
// for that record.
struct UpvotesAndTimeout
{
std::vector<UInt256> upVotes;
@@ -120,7 +122,7 @@ private:
*/
std::optional<NetClock::time_point> timeout;
};
HashMap<PublicKey, UpvotesAndTimeout> recordedVotes_;
HashMap<NodeID, UpvotesAndTimeout> recordedVotes_;
public:
TrustedVotes() = default;
@@ -132,14 +134,14 @@ public:
//
// Call with AmendmentTable::mutex_ locked.
void
trustChanged(HashSet<PublicKey> const& allTrusted, std::scoped_lock<std::mutex> const& lock)
trustChanged(HashSet<NodeID> const& allTrusted, std::scoped_lock<std::mutex> const& lock)
{
decltype(recordedVotes_) newRecordedVotes;
newRecordedVotes.reserve(allTrusted.size());
// Make sure every PublicKey in allTrusted is represented in
// Make sure every NodeID in allTrusted is represented in
// recordedVotes_. Also make sure recordedVotes_ contains
// no additional PublicKeys.
// no additional NodeIDs.
for (auto& trusted : allTrusted)
{
if (recordedVotes_.contains(trusted))
@@ -192,7 +194,7 @@ public:
{
auto const pkHuman = toBase58(TokenType::NodePublic, val->getSignerPublic());
// If this validation comes from one of our trusted validators...
if (auto const iter = recordedVotes_.find(val->getSignerPublic());
if (auto const iter = recordedVotes_.find(val->getNodeID());
iter != recordedVotes_.end())
{
iter->second.timeout = newTimeout;
@@ -230,7 +232,7 @@ public:
std::ranges::for_each(
recordedVotes_,
[&closeTime, newTimeout, &j](decltype(recordedVotes_)::value_type& votes) {
auto const pkHuman = toBase58(TokenType::NodePublic, votes.first);
auto const nodeHuman = to_string(votes.first);
if (!votes.second.timeout)
{
XRPL_ASSERT(
@@ -239,11 +241,11 @@ public:
"upvotes");
JLOG(j.debug()) << "recordVotes: Have not received any "
"amendment votes from "
<< pkHuman << " since last timeout or startup";
<< nodeHuman << " since last timeout or startup";
}
else if (closeTime > votes.second.timeout)
{
JLOG(j.debug()) << "recordVotes: Timeout: Clearing votes from " << pkHuman;
JLOG(j.debug()) << "recordVotes: Timeout: Clearing votes from " << nodeHuman;
votes.second.timeout.reset();
votes.second.upVotes.clear();
}
@@ -256,7 +258,7 @@ public:
using namespace std::chrono;
auto const age = duration_cast<minutes>(newTimeout - *votes.second.timeout);
JLOG(j.debug()) << "recordVotes: Using " << age.count()
<< "min old cached votes from " << pkHuman;
<< "min old cached votes from " << nodeHuman;
}
});
}
@@ -499,7 +501,7 @@ public:
MajorityAmendmentsT const& majority) override;
void
trustChanged(HashSet<PublicKey> const& allTrusted) override;
trustChanged(HashSet<NodeID> const& allTrusted) override;
std::vector<UInt256>
doValidation(std::set<UInt256> const& enabledAmendments) const override;
@@ -953,7 +955,7 @@ AmendmentTableImpl::doValidatedLedger(
}
void
AmendmentTableImpl::trustChanged(HashSet<PublicKey> const& allTrusted)
AmendmentTableImpl::trustChanged(HashSet<NodeID> const& allTrusted)
{
std::scoped_lock const lock(mutex_);
previousTrustedVotes_.trustChanged(allTrusted, lock);

View File

@@ -1916,23 +1916,6 @@ ValidatorList::updateTrusted(
trustChanges.added.insert(calcNodeID(val.first));
}
// If there were any changes, we need to update the ephemeral signing
// keys:
if (!trustChanges.added.empty() || !trustChanges.removed.empty())
{
trustedSigningKeys_.clear();
// trustedMasterKeys_ contain non-revoked manifests only. Hence the
// manifests must contain a valid signingKey
for (auto const& k : trustedMasterKeys_)
{
std::optional<PublicKey> const signingKey = validatorManifests_.getSigningKey(k);
XRPL_ASSERT(signingKey, "xrpl::ValidatorList::updateTrusted : found signing key");
trustedSigningKeys_.insert(
*signingKey); // NOLINT(bugprone-unchecked-optional-access) assert above
}
}
JLOG(j_.debug()) << trustedMasterKeys_.size() << " of " << keyListings_.size()
<< " listed validators eligible for inclusion in the trusted set";
@@ -1978,6 +1961,17 @@ ValidatorList::updateTrusted(
return trustChanges;
}
ValidatorList::QuorumKeys
ValidatorList::getQuorumKeys() const
{
SharedLock const readLock{mutex_};
HashSet<NodeID> nodeIDs;
nodeIDs.reserve(trustedMasterKeys_.size());
for (auto const& k : trustedMasterKeys_)
nodeIDs.insert(calcNodeID(k));
return {quorum_, std::move(nodeIDs)};
}
HashSet<PublicKey>
ValidatorList::getTrustedMasterKeys() const
{