mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
Update validations on UNL change (RIPD-1566):
Change the trust status of existing validations based when nodes are added or removed from the UNL.
This commit is contained in:
@@ -81,7 +81,7 @@ RCLConsensus::Adaptor::Adaptor(
|
|||||||
, localTxs_(localTxs)
|
, localTxs_(localTxs)
|
||||||
, inboundTransactions_{inboundTransactions}
|
, inboundTransactions_{inboundTransactions}
|
||||||
, j_(journal)
|
, j_(journal)
|
||||||
, nodeID_{calcNodeID(app.nodeIdentity().first)}
|
, nodeID_{validatorKeys.nodeID}
|
||||||
, valPublic_{validatorKeys.publicKey}
|
, valPublic_{validatorKeys.publicKey}
|
||||||
, valSecret_{validatorKeys.secretKey}
|
, valSecret_{validatorKeys.secretKey}
|
||||||
{
|
{
|
||||||
@@ -837,6 +837,7 @@ RCLConsensus::Adaptor::validate(RCLCxLedger const& ledger, bool proposing)
|
|||||||
ledger.id(),
|
ledger.id(),
|
||||||
validationTime,
|
validationTime,
|
||||||
valPublic_,
|
valPublic_,
|
||||||
|
nodeID_,
|
||||||
proposing /* full if proposed */);
|
proposing /* full if proposed */);
|
||||||
v->setFieldU32(sfLedgerSequence, ledger.seq());
|
v->setFieldU32(sfLedgerSequence, ledger.seq());
|
||||||
|
|
||||||
@@ -980,10 +981,11 @@ void
|
|||||||
RCLConsensus::startRound(
|
RCLConsensus::startRound(
|
||||||
NetClock::time_point const& now,
|
NetClock::time_point const& now,
|
||||||
RCLCxLedger::ID const& prevLgrId,
|
RCLCxLedger::ID const& prevLgrId,
|
||||||
RCLCxLedger const& prevLgr)
|
RCLCxLedger const& prevLgr,
|
||||||
|
hash_set<NodeID> const& nowUntrusted)
|
||||||
{
|
{
|
||||||
ScopedLockType _{mutex_};
|
ScopedLockType _{mutex_};
|
||||||
consensus_.startRound(
|
consensus_.startRound(
|
||||||
now, prevLgrId, prevLgr, adaptor_.preStartRound(prevLgr));
|
now, prevLgrId, prevLgr, nowUntrusted, adaptor_.preStartRound(prevLgr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -427,7 +427,8 @@ public:
|
|||||||
startRound(
|
startRound(
|
||||||
NetClock::time_point const& now,
|
NetClock::time_point const& now,
|
||||||
RCLCxLedger::ID const& prevLgrId,
|
RCLCxLedger::ID const& prevLgrId,
|
||||||
RCLCxLedger const& prevLgr);
|
RCLCxLedger const& prevLgr,
|
||||||
|
hash_set<NodeID> const& nowUntrusted);
|
||||||
|
|
||||||
//! @see Consensus::timerEntry
|
//! @see Consensus::timerEntry
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ RCLValidationsAdaptor::onStale(RCLValidation&& v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCLValidationsAdaptor::flush(hash_map<PublicKey, RCLValidation>&& remaining)
|
RCLValidationsAdaptor::flush(hash_map<NodeID, RCLValidation>&& remaining)
|
||||||
{
|
{
|
||||||
bool anyNew = false;
|
bool anyNew = false;
|
||||||
{
|
{
|
||||||
@@ -317,7 +317,7 @@ handleNewValidation(Application& app,
|
|||||||
// masterKey is seated only if validator is trusted or listed
|
// masterKey is seated only if validator is trusted or listed
|
||||||
if (masterKey)
|
if (masterKey)
|
||||||
{
|
{
|
||||||
ValStatus const outcome = validations.add(*masterKey, val);
|
ValStatus const outcome = validations.add(calcNodeID(*masterKey), val);
|
||||||
if(j.debug())
|
if(j.debug())
|
||||||
dmp(j.debug(), to_string(outcome));
|
dmp(j.debug(), to_string(outcome));
|
||||||
|
|
||||||
@@ -327,13 +327,6 @@ handleNewValidation(Application& app,
|
|||||||
dmp(j.warn(),
|
dmp(j.warn(),
|
||||||
"already validated sequence at or past " + to_string(seq));
|
"already validated sequence at or past " + to_string(seq));
|
||||||
}
|
}
|
||||||
else if(outcome == ValStatus::repeatID && j.warn())
|
|
||||||
{
|
|
||||||
auto const seq = val->getFieldU32(sfLedgerSequence);
|
|
||||||
dmp(j.warn(),
|
|
||||||
"already validated ledger with same id but different seq "
|
|
||||||
"than" + to_string(seq));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val->isTrusted() && outcome == ValStatus::current)
|
if (val->isTrusted() && outcome == ValStatus::current)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -101,7 +101,19 @@ public:
|
|||||||
return val_->isTrusted();
|
return val_->isTrusted();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the validatioon is full (not-partial)
|
void
|
||||||
|
setTrusted()
|
||||||
|
{
|
||||||
|
val_->setTrusted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setUntrusted()
|
||||||
|
{
|
||||||
|
val_->setUntrusted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the validation is full (not-partial)
|
||||||
bool
|
bool
|
||||||
full() const
|
full() const
|
||||||
{
|
{
|
||||||
@@ -214,7 +226,7 @@ public:
|
|||||||
@param remaining The remaining validations to flush
|
@param remaining The remaining validations to flush
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
flush(hash_map<PublicKey, RCLValidation>&& remaining);
|
flush(hash_map<NodeID, RCLValidation>&& remaining);
|
||||||
|
|
||||||
/** Attempt to acquire the ledger with given id from the network */
|
/** Attempt to acquire the ledger with given id from the network */
|
||||||
boost::optional<RCLValidatedLedger>
|
boost::optional<RCLValidatedLedger>
|
||||||
|
|||||||
@@ -293,8 +293,7 @@ public:
|
|||||||
// Ledger proposal/close functions.
|
// Ledger proposal/close functions.
|
||||||
void processTrustedProposal (
|
void processTrustedProposal (
|
||||||
RCLCxPeerPos proposal,
|
RCLCxPeerPos proposal,
|
||||||
std::shared_ptr<protocol::TMProposeSet> set,
|
std::shared_ptr<protocol::TMProposeSet> set) override;
|
||||||
NodeID const &node) override;
|
|
||||||
|
|
||||||
bool recvValidation (
|
bool recvValidation (
|
||||||
STValidation::ref val, std::string const& source) override;
|
STValidation::ref val, std::string const& source) override;
|
||||||
@@ -1434,13 +1433,17 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
|
|||||||
assert (closingInfo.parentHash ==
|
assert (closingInfo.parentHash ==
|
||||||
m_ledgerMaster.getClosedLedger()->info().hash);
|
m_ledgerMaster.getClosedLedger()->info().hash);
|
||||||
|
|
||||||
app_.validators().onConsensusStart (
|
TrustChanges const changes = app_.validators().updateTrusted(
|
||||||
app_.getValidations().getCurrentPublicKeys ());
|
app_.getValidations().getCurrentNodeIDs());
|
||||||
|
|
||||||
mConsensus.startRound (
|
if (!changes.added.empty() || !changes.removed.empty())
|
||||||
|
app_.getValidations().trustChanged(changes.added, changes.removed);
|
||||||
|
|
||||||
|
mConsensus.startRound(
|
||||||
app_.timeKeeper().closeTime(),
|
app_.timeKeeper().closeTime(),
|
||||||
networkClosed,
|
networkClosed,
|
||||||
prevLedger);
|
prevLedger,
|
||||||
|
changes.removed);
|
||||||
|
|
||||||
JLOG(m_journal.debug()) << "Initiating consensus engine";
|
JLOG(m_journal.debug()) << "Initiating consensus engine";
|
||||||
return true;
|
return true;
|
||||||
@@ -1453,8 +1456,7 @@ uint256 NetworkOPsImp::getConsensusLCL ()
|
|||||||
|
|
||||||
void NetworkOPsImp::processTrustedProposal (
|
void NetworkOPsImp::processTrustedProposal (
|
||||||
RCLCxPeerPos peerPos,
|
RCLCxPeerPos peerPos,
|
||||||
std::shared_ptr<protocol::TMProposeSet> set,
|
std::shared_ptr<protocol::TMProposeSet> set)
|
||||||
NodeID const& node)
|
|
||||||
{
|
{
|
||||||
if (mConsensus.peerProposal(
|
if (mConsensus.peerProposal(
|
||||||
app_.timeKeeper().closeTime(), peerPos))
|
app_.timeKeeper().closeTime(), peerPos))
|
||||||
|
|||||||
@@ -153,8 +153,7 @@ public:
|
|||||||
|
|
||||||
// ledger proposal/close functions
|
// ledger proposal/close functions
|
||||||
virtual void processTrustedProposal (RCLCxPeerPos peerPos,
|
virtual void processTrustedProposal (RCLCxPeerPos peerPos,
|
||||||
std::shared_ptr<protocol::TMProposeSet> set,
|
std::shared_ptr<protocol::TMProposeSet> set) = 0;
|
||||||
NodeID const& node) = 0;
|
|
||||||
|
|
||||||
virtual bool recvValidation (STValidation::ref val,
|
virtual bool recvValidation (STValidation::ref val,
|
||||||
std::string const& source) = 0;
|
std::string const& source) = 0;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <ripple/beast/utility/Journal.h>
|
#include <ripple/beast/utility/Journal.h>
|
||||||
#include <ripple/protocol/PublicKey.h>
|
#include <ripple/protocol/PublicKey.h>
|
||||||
#include <ripple/protocol/SecretKey.h>
|
#include <ripple/protocol/SecretKey.h>
|
||||||
|
#include <ripple/protocol/UintTypes.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
@@ -37,6 +38,7 @@ class ValidatorKeys
|
|||||||
public:
|
public:
|
||||||
PublicKey publicKey;
|
PublicKey publicKey;
|
||||||
SecretKey secretKey;
|
SecretKey secretKey;
|
||||||
|
NodeID nodeID;
|
||||||
std::string manifest;
|
std::string manifest;
|
||||||
ValidatorKeys(Config const& config, beast::Journal j);
|
ValidatorKeys(Config const& config, beast::Journal j);
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,14 @@ enum class ListDisposition
|
|||||||
std::string
|
std::string
|
||||||
to_string(ListDisposition disposition);
|
to_string(ListDisposition disposition);
|
||||||
|
|
||||||
|
/** Changes in trusted nodes after updating validator list
|
||||||
|
*/
|
||||||
|
struct TrustChanges
|
||||||
|
{
|
||||||
|
hash_set<NodeID> added;
|
||||||
|
hash_set<NodeID> removed;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Trusted Validators List
|
Trusted Validators List
|
||||||
-----------------------
|
-----------------------
|
||||||
@@ -202,22 +210,23 @@ public:
|
|||||||
std::string const& signature,
|
std::string const& signature,
|
||||||
std::uint32_t version);
|
std::uint32_t version);
|
||||||
|
|
||||||
/** Update trusted keys
|
/** Update trusted nodes
|
||||||
|
|
||||||
Reset the trusted keys based on latest manifests, received validations,
|
Reset the trusted nodes based on latest manifests, received validations,
|
||||||
and lists.
|
and lists.
|
||||||
|
|
||||||
@param seenValidators Set of public keys used to sign recently
|
@param seenValidators Set of NodeIDs of validators that have signed
|
||||||
received validations
|
recently received validations
|
||||||
|
|
||||||
|
@return TrustedKeyChanges instance with newly trusted or untrusted
|
||||||
|
node identities.
|
||||||
|
|
||||||
@par Thread Safety
|
@par Thread Safety
|
||||||
|
|
||||||
May be called concurrently
|
May be called concurrently
|
||||||
*/
|
*/
|
||||||
template<class KeySet>
|
TrustChanges
|
||||||
void
|
updateTrusted (hash_set<NodeID> const& seenValidators);
|
||||||
onConsensusStart (
|
|
||||||
KeySet const& seenValidators);
|
|
||||||
|
|
||||||
/** Get quorum value for current trusted key set
|
/** Get quorum value for current trusted key set
|
||||||
|
|
||||||
@@ -390,133 +399,6 @@ private:
|
|||||||
calculateMinimumQuorum (
|
calculateMinimumQuorum (
|
||||||
std::size_t nListedKeys, bool unlistedLocal=false);
|
std::size_t nListedKeys, bool unlistedLocal=false);
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class KeySet>
|
|
||||||
void
|
|
||||||
ValidatorList::onConsensusStart (
|
|
||||||
KeySet const& seenValidators)
|
|
||||||
{
|
|
||||||
boost::unique_lock<boost::shared_mutex> lock{mutex_};
|
|
||||||
|
|
||||||
// Check that lists from all configured publishers are available
|
|
||||||
bool allListsAvailable = true;
|
|
||||||
|
|
||||||
for (auto const& list : publisherLists_)
|
|
||||||
{
|
|
||||||
// Remove any expired published lists
|
|
||||||
if (TimeKeeper::time_point{} < list.second.expiration &&
|
|
||||||
list.second.expiration <= timeKeeper_.now())
|
|
||||||
removePublisherList(list.first);
|
|
||||||
|
|
||||||
if (! list.second.available)
|
|
||||||
allListsAvailable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::multimap<std::size_t, PublicKey> rankedKeys;
|
|
||||||
bool localKeyListed = false;
|
|
||||||
|
|
||||||
// "Iterate" the listed keys in random order so that the rank of multiple
|
|
||||||
// keys with the same number of listings is not deterministic
|
|
||||||
std::vector<std::size_t> indexes (keyListings_.size());
|
|
||||||
std::iota (indexes.begin(), indexes.end(), 0);
|
|
||||||
std::shuffle (indexes.begin(), indexes.end(), crypto_prng());
|
|
||||||
|
|
||||||
for (auto const& index : indexes)
|
|
||||||
{
|
|
||||||
auto const& val = std::next (keyListings_.begin(), index);
|
|
||||||
|
|
||||||
if (validatorManifests_.revoked (val->first))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (val->first == localPubKey_)
|
|
||||||
{
|
|
||||||
localKeyListed = val->second > 1;
|
|
||||||
rankedKeys.insert (
|
|
||||||
std::pair<std::size_t,PublicKey>(
|
|
||||||
std::numeric_limits<std::size_t>::max(), localPubKey_));
|
|
||||||
}
|
|
||||||
// If the total number of validators is too small, or
|
|
||||||
// no validations are being received, use all validators.
|
|
||||||
// Otherwise, do not use validators whose validations aren't
|
|
||||||
// being received.
|
|
||||||
else if (keyListings_.size() < MINIMUM_RESIZEABLE_UNL ||
|
|
||||||
seenValidators.empty() ||
|
|
||||||
seenValidators.find (val->first) != seenValidators.end ())
|
|
||||||
{
|
|
||||||
rankedKeys.insert (
|
|
||||||
std::pair<std::size_t,PublicKey>(val->second, val->first));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This minimum quorum guarantees safe overlap with the trusted sets of
|
|
||||||
// other nodes using the same set of published lists.
|
|
||||||
std::size_t quorum = calculateMinimumQuorum (keyListings_.size(),
|
|
||||||
localPubKey_.size() && !localKeyListed);
|
|
||||||
|
|
||||||
JLOG (j_.debug()) <<
|
|
||||||
rankedKeys.size() << " of " << keyListings_.size() <<
|
|
||||||
" listed validators eligible for inclusion in the trusted set";
|
|
||||||
|
|
||||||
auto size = rankedKeys.size();
|
|
||||||
|
|
||||||
// Require 80% quorum if there are lots of validators.
|
|
||||||
if (rankedKeys.size() > BYZANTINE_THRESHOLD)
|
|
||||||
{
|
|
||||||
// Use all eligible keys if there is only one trusted list
|
|
||||||
if (publisherLists_.size() == 1 ||
|
|
||||||
keyListings_.size() < MINIMUM_RESIZEABLE_UNL)
|
|
||||||
{
|
|
||||||
// Try to raise the quorum to at least 80% of the trusted set
|
|
||||||
quorum = std::max(quorum, size - size / 5);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Reduce the trusted set size so that the quorum represents
|
|
||||||
// at least 80%
|
|
||||||
size = quorum * 1.25;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minimumQuorum_ && seenValidators.size() < quorum)
|
|
||||||
{
|
|
||||||
quorum = *minimumQuorum_;
|
|
||||||
JLOG (j_.warn())
|
|
||||||
<< "Using unsafe quorum of "
|
|
||||||
<< quorum_
|
|
||||||
<< " as specified in the command line";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not use achievable quorum until lists from all configured
|
|
||||||
// publishers are available
|
|
||||||
else if (! allListsAvailable)
|
|
||||||
quorum = std::numeric_limits<std::size_t>::max();
|
|
||||||
|
|
||||||
trustedKeys_.clear();
|
|
||||||
quorum_ = quorum;
|
|
||||||
|
|
||||||
for (auto const& val : boost::adaptors::reverse (rankedKeys))
|
|
||||||
{
|
|
||||||
if (size <= trustedKeys_.size())
|
|
||||||
break;
|
|
||||||
|
|
||||||
trustedKeys_.insert (val.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
JLOG (j_.debug()) <<
|
|
||||||
"Using quorum of " << quorum_ << " for new set of " <<
|
|
||||||
trustedKeys_.size() << " trusted validators";
|
|
||||||
|
|
||||||
if (trustedKeys_.size() < quorum_)
|
|
||||||
{
|
|
||||||
JLOG (j_.warn()) <<
|
|
||||||
"New quorum of " << quorum_ <<
|
|
||||||
" exceeds the number of trusted validators (" <<
|
|
||||||
trustedKeys_.size() << ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j)
|
|||||||
KeyType::secp256k1, token->validationSecret);
|
KeyType::secp256k1, token->validationSecret);
|
||||||
auto const m = Manifest::make_Manifest(
|
auto const m = Manifest::make_Manifest(
|
||||||
beast::detail::base64_decode(token->manifest));
|
beast::detail::base64_decode(token->manifest));
|
||||||
|
|
||||||
if (! m || pk != m->signingKey)
|
if (! m || pk != m->signingKey)
|
||||||
{
|
{
|
||||||
configInvalid_ = true;
|
configInvalid_ = true;
|
||||||
@@ -58,6 +57,7 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j)
|
|||||||
{
|
{
|
||||||
secretKey = token->validationSecret;
|
secretKey = token->validationSecret;
|
||||||
publicKey = pk;
|
publicKey = pk;
|
||||||
|
nodeID = calcNodeID(m->masterKey);
|
||||||
manifest = std::move(token->manifest);
|
manifest = std::move(token->manifest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,6 +82,7 @@ ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j)
|
|||||||
{
|
{
|
||||||
secretKey = generateSecretKey(KeyType::secp256k1, *seed);
|
secretKey = generateSecretKey(KeyType::secp256k1, *seed);
|
||||||
publicKey = derivePublicKey(KeyType::secp256k1, secretKey);
|
publicKey = derivePublicKey(KeyType::secp256k1, secretKey);
|
||||||
|
nodeID = calcNodeID(publicKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -597,4 +597,140 @@ ValidatorList::calculateMinimumQuorum (
|
|||||||
return nListedKeys * 2/3 + 1;
|
return nListedKeys * 2/3 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrustChanges
|
||||||
|
ValidatorList::updateTrusted(hash_set<NodeID> const& seenValidators)
|
||||||
|
{
|
||||||
|
boost::unique_lock<boost::shared_mutex> lock{mutex_};
|
||||||
|
|
||||||
|
// Check that lists from all configured publishers are available
|
||||||
|
bool allListsAvailable = true;
|
||||||
|
|
||||||
|
for (auto const& list : publisherLists_)
|
||||||
|
{
|
||||||
|
// Remove any expired published lists
|
||||||
|
if (TimeKeeper::time_point{} < list.second.expiration &&
|
||||||
|
list.second.expiration <= timeKeeper_.now())
|
||||||
|
removePublisherList(list.first);
|
||||||
|
|
||||||
|
if (! list.second.available)
|
||||||
|
allListsAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::multimap<std::size_t, PublicKey> rankedKeys;
|
||||||
|
bool localKeyListed = false;
|
||||||
|
|
||||||
|
// "Iterate" the listed keys in random order so that the rank of multiple
|
||||||
|
// keys with the same number of listings is not deterministic
|
||||||
|
std::vector<std::size_t> indexes (keyListings_.size());
|
||||||
|
std::iota (indexes.begin(), indexes.end(), 0);
|
||||||
|
std::shuffle (indexes.begin(), indexes.end(), crypto_prng());
|
||||||
|
|
||||||
|
for (auto const& index : indexes)
|
||||||
|
{
|
||||||
|
auto const& val = std::next (keyListings_.begin(), index);
|
||||||
|
|
||||||
|
if (validatorManifests_.revoked (val->first))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (val->first == localPubKey_)
|
||||||
|
{
|
||||||
|
localKeyListed = val->second > 1;
|
||||||
|
rankedKeys.insert (
|
||||||
|
std::pair<std::size_t,PublicKey>(
|
||||||
|
std::numeric_limits<std::size_t>::max(), localPubKey_));
|
||||||
|
}
|
||||||
|
// If the total number of validators is too small, or
|
||||||
|
// no validations are being received, use all validators.
|
||||||
|
// Otherwise, do not use validators whose validations aren't
|
||||||
|
// being received.
|
||||||
|
else if (
|
||||||
|
keyListings_.size() < MINIMUM_RESIZEABLE_UNL ||
|
||||||
|
seenValidators.empty() ||
|
||||||
|
seenValidators.find(calcNodeID(val->first)) != seenValidators.end())
|
||||||
|
{
|
||||||
|
rankedKeys.insert (
|
||||||
|
std::pair<std::size_t,PublicKey>(val->second, val->first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This minimum quorum guarantees safe overlap with the trusted sets of
|
||||||
|
// other nodes using the same set of published lists.
|
||||||
|
std::size_t quorum = calculateMinimumQuorum (keyListings_.size(),
|
||||||
|
localPubKey_.size() && !localKeyListed);
|
||||||
|
|
||||||
|
JLOG (j_.debug()) <<
|
||||||
|
rankedKeys.size() << " of " << keyListings_.size() <<
|
||||||
|
" listed validators eligible for inclusion in the trusted set";
|
||||||
|
|
||||||
|
auto size = rankedKeys.size();
|
||||||
|
|
||||||
|
// Require 80% quorum if there are lots of validators.
|
||||||
|
if (rankedKeys.size() > BYZANTINE_THRESHOLD)
|
||||||
|
{
|
||||||
|
// Use all eligible keys if there is only one trusted list
|
||||||
|
if (publisherLists_.size() == 1 ||
|
||||||
|
keyListings_.size() < MINIMUM_RESIZEABLE_UNL)
|
||||||
|
{
|
||||||
|
// Try to raise the quorum to at least 80% of the trusted set
|
||||||
|
quorum = std::max(quorum, size - size / 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reduce the trusted set size so that the quorum represents
|
||||||
|
// at least 80%
|
||||||
|
size = quorum * 1.25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minimumQuorum_ && seenValidators.size() < quorum)
|
||||||
|
{
|
||||||
|
quorum = *minimumQuorum_;
|
||||||
|
JLOG (j_.warn())
|
||||||
|
<< "Using unsafe quorum of "
|
||||||
|
<< quorum_
|
||||||
|
<< " as specified in the command line";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not use achievable quorum until lists from all configured
|
||||||
|
// publishers are available
|
||||||
|
else if (! allListsAvailable)
|
||||||
|
quorum = std::numeric_limits<std::size_t>::max();
|
||||||
|
|
||||||
|
TrustChanges trustChanges;
|
||||||
|
{
|
||||||
|
hash_set<PublicKey> newTrustedKeys;
|
||||||
|
for (auto const& val : boost::adaptors::reverse(rankedKeys))
|
||||||
|
{
|
||||||
|
if (size <= newTrustedKeys.size())
|
||||||
|
break;
|
||||||
|
newTrustedKeys.insert(val.second);
|
||||||
|
|
||||||
|
if (trustedKeys_.erase(val.second) == 0)
|
||||||
|
trustChanges.added.insert(calcNodeID(val.second));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& k : trustedKeys_)
|
||||||
|
trustChanges.removed.insert(calcNodeID(k));
|
||||||
|
trustedKeys_ = std::move(newTrustedKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
quorum_ = quorum;
|
||||||
|
|
||||||
|
|
||||||
|
JLOG(j_.debug()) << "Using quorum of " << quorum_ << " for new set of "
|
||||||
|
<< trustedKeys_.size() << " trusted validators ("
|
||||||
|
<< trustChanges.added.size() << " added, "
|
||||||
|
<< trustChanges.removed.size() << " removed)";
|
||||||
|
|
||||||
|
if (trustedKeys_.size() < quorum_)
|
||||||
|
{
|
||||||
|
JLOG (j_.warn()) <<
|
||||||
|
"New quorum of " << quorum_ <<
|
||||||
|
" exceeds the number of trusted validators (" <<
|
||||||
|
trustedKeys_.size() << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return trustChanges;
|
||||||
|
}
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -337,6 +337,7 @@ public:
|
|||||||
@param now The network adjusted time
|
@param now The network adjusted time
|
||||||
@param prevLedgerID the ID of the last ledger
|
@param prevLedgerID the ID of the last ledger
|
||||||
@param prevLedger The last ledger
|
@param prevLedger The last ledger
|
||||||
|
@param nowUntrusted ID of nodes that are newly untrusted this round
|
||||||
@param proposing Whether we want to send proposals to peers this round.
|
@param proposing Whether we want to send proposals to peers this round.
|
||||||
|
|
||||||
@note @b prevLedgerID is not required to the ID of @b prevLedger since
|
@note @b prevLedgerID is not required to the ID of @b prevLedger since
|
||||||
@@ -347,6 +348,7 @@ public:
|
|||||||
NetClock::time_point const& now,
|
NetClock::time_point const& now,
|
||||||
typename Ledger_t::ID const& prevLedgerID,
|
typename Ledger_t::ID const& prevLedgerID,
|
||||||
Ledger_t prevLedger,
|
Ledger_t prevLedger,
|
||||||
|
hash_set<NodeID_t> const & nowUntrusted,
|
||||||
bool proposing);
|
bool proposing);
|
||||||
|
|
||||||
/** A peer has proposed a new position, adjust our tracking.
|
/** A peer has proposed a new position, adjust our tracking.
|
||||||
@@ -552,7 +554,7 @@ private:
|
|||||||
hash_map<NodeID_t, PeerPosition_t> currPeerPositions_;
|
hash_map<NodeID_t, PeerPosition_t> currPeerPositions_;
|
||||||
|
|
||||||
// Recently received peer positions, available when transitioning between
|
// Recently received peer positions, available when transitioning between
|
||||||
// ledgers or roundss
|
// ledgers or rounds
|
||||||
hash_map<NodeID_t, std::deque<PeerPosition_t>> recentPeerPositions_;
|
hash_map<NodeID_t, std::deque<PeerPosition_t>> recentPeerPositions_;
|
||||||
|
|
||||||
// The number of proposers who participated in the last consensus round
|
// The number of proposers who participated in the last consensus round
|
||||||
@@ -583,6 +585,7 @@ Consensus<Adaptor>::startRound(
|
|||||||
NetClock::time_point const& now,
|
NetClock::time_point const& now,
|
||||||
typename Ledger_t::ID const& prevLedgerID,
|
typename Ledger_t::ID const& prevLedgerID,
|
||||||
Ledger_t prevLedger,
|
Ledger_t prevLedger,
|
||||||
|
hash_set<NodeID_t> const& nowUntrusted,
|
||||||
bool proposing)
|
bool proposing)
|
||||||
{
|
{
|
||||||
if (firstRound_)
|
if (firstRound_)
|
||||||
@@ -597,6 +600,9 @@ Consensus<Adaptor>::startRound(
|
|||||||
prevCloseTime_ = rawCloseTimes_.self;
|
prevCloseTime_ = rawCloseTimes_.self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(NodeID_t const& n : nowUntrusted)
|
||||||
|
recentPeerPositions_.erase(n);
|
||||||
|
|
||||||
ConsensusMode startMode =
|
ConsensusMode startMode =
|
||||||
proposing ? ConsensusMode::proposing : ConsensusMode::observing;
|
proposing ? ConsensusMode::proposing : ConsensusMode::observing;
|
||||||
|
|
||||||
|
|||||||
@@ -151,8 +151,6 @@ isCurrent(
|
|||||||
enum class ValStatus {
|
enum class ValStatus {
|
||||||
/// This was a new validation and was added
|
/// This was a new validation and was added
|
||||||
current,
|
current,
|
||||||
/// Already had this validation for this ID but different seq
|
|
||||||
repeatID,
|
|
||||||
/// Not current or was older than current from this node
|
/// Not current or was older than current from this node
|
||||||
stale,
|
stale,
|
||||||
/// A validation violates the increasing seq requirement
|
/// A validation violates the increasing seq requirement
|
||||||
@@ -166,8 +164,6 @@ to_string(ValStatus m)
|
|||||||
{
|
{
|
||||||
case ValStatus::current:
|
case ValStatus::current:
|
||||||
return "current";
|
return "current";
|
||||||
case ValStatus::repeatID:
|
|
||||||
return "repeatID";
|
|
||||||
case ValStatus::stale:
|
case ValStatus::stale:
|
||||||
return "stale";
|
return "stale";
|
||||||
case ValStatus::badSeq:
|
case ValStatus::badSeq:
|
||||||
@@ -204,6 +200,7 @@ to_string(ValStatus m)
|
|||||||
|
|
||||||
struct Validation
|
struct Validation
|
||||||
{
|
{
|
||||||
|
using NodeID = ...;
|
||||||
using NodeKey = ...;
|
using NodeKey = ...;
|
||||||
|
|
||||||
// Ledger ID associated with this validation
|
// Ledger ID associated with this validation
|
||||||
@@ -225,9 +222,19 @@ to_string(ValStatus m)
|
|||||||
// arrived
|
// arrived
|
||||||
bool trusted() const;
|
bool trusted() const;
|
||||||
|
|
||||||
|
// Set the validation as trusted
|
||||||
|
void setTrusted();
|
||||||
|
|
||||||
|
// Set the validation as untrusted
|
||||||
|
void setUntrusted();
|
||||||
|
|
||||||
// Whether this is a full or partial validation
|
// Whether this is a full or partial validation
|
||||||
bool full() const;
|
bool full() const;
|
||||||
|
|
||||||
|
// Identifier for this node that remains fixed even when rotating signing
|
||||||
|
// keys
|
||||||
|
NodeID nodeID() const;
|
||||||
|
|
||||||
implementation_specific_t
|
implementation_specific_t
|
||||||
unwrap() -> return the implementation-specific type being wrapped
|
unwrap() -> return the implementation-specific type being wrapped
|
||||||
|
|
||||||
@@ -246,7 +253,7 @@ to_string(ValStatus m)
|
|||||||
void onStale(Validation && );
|
void onStale(Validation && );
|
||||||
|
|
||||||
// Flush the remaining validations (typically done on shutdown)
|
// Flush the remaining validations (typically done on shutdown)
|
||||||
void flush(hash_map<NodeKey,Validation> && remaining);
|
void flush(hash_map<NodeID,Validation> && remaining);
|
||||||
|
|
||||||
// Return the current network time (used to determine staleness)
|
// Return the current network time (used to determine staleness)
|
||||||
NetClock::time_point now() const;
|
NetClock::time_point now() const;
|
||||||
@@ -268,7 +275,7 @@ class Validations
|
|||||||
using Ledger = typename Adaptor::Ledger;
|
using Ledger = typename Adaptor::Ledger;
|
||||||
using ID = typename Ledger::ID;
|
using ID = typename Ledger::ID;
|
||||||
using Seq = typename Ledger::Seq;
|
using Seq = typename Ledger::Seq;
|
||||||
using NodeKey = typename Validation::NodeKey;
|
using NodeID = typename Validation::NodeID;
|
||||||
|
|
||||||
using WrappedValidationType = std::decay_t<
|
using WrappedValidationType = std::decay_t<
|
||||||
std::result_of_t<decltype (&Validation::unwrap)(Validation)>>;
|
std::result_of_t<decltype (&Validation::unwrap)(Validation)>>;
|
||||||
@@ -279,18 +286,18 @@ class Validations
|
|||||||
mutable Mutex mutex_;
|
mutable Mutex mutex_;
|
||||||
|
|
||||||
// Validations from currently listed and trusted nodes (partial and full)
|
// Validations from currently listed and trusted nodes (partial and full)
|
||||||
hash_map<NodeKey, Validation> current_;
|
hash_map<NodeID, Validation> current_;
|
||||||
|
|
||||||
// Used to enforce the largest validation invariant for the local node
|
// Used to enforce the largest validation invariant for the local node
|
||||||
SeqEnforcer<Seq> localSeqEnforcer_;
|
SeqEnforcer<Seq> localSeqEnforcer_;
|
||||||
|
|
||||||
// Sequence of the largest validation received from each node
|
// Sequence of the largest validation received from each node
|
||||||
hash_map<NodeKey, SeqEnforcer<Seq>> seqEnforcers_;
|
hash_map<NodeID, SeqEnforcer<Seq>> seqEnforcers_;
|
||||||
|
|
||||||
//! Validations from listed nodes, indexed by ledger id (partial and full)
|
//! Validations from listed nodes, indexed by ledger id (partial and full)
|
||||||
beast::aged_unordered_map<
|
beast::aged_unordered_map<
|
||||||
ID,
|
ID,
|
||||||
hash_map<NodeKey, Validation>,
|
hash_map<NodeID, Validation>,
|
||||||
std::chrono::steady_clock,
|
std::chrono::steady_clock,
|
||||||
beast::uhash<>>
|
beast::uhash<>>
|
||||||
byLedger_;
|
byLedger_;
|
||||||
@@ -300,10 +307,10 @@ class Validations
|
|||||||
|
|
||||||
// Last (validated) ledger successfully acquired. If in this map, it is
|
// Last (validated) ledger successfully acquired. If in this map, it is
|
||||||
// accounted for in the trie.
|
// accounted for in the trie.
|
||||||
hash_map<NodeKey, Ledger> lastLedger_;
|
hash_map<NodeID, Ledger> lastLedger_;
|
||||||
|
|
||||||
// Set of ledgers being acquired from the network
|
// Set of ledgers being acquired from the network
|
||||||
hash_map<std::pair<Seq,ID>, hash_set<NodeKey>> acquiring_;
|
hash_map<std::pair<Seq,ID>, hash_set<NodeID>> acquiring_;
|
||||||
|
|
||||||
// Parameters to determine validation staleness
|
// Parameters to determine validation staleness
|
||||||
ValidationParms const parms_;
|
ValidationParms const parms_;
|
||||||
@@ -315,23 +322,23 @@ class Validations
|
|||||||
private:
|
private:
|
||||||
// Remove support of a validated ledger
|
// Remove support of a validated ledger
|
||||||
void
|
void
|
||||||
removeTrie(ScopedLock const&, NodeKey const& key, Validation const& val)
|
removeTrie(ScopedLock const&, NodeID const& nodeID, Validation const& val)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto it = acquiring_.find(std::make_pair(val.seq(), val.ledgerID()));
|
auto it = acquiring_.find(std::make_pair(val.seq(), val.ledgerID()));
|
||||||
if (it != acquiring_.end())
|
if (it != acquiring_.end())
|
||||||
{
|
{
|
||||||
it->second.erase(key);
|
it->second.erase(nodeID);
|
||||||
if (it->second.empty())
|
if (it->second.empty())
|
||||||
acquiring_.erase(it);
|
acquiring_.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto it = lastLedger_.find(key);
|
auto it = lastLedger_.find(nodeID);
|
||||||
if (it != lastLedger_.end() && it->second.id() == val.ledgerID())
|
if (it != lastLedger_.end() && it->second.id() == val.ledgerID())
|
||||||
{
|
{
|
||||||
trie_.remove(it->second);
|
trie_.remove(it->second);
|
||||||
lastLedger_.erase(key);
|
lastLedger_.erase(nodeID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,8 +352,8 @@ private:
|
|||||||
if (boost::optional<Ledger> ledger =
|
if (boost::optional<Ledger> ledger =
|
||||||
adaptor_.acquire(it->first.second))
|
adaptor_.acquire(it->first.second))
|
||||||
{
|
{
|
||||||
for (NodeKey const& key : it->second)
|
for (NodeID const& nodeID : it->second)
|
||||||
updateTrie(lock, key, *ledger);
|
updateTrie(lock, nodeID, *ledger);
|
||||||
|
|
||||||
it = acquiring_.erase(it);
|
it = acquiring_.erase(it);
|
||||||
}
|
}
|
||||||
@@ -357,9 +364,9 @@ private:
|
|||||||
|
|
||||||
// Update the trie to reflect a new validated ledger
|
// Update the trie to reflect a new validated ledger
|
||||||
void
|
void
|
||||||
updateTrie(ScopedLock const&, NodeKey const& key, Ledger ledger)
|
updateTrie(ScopedLock const&, NodeID const& nodeID, Ledger ledger)
|
||||||
{
|
{
|
||||||
auto ins = lastLedger_.emplace(key, ledger);
|
auto ins = lastLedger_.emplace(nodeID, ledger);
|
||||||
if (!ins.second)
|
if (!ins.second)
|
||||||
{
|
{
|
||||||
trie_.remove(ins.first->second);
|
trie_.remove(ins.first->second);
|
||||||
@@ -376,16 +383,16 @@ private:
|
|||||||
node remains.
|
node remains.
|
||||||
|
|
||||||
@param lock Existing lock of mutex_
|
@param lock Existing lock of mutex_
|
||||||
@param key The master public key identifying the validating node
|
@param nodeID The node identifier of the validating node
|
||||||
@param val The trusted validation issued by the node
|
@param val The trusted validation issued by the node
|
||||||
@param prior If not none, the last current validated ledger Seq,ID of key
|
@param prior If not none, the last current validated ledger Seq,ID of key
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
updateTrie(
|
updateTrie(
|
||||||
ScopedLock const& lock,
|
ScopedLock const& lock,
|
||||||
NodeKey const& key,
|
NodeID const& nodeID,
|
||||||
Validation const& val,
|
Validation const& val,
|
||||||
boost::optional<std::pair<Seq,ID>> prior)
|
boost::optional<std::pair<Seq, ID>> prior)
|
||||||
{
|
{
|
||||||
assert(val.trusted());
|
assert(val.trusted());
|
||||||
|
|
||||||
@@ -395,7 +402,7 @@ private:
|
|||||||
auto it = acquiring_.find(*prior);
|
auto it = acquiring_.find(*prior);
|
||||||
if (it != acquiring_.end())
|
if (it != acquiring_.end())
|
||||||
{
|
{
|
||||||
it->second.erase(key);
|
it->second.erase(nodeID);
|
||||||
if (it->second.empty())
|
if (it->second.empty())
|
||||||
acquiring_.erase(it);
|
acquiring_.erase(it);
|
||||||
}
|
}
|
||||||
@@ -403,20 +410,20 @@ private:
|
|||||||
|
|
||||||
checkAcquired(lock);
|
checkAcquired(lock);
|
||||||
|
|
||||||
std::pair<Seq,ID> valPair{val.seq(),val.ledgerID()};
|
std::pair<Seq, ID> valPair{val.seq(), val.ledgerID()};
|
||||||
auto it = acquiring_.find(valPair);
|
auto it = acquiring_.find(valPair);
|
||||||
if(it != acquiring_.end())
|
if (it != acquiring_.end())
|
||||||
{
|
{
|
||||||
it->second.insert(key);
|
it->second.insert(nodeID);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (boost::optional<Ledger> ledger = adaptor_.acquire(val.ledgerID()))
|
if (boost::optional<Ledger> ledger =
|
||||||
updateTrie(lock, key, *ledger);
|
adaptor_.acquire(val.ledgerID()))
|
||||||
|
updateTrie(lock, nodeID, *ledger);
|
||||||
else
|
else
|
||||||
acquiring_[valPair].insert(key);
|
acquiring_[valPair].insert(nodeID);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Use the trie for a calculation
|
/** Use the trie for a calculation
|
||||||
@@ -448,7 +455,7 @@ private:
|
|||||||
@param lock Existing lock of mutex_
|
@param lock Existing lock of mutex_
|
||||||
@param pre Invokable with signature (std::size_t) called prior to
|
@param pre Invokable with signature (std::size_t) called prior to
|
||||||
looping.
|
looping.
|
||||||
@param f Invokable with signature (NodeKey const &, Validations const &)
|
@param f Invokable with signature (NodeID const &, Validations const &)
|
||||||
for each current validation.
|
for each current validation.
|
||||||
|
|
||||||
@note The invokable `pre` is called _prior_ to checking for staleness
|
@note The invokable `pre` is called _prior_ to checking for staleness
|
||||||
@@ -489,7 +496,7 @@ private:
|
|||||||
@param lock Existing lock on mutex_
|
@param lock Existing lock on mutex_
|
||||||
@param ledgerID The identifier of the ledger
|
@param ledgerID The identifier of the ledger
|
||||||
@param pre Invokable with signature(std::size_t)
|
@param pre Invokable with signature(std::size_t)
|
||||||
@param f Invokable with signature (NodeKey const &, Validation const &)
|
@param f Invokable with signature (NodeID const &, Validation const &)
|
||||||
|
|
||||||
@note The invokable `pre` is called prior to iterating validations. The
|
@note The invokable `pre` is called prior to iterating validations. The
|
||||||
argument is the number of times `f` will be called.
|
argument is the number of times `f` will be called.
|
||||||
@@ -514,7 +521,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
/** Constructor
|
/** Constructor
|
||||||
|
|
||||||
@param p ValidationParms to control staleness/expiration of validaitons
|
@param p ValidationParms to control staleness/expiration of validations
|
||||||
@param c Clock to use for expiring validations stored by ledger
|
@param c Clock to use for expiring validations stored by ledger
|
||||||
@param ts Parameters for constructing Adaptor instance
|
@param ts Parameters for constructing Adaptor instance
|
||||||
*/
|
*/
|
||||||
@@ -561,15 +568,12 @@ public:
|
|||||||
|
|
||||||
Attempt to add a new validation.
|
Attempt to add a new validation.
|
||||||
|
|
||||||
@param key The master key associated with this validation
|
@param nodeID The identity of the node issuing this validation
|
||||||
@param val The validation to store
|
@param val The validation to store
|
||||||
@return The outcome
|
@return The outcome
|
||||||
|
|
||||||
@note The provided key may differ from the validation's key()
|
|
||||||
member if the validator is using ephemeral signing keys.
|
|
||||||
*/
|
*/
|
||||||
ValStatus
|
ValStatus
|
||||||
add(NodeKey const& key, Validation const& val)
|
add(NodeID const& nodeID, Validation const& val)
|
||||||
{
|
{
|
||||||
if (!isCurrent(parms_, adaptor_.now(), val.signTime(), val.seenTime()))
|
if (!isCurrent(parms_, adaptor_.now(), val.signTime(), val.seenTime()))
|
||||||
return ValStatus::stale;
|
return ValStatus::stale;
|
||||||
@@ -580,17 +584,16 @@ public:
|
|||||||
// Check that validation sequence is greater than any non-expired
|
// Check that validation sequence is greater than any non-expired
|
||||||
// validations sequence from that validator
|
// validations sequence from that validator
|
||||||
auto const now = byLedger_.clock().now();
|
auto const now = byLedger_.clock().now();
|
||||||
SeqEnforcer<Seq>& enforcer = seqEnforcers_[key];
|
SeqEnforcer<Seq>& enforcer = seqEnforcers_[nodeID];
|
||||||
if (!enforcer(now, val.seq(), parms_))
|
if (!enforcer(now, val.seq(), parms_))
|
||||||
return ValStatus::badSeq;
|
return ValStatus::badSeq;
|
||||||
|
|
||||||
// This validation is a repeat if we already have
|
// Use insert_or_assign when C++17 supported
|
||||||
// one with the same id for this key
|
auto ret = byLedger_[val.ledgerID()].emplace(nodeID, val);
|
||||||
auto const ret = byLedger_[val.ledgerID()].emplace(key, val);
|
if (!ret.second)
|
||||||
if (!ret.second && ret.first->second.key() == val.key())
|
ret.first->second = val;
|
||||||
return ValStatus::repeatID;
|
|
||||||
|
|
||||||
auto const ins = current_.emplace(key, val);
|
auto const ins = current_.emplace(nodeID, val);
|
||||||
if (!ins.second)
|
if (!ins.second)
|
||||||
{
|
{
|
||||||
// Replace existing only if this one is newer
|
// Replace existing only if this one is newer
|
||||||
@@ -601,14 +604,14 @@ public:
|
|||||||
adaptor_.onStale(std::move(oldVal));
|
adaptor_.onStale(std::move(oldVal));
|
||||||
ins.first->second = val;
|
ins.first->second = val;
|
||||||
if (val.trusted())
|
if (val.trusted())
|
||||||
updateTrie(lock, key, val, old);
|
updateTrie(lock, nodeID, val, old);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return ValStatus::stale;
|
return ValStatus::stale;
|
||||||
}
|
}
|
||||||
else if (val.trusted())
|
else if (val.trusted())
|
||||||
{
|
{
|
||||||
updateTrie(lock, key, val, boost::none);
|
updateTrie(lock, nodeID, val, boost::none);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ValStatus::current;
|
return ValStatus::current;
|
||||||
@@ -626,6 +629,50 @@ public:
|
|||||||
beast::expire(byLedger_, parms_.validationSET_EXPIRES);
|
beast::expire(byLedger_, parms_.validationSET_EXPIRES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Update trust status of validations
|
||||||
|
|
||||||
|
Updates the trusted status of known validations to account for nodes
|
||||||
|
that have been added or removed from the UNL. This also updates the trie
|
||||||
|
to ensure only currently trusted nodes' validations are used.
|
||||||
|
|
||||||
|
@param added Identifiers of nodes that are now trusted
|
||||||
|
@param removed Identifiers of nodes that are no longer trusted
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
trustChanged(hash_set<NodeID> const& added, hash_set<NodeID> const& removed)
|
||||||
|
{
|
||||||
|
ScopedLock lock{mutex_};
|
||||||
|
|
||||||
|
for (auto& it : current_)
|
||||||
|
{
|
||||||
|
if (added.count(it.first))
|
||||||
|
{
|
||||||
|
it.second.setTrusted();
|
||||||
|
updateTrie(lock, it.first, it.second, boost::none);
|
||||||
|
}
|
||||||
|
else if (removed.count(it.first))
|
||||||
|
{
|
||||||
|
it.second.setUntrusted();
|
||||||
|
removeTrie(lock, it.first, it.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& it : byLedger_)
|
||||||
|
{
|
||||||
|
for (auto& nodeVal : it.second)
|
||||||
|
{
|
||||||
|
if (added.count(nodeVal.first))
|
||||||
|
{
|
||||||
|
nodeVal.second.setTrusted();
|
||||||
|
}
|
||||||
|
else if (removed.count(nodeVal.first))
|
||||||
|
{
|
||||||
|
nodeVal.second.setUntrusted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Json::Value
|
Json::Value
|
||||||
getJsonTrie() const
|
getJsonTrie() const
|
||||||
{
|
{
|
||||||
@@ -663,10 +710,10 @@ public:
|
|||||||
acquiring_.end(),
|
acquiring_.end(),
|
||||||
[](auto const& a, auto const& b) {
|
[](auto const& a, auto const& b) {
|
||||||
std::pair<Seq, ID> const& aKey = a.first;
|
std::pair<Seq, ID> const& aKey = a.first;
|
||||||
typename hash_set<NodeKey>::size_type const& aSize =
|
typename hash_set<NodeID>::size_type const& aSize =
|
||||||
a.second.size();
|
a.second.size();
|
||||||
std::pair<Seq, ID> const& bKey = b.first;
|
std::pair<Seq, ID> const& bKey = b.first;
|
||||||
typename hash_set<NodeKey>::size_type const& bSize =
|
typename hash_set<NodeID>::size_type const& bSize =
|
||||||
b.second.size();
|
b.second.size();
|
||||||
// order by number of trusted peers validating that ledger
|
// order by number of trusted peers validating that ledger
|
||||||
// break ties with ledger ID
|
// break ties with ledger ID
|
||||||
@@ -764,7 +811,7 @@ public:
|
|||||||
|
|
||||||
@param ledger The working ledger
|
@param ledger The working ledger
|
||||||
@param ledgerID The preferred ledger
|
@param ledgerID The preferred ledger
|
||||||
@return The number of current trusted validators working on a descendent
|
@return The number of current trusted validators working on a descendant
|
||||||
of the preferred ledger
|
of the preferred ledger
|
||||||
|
|
||||||
@note If ledger.id() != ledgerID, only counts immediate child ledgers of
|
@note If ledger.id() != ledgerID, only counts immediate child ledgers of
|
||||||
@@ -804,26 +851,26 @@ public:
|
|||||||
current(
|
current(
|
||||||
lock,
|
lock,
|
||||||
[&](std::size_t numValidations) { ret.reserve(numValidations); },
|
[&](std::size_t numValidations) { ret.reserve(numValidations); },
|
||||||
[&](NodeKey const&, Validation const& v) {
|
[&](NodeID const&, Validation const& v) {
|
||||||
if (v.trusted() && v.full())
|
if (v.trusted() && v.full())
|
||||||
ret.push_back(v.unwrap());
|
ret.push_back(v.unwrap());
|
||||||
});
|
});
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the set of known public keys associated with current validations
|
/** Get the set of node ids associated with current validations
|
||||||
|
|
||||||
@return The set of known keys for current listed validators
|
@return The set of node ids for active, listed validators
|
||||||
*/
|
*/
|
||||||
hash_set<NodeKey>
|
auto
|
||||||
getCurrentPublicKeys()
|
getCurrentNodeIDs() -> hash_set<NodeID>
|
||||||
{
|
{
|
||||||
hash_set<NodeKey> ret;
|
hash_set<NodeID> ret;
|
||||||
ScopedLock lock{mutex_};
|
ScopedLock lock{mutex_};
|
||||||
current(
|
current(
|
||||||
lock,
|
lock,
|
||||||
[&](std::size_t numValidations) { ret.reserve(numValidations); },
|
[&](std::size_t numValidations) { ret.reserve(numValidations); },
|
||||||
[&](NodeKey const& k, Validation const&) { ret.insert(k); });
|
[&](NodeID const& nid, Validation const&) { ret.insert(nid); });
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -842,7 +889,7 @@ public:
|
|||||||
lock,
|
lock,
|
||||||
ledgerID,
|
ledgerID,
|
||||||
[&](std::size_t) {}, // nothing to reserve
|
[&](std::size_t) {}, // nothing to reserve
|
||||||
[&](NodeKey const&, Validation const& v) {
|
[&](NodeID const&, Validation const& v) {
|
||||||
if (v.trusted() && v.full())
|
if (v.trusted() && v.full())
|
||||||
++count;
|
++count;
|
||||||
});
|
});
|
||||||
@@ -863,7 +910,7 @@ public:
|
|||||||
lock,
|
lock,
|
||||||
ledgerID,
|
ledgerID,
|
||||||
[&](std::size_t numValidations) { res.reserve(numValidations); },
|
[&](std::size_t numValidations) { res.reserve(numValidations); },
|
||||||
[&](NodeKey const&, Validation const& v) {
|
[&](NodeID const&, Validation const& v) {
|
||||||
if (v.trusted() && v.full())
|
if (v.trusted() && v.full())
|
||||||
res.emplace_back(v.unwrap());
|
res.emplace_back(v.unwrap());
|
||||||
});
|
});
|
||||||
@@ -885,7 +932,7 @@ public:
|
|||||||
lock,
|
lock,
|
||||||
ledgerID,
|
ledgerID,
|
||||||
[&](std::size_t numValidations) { times.reserve(numValidations); },
|
[&](std::size_t numValidations) { times.reserve(numValidations); },
|
||||||
[&](NodeKey const&, Validation const& v) {
|
[&](NodeID const&, Validation const& v) {
|
||||||
if (v.trusted() && v.full())
|
if (v.trusted() && v.full())
|
||||||
times.emplace_back(v.signTime());
|
times.emplace_back(v.signTime());
|
||||||
});
|
});
|
||||||
@@ -907,7 +954,7 @@ public:
|
|||||||
lock,
|
lock,
|
||||||
ledgerID,
|
ledgerID,
|
||||||
[&](std::size_t numValidations) { res.reserve(numValidations); },
|
[&](std::size_t numValidations) { res.reserve(numValidations); },
|
||||||
[&](NodeKey const&, Validation const& v) {
|
[&](NodeID const&, Validation const& v) {
|
||||||
if (v.trusted() && v.full())
|
if (v.trusted() && v.full())
|
||||||
{
|
{
|
||||||
boost::optional<std::uint32_t> loadFee = v.loadFee();
|
boost::optional<std::uint32_t> loadFee = v.loadFee();
|
||||||
@@ -925,7 +972,7 @@ public:
|
|||||||
void
|
void
|
||||||
flush()
|
flush()
|
||||||
{
|
{
|
||||||
hash_map<NodeKey, Validation> flushed;
|
hash_map<NodeID, Validation> flushed;
|
||||||
{
|
{
|
||||||
ScopedLock lock{mutex_};
|
ScopedLock lock{mutex_};
|
||||||
for (auto it : current_)
|
for (auto it : current_)
|
||||||
|
|||||||
@@ -899,8 +899,12 @@ OverlayImpl::send (protocol::TMValidation& m)
|
|||||||
});
|
});
|
||||||
|
|
||||||
SerialIter sit (m.validation().data(), m.validation().size());
|
SerialIter sit (m.validation().data(), m.validation().size());
|
||||||
auto val = std::make_shared <
|
auto val = std::make_shared<STValidation>(
|
||||||
STValidation> (std::ref (sit), false);
|
std::ref(sit),
|
||||||
|
[this](PublicKey const& pk) {
|
||||||
|
return calcNodeID(app_.validatorManifests().getMasterKey(pk));
|
||||||
|
},
|
||||||
|
false);
|
||||||
app_.getOPs().pubValidation (val);
|
app_.getOPs().pubValidation (val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1301,9 +1301,16 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMProposeSet> const& m)
|
|||||||
"Proposal: " << (isTrusted ? "trusted" : "UNTRUSTED");
|
"Proposal: " << (isTrusted ? "trusted" : "UNTRUSTED");
|
||||||
|
|
||||||
auto proposal = RCLCxPeerPos(
|
auto proposal = RCLCxPeerPos(
|
||||||
publicKey, signature, suppression,
|
publicKey,
|
||||||
RCLCxPeerPos::Proposal{prevLedger, set.proposeseq (), proposeHash, closeTime,
|
signature,
|
||||||
app_.timeKeeper().closeTime(),calcNodeID(publicKey)});
|
suppression,
|
||||||
|
RCLCxPeerPos::Proposal{
|
||||||
|
prevLedger,
|
||||||
|
set.proposeseq(),
|
||||||
|
proposeHash,
|
||||||
|
closeTime,
|
||||||
|
app_.timeKeeper().closeTime(),
|
||||||
|
calcNodeID(app_.validatorManifests().getMasterKey(publicKey))});
|
||||||
|
|
||||||
std::weak_ptr<PeerImp> weak = shared_from_this();
|
std::weak_ptr<PeerImp> weak = shared_from_this();
|
||||||
app_.getJobQueue ().addJob (
|
app_.getJobQueue ().addJob (
|
||||||
@@ -1611,8 +1618,13 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMValidation> const& m)
|
|||||||
STValidation::pointer val;
|
STValidation::pointer val;
|
||||||
{
|
{
|
||||||
SerialIter sit (makeSlice(m->validation()));
|
SerialIter sit (makeSlice(m->validation()));
|
||||||
val = std::make_shared <
|
val = std::make_shared<STValidation>(
|
||||||
STValidation> (std::ref (sit), false);
|
std::ref(sit),
|
||||||
|
[this](PublicKey const& pk) {
|
||||||
|
return calcNodeID(
|
||||||
|
app_.validatorManifests().getMasterKey(pk));
|
||||||
|
},
|
||||||
|
false);
|
||||||
val->setSeen (closeTime);
|
val->setSeen (closeTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1954,8 +1966,7 @@ PeerImp::checkPropose (Job& job,
|
|||||||
|
|
||||||
if (isTrusted)
|
if (isTrusted)
|
||||||
{
|
{
|
||||||
app_.getOPs ().processTrustedProposal (
|
app_.getOPs ().processTrustedProposal (peerPos, packet);
|
||||||
peerPos, packet, calcNodeID (publicKey_));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,90 +20,170 @@
|
|||||||
#ifndef RIPPLE_PROTOCOL_STVALIDATION_H_INCLUDED
|
#ifndef RIPPLE_PROTOCOL_STVALIDATION_H_INCLUDED
|
||||||
#define RIPPLE_PROTOCOL_STVALIDATION_H_INCLUDED
|
#define RIPPLE_PROTOCOL_STVALIDATION_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/basics/Log.h>
|
||||||
#include <ripple/protocol/PublicKey.h>
|
#include <ripple/protocol/PublicKey.h>
|
||||||
#include <ripple/protocol/SecretKey.h>
|
|
||||||
#include <ripple/protocol/STObject.h>
|
#include <ripple/protocol/STObject.h>
|
||||||
|
#include <ripple/protocol/SecretKey.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
// Validation flags
|
// Validation flags
|
||||||
const std::uint32_t vfFullyCanonicalSig = 0x80000000; // signature is fully canonical
|
const std::uint32_t vfFullyCanonicalSig =
|
||||||
|
0x80000000; // signature is fully canonical
|
||||||
|
|
||||||
class STValidation final
|
class STValidation final : public STObject, public CountedObject<STValidation>
|
||||||
: public STObject
|
|
||||||
, public CountedObject <STValidation>
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static char const* getCountedObjectName () { return "STValidation"; }
|
static char const*
|
||||||
|
getCountedObjectName()
|
||||||
|
{
|
||||||
|
return "STValidation";
|
||||||
|
}
|
||||||
|
|
||||||
using pointer = std::shared_ptr<STValidation>;
|
using pointer = std::shared_ptr<STValidation>;
|
||||||
using ref = const std::shared_ptr<STValidation>&;
|
using ref = const std::shared_ptr<STValidation>&;
|
||||||
|
|
||||||
enum
|
enum { kFullFlag = 0x1 };
|
||||||
|
|
||||||
|
/** Construct a STValidation from a peer.
|
||||||
|
|
||||||
|
Construct a STValidation from serialized data previously shared by a
|
||||||
|
peer.
|
||||||
|
|
||||||
|
@param sit Iterator over serialized data
|
||||||
|
@param lookupNodeID Invocable with signature
|
||||||
|
NodeID(PublicKey const&)
|
||||||
|
used to find the Node ID based on the public key
|
||||||
|
that signed the validation. For manifest based
|
||||||
|
validators, this should be the NodeID of the master
|
||||||
|
public key.
|
||||||
|
@param checkSignature Whether to verify the data was signed properly
|
||||||
|
|
||||||
|
@note Throws if the object is not valid
|
||||||
|
*/
|
||||||
|
template <class LookupNodeID>
|
||||||
|
STValidation(
|
||||||
|
SerialIter& sit,
|
||||||
|
LookupNodeID&& lookupNodeID,
|
||||||
|
bool checkSignature)
|
||||||
|
: STObject(getFormat(), sit, sfValidation)
|
||||||
{
|
{
|
||||||
kFullFlag = 0x1
|
mNodeID =
|
||||||
};
|
lookupNodeID(PublicKey(makeSlice(getFieldVL(sfSigningPubKey))));
|
||||||
|
assert(mNodeID.isNonZero());
|
||||||
|
|
||||||
// These throw if the object is not valid
|
if (checkSignature && !isValid())
|
||||||
STValidation (SerialIter & sit, bool checkSignature = true);
|
{
|
||||||
|
JLOG(debugLog().error()) << "Invalid validation" << getJson(0);
|
||||||
|
Throw<std::runtime_error>("Invalid validation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Does not sign the validation
|
/** Construct a new STValidation
|
||||||
STValidation (
|
|
||||||
|
Constructs a new STValidation issued by a node. The instance should be
|
||||||
|
signed before sharing with other nodes.
|
||||||
|
|
||||||
|
@param ledgerHash The hash of the validated ledger
|
||||||
|
@param signTime When the validation is signed
|
||||||
|
@param publicKey The current signing public key
|
||||||
|
@param nodeID ID corresponding to node's public master key
|
||||||
|
@param isFull Whether the validation is full or partial
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
STValidation(
|
||||||
uint256 const& ledgerHash,
|
uint256 const& ledgerHash,
|
||||||
NetClock::time_point signTime,
|
NetClock::time_point signTime,
|
||||||
PublicKey const& raPub,
|
PublicKey const& publicKey,
|
||||||
|
NodeID const& nodeID,
|
||||||
bool isFull);
|
bool isFull);
|
||||||
|
|
||||||
STBase*
|
STBase*
|
||||||
copy (std::size_t n, void* buf) const override
|
copy(std::size_t n, void* buf) const override
|
||||||
{
|
{
|
||||||
return emplace(n, buf, *this);
|
return emplace(n, buf, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
STBase*
|
STBase*
|
||||||
move (std::size_t n, void* buf) override
|
move(std::size_t n, void* buf) override
|
||||||
{
|
{
|
||||||
return emplace(n, buf, std::move(*this));
|
return emplace(n, buf, std::move(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 getLedgerHash () const;
|
uint256
|
||||||
NetClock::time_point getSignTime () const;
|
getLedgerHash() const;
|
||||||
NetClock::time_point getSeenTime () const;
|
|
||||||
std::uint32_t getFlags () const;
|
NetClock::time_point
|
||||||
PublicKey getSignerPublic () const;
|
getSignTime() const;
|
||||||
NodeID getNodeID () const
|
|
||||||
|
NetClock::time_point
|
||||||
|
getSeenTime() const;
|
||||||
|
|
||||||
|
std::uint32_t
|
||||||
|
getFlags() const;
|
||||||
|
|
||||||
|
PublicKey
|
||||||
|
getSignerPublic() const;
|
||||||
|
|
||||||
|
NodeID
|
||||||
|
getNodeID() const
|
||||||
{
|
{
|
||||||
return mNodeID;
|
return mNodeID;
|
||||||
}
|
}
|
||||||
bool isValid () const;
|
|
||||||
bool isFull () const;
|
bool
|
||||||
bool isTrusted () const
|
isValid() const;
|
||||||
|
|
||||||
|
bool
|
||||||
|
isFull() const;
|
||||||
|
|
||||||
|
bool
|
||||||
|
isTrusted() const
|
||||||
{
|
{
|
||||||
return mTrusted;
|
return mTrusted;
|
||||||
}
|
}
|
||||||
uint256 getSigningHash () const;
|
|
||||||
bool isValid (uint256 const& ) const;
|
|
||||||
|
|
||||||
void setTrusted ()
|
uint256
|
||||||
|
getSigningHash() const;
|
||||||
|
|
||||||
|
bool
|
||||||
|
isValid(uint256 const&) const;
|
||||||
|
|
||||||
|
void
|
||||||
|
setTrusted()
|
||||||
{
|
{
|
||||||
mTrusted = true;
|
mTrusted = true;
|
||||||
}
|
}
|
||||||
void setSeen (NetClock::time_point s)
|
|
||||||
|
void
|
||||||
|
setUntrusted()
|
||||||
|
{
|
||||||
|
mTrusted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setSeen(NetClock::time_point s)
|
||||||
{
|
{
|
||||||
mSeen = s;
|
mSeen = s;
|
||||||
}
|
}
|
||||||
Blob getSerialized () const;
|
|
||||||
Blob getSignature () const;
|
Blob
|
||||||
|
getSerialized() const;
|
||||||
|
|
||||||
|
Blob
|
||||||
|
getSignature() const;
|
||||||
|
|
||||||
// Signs the validation and returns the signing hash
|
// Signs the validation and returns the signing hash
|
||||||
uint256 sign (SecretKey const& secretKey);
|
uint256
|
||||||
|
sign(SecretKey const& secretKey);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static SOTemplate const& getFormat ();
|
static SOTemplate const&
|
||||||
|
getFormat();
|
||||||
void setNode ();
|
|
||||||
|
|
||||||
NodeID mNodeID;
|
NodeID mNodeID;
|
||||||
bool mTrusted = false;
|
bool mTrusted = false;
|
||||||
|
|||||||
@@ -26,35 +26,19 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
STValidation::STValidation (SerialIter& sit, bool checkSignature)
|
STValidation::STValidation(
|
||||||
: STObject (getFormat (), sit, sfValidation)
|
uint256 const& ledgerHash,
|
||||||
{
|
NetClock::time_point signTime,
|
||||||
mNodeID = calcNodeID(
|
PublicKey const& publicKey,
|
||||||
PublicKey(makeSlice (getFieldVL (sfSigningPubKey))));
|
NodeID const& nodeID,
|
||||||
assert (mNodeID.isNonZero ());
|
bool isFull)
|
||||||
|
: STObject(getFormat(), sfValidation), mNodeID(nodeID), mSeen(signTime)
|
||||||
if (checkSignature && !isValid ())
|
|
||||||
{
|
|
||||||
JLOG (debugLog().error())
|
|
||||||
<< "Invalid validation" << getJson (0);
|
|
||||||
Throw<std::runtime_error> ("Invalid validation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
STValidation::STValidation (
|
|
||||||
uint256 const& ledgerHash,
|
|
||||||
NetClock::time_point signTime,
|
|
||||||
PublicKey const& publicKey,
|
|
||||||
bool isFull)
|
|
||||||
: STObject (getFormat (), sfValidation)
|
|
||||||
, mSeen (signTime)
|
|
||||||
{
|
{
|
||||||
// Does not sign
|
// Does not sign
|
||||||
setFieldH256 (sfLedgerHash, ledgerHash);
|
setFieldH256 (sfLedgerHash, ledgerHash);
|
||||||
setFieldU32 (sfSigningTime, signTime.time_since_epoch().count());
|
setFieldU32 (sfSigningTime, signTime.time_since_epoch().count());
|
||||||
|
|
||||||
setFieldVL (sfSigningPubKey, publicKey.slice());
|
setFieldVL (sfSigningPubKey, publicKey.slice());
|
||||||
mNodeID = calcNodeID(publicKey);
|
|
||||||
assert (mNodeID.isNonZero ());
|
assert (mNodeID.isNonZero ());
|
||||||
|
|
||||||
if (isFull)
|
if (isFull)
|
||||||
|
|||||||
@@ -379,7 +379,7 @@ public:
|
|||||||
for (auto const& val : validators)
|
for (auto const& val : validators)
|
||||||
{
|
{
|
||||||
auto v = std::make_shared <STValidation> (
|
auto v = std::make_shared <STValidation> (
|
||||||
uint256(), roundTime, val, true);
|
uint256(), roundTime, val, calcNodeID(val), true);
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
STVector256 field (sfAmendments);
|
STVector256 field (sfAmendments);
|
||||||
|
|||||||
@@ -31,15 +31,38 @@ namespace test {
|
|||||||
class RCLValidations_test : public beast::unit_test::suite
|
class RCLValidations_test : public beast::unit_test::suite
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
|
||||||
void
|
void
|
||||||
run() override
|
testChangeTrusted()
|
||||||
{
|
{
|
||||||
|
testcase("Change validation trusted status");
|
||||||
|
PublicKey key = derivePublicKey(KeyType::ed25519, randomSecretKey());
|
||||||
|
auto v = std::make_shared<STValidation>(
|
||||||
|
uint256(), NetClock::time_point(), key, calcNodeID(key), true);
|
||||||
|
|
||||||
|
BEAST_EXPECT(!v->isTrusted());
|
||||||
|
v->setTrusted();
|
||||||
|
BEAST_EXPECT(v->isTrusted());
|
||||||
|
v->setUntrusted();
|
||||||
|
BEAST_EXPECT(!v->isTrusted());
|
||||||
|
|
||||||
|
RCLValidation rcv{v};
|
||||||
|
BEAST_EXPECT(!rcv.trusted());
|
||||||
|
rcv.setTrusted();
|
||||||
|
BEAST_EXPECT(rcv.trusted());
|
||||||
|
rcv.setUntrusted();
|
||||||
|
BEAST_EXPECT(!rcv.trusted());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testRCLValidatedLedger()
|
||||||
|
{
|
||||||
|
testcase("RCLValidatedLedger ancestry");
|
||||||
beast::Journal j;
|
beast::Journal j;
|
||||||
|
|
||||||
using Seq = RCLValidatedLedger::Seq;
|
using Seq = RCLValidatedLedger::Seq;
|
||||||
using ID = RCLValidatedLedger::ID;
|
using ID = RCLValidatedLedger::ID;
|
||||||
|
|
||||||
|
|
||||||
// This tests RCLValidatedLedger properly implements the type
|
// This tests RCLValidatedLedger properly implements the type
|
||||||
// requirements of a LedgerTrie ledger, with its added behavior that
|
// requirements of a LedgerTrie ledger, with its added behavior that
|
||||||
// only the 256 prior ledger hashes are available to determine ancestry.
|
// only the 256 prior ledger hashes are available to determine ancestry.
|
||||||
@@ -193,8 +216,14 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
testChangeTrusted();
|
||||||
|
testRCLValidatedLedger();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,11 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <ripple/app/misc/ValidatorKeys.h>
|
#include <ripple/app/misc/ValidatorKeys.h>
|
||||||
|
#include <ripple/app/misc/Manifest.h>
|
||||||
#include <ripple/beast/unit_test.h>
|
#include <ripple/beast/unit_test.h>
|
||||||
#include <ripple/core/Config.h>
|
#include <ripple/core/Config.h>
|
||||||
#include <ripple/core/ConfigSections.h>
|
#include <ripple/core/ConfigSections.h>
|
||||||
|
#include <beast/core/detail/base64.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
@@ -74,19 +76,24 @@ public:
|
|||||||
{
|
{
|
||||||
beast::Journal j;
|
beast::Journal j;
|
||||||
|
|
||||||
// Keys when using [validation_seed]
|
// Keys/ID when using [validation_seed]
|
||||||
auto const seedSecretKey =
|
SecretKey const seedSecretKey =
|
||||||
generateSecretKey(KeyType::secp256k1, *parseBase58<Seed>(seed));
|
generateSecretKey(KeyType::secp256k1, *parseBase58<Seed>(seed));
|
||||||
auto const seedPublicKey =
|
PublicKey const seedPublicKey =
|
||||||
derivePublicKey(KeyType::secp256k1, seedSecretKey);
|
derivePublicKey(KeyType::secp256k1, seedSecretKey);
|
||||||
|
NodeID const seedNodeID = calcNodeID(seedPublicKey);
|
||||||
|
|
||||||
// Keys when using [validation_token]
|
// Keys/ID when using [validation_token]
|
||||||
auto const tokenSecretKey = *parseBase58<SecretKey>(
|
SecretKey const tokenSecretKey = *parseBase58<SecretKey>(
|
||||||
TokenType::TOKEN_NODE_PRIVATE, tokenSecretStr);
|
TokenType::TOKEN_NODE_PRIVATE, tokenSecretStr);
|
||||||
|
PublicKey const tokenPublicKey =
|
||||||
auto const tokenPublicKey =
|
|
||||||
derivePublicKey(KeyType::secp256k1, tokenSecretKey);
|
derivePublicKey(KeyType::secp256k1, tokenSecretKey);
|
||||||
|
|
||||||
|
auto const m = Manifest::make_Manifest(
|
||||||
|
beast::detail::base64_decode(tokenManifest));
|
||||||
|
BEAST_EXPECT(m);
|
||||||
|
NodeID const tokenNodeID = calcNodeID(m->masterKey);
|
||||||
|
|
||||||
{
|
{
|
||||||
// No config -> no key but valid
|
// No config -> no key but valid
|
||||||
Config c;
|
Config c;
|
||||||
@@ -104,6 +111,7 @@ public:
|
|||||||
ValidatorKeys k{c, j};
|
ValidatorKeys k{c, j};
|
||||||
BEAST_EXPECT(k.publicKey == seedPublicKey);
|
BEAST_EXPECT(k.publicKey == seedPublicKey);
|
||||||
BEAST_EXPECT(k.secretKey == seedSecretKey);
|
BEAST_EXPECT(k.secretKey == seedSecretKey);
|
||||||
|
BEAST_EXPECT(k.nodeID == seedNodeID);
|
||||||
BEAST_EXPECT(k.manifest.empty());
|
BEAST_EXPECT(k.manifest.empty());
|
||||||
BEAST_EXPECT(!k.configInvalid());
|
BEAST_EXPECT(!k.configInvalid());
|
||||||
}
|
}
|
||||||
@@ -127,6 +135,7 @@ public:
|
|||||||
|
|
||||||
BEAST_EXPECT(k.publicKey == tokenPublicKey);
|
BEAST_EXPECT(k.publicKey == tokenPublicKey);
|
||||||
BEAST_EXPECT(k.secretKey == tokenSecretKey);
|
BEAST_EXPECT(k.secretKey == tokenSecretKey);
|
||||||
|
BEAST_EXPECT(k.nodeID == tokenNodeID);
|
||||||
BEAST_EXPECT(k.manifest == tokenManifest);
|
BEAST_EXPECT(k.manifest == tokenManifest);
|
||||||
BEAST_EXPECT(!k.configInvalid());
|
BEAST_EXPECT(!k.configInvalid());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,16 @@ private:
|
|||||||
keys.first, keys.second, makeSlice(data)));
|
keys.first, keys.second, makeSlice(data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static hash_set<NodeID>
|
||||||
|
asNodeIDs(std::initializer_list<PublicKey> const& pks)
|
||||||
|
{
|
||||||
|
hash_set<NodeID> res;
|
||||||
|
res.reserve(pks.size());
|
||||||
|
for (auto const& pk : pks)
|
||||||
|
res.insert(calcNodeID(pk));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testGenesisQuorum ()
|
testGenesisQuorum ()
|
||||||
{
|
{
|
||||||
@@ -509,9 +519,9 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testUpdate ()
|
testUpdateTrusted ()
|
||||||
{
|
{
|
||||||
testcase ("Update");
|
testcase ("Update trusted");
|
||||||
|
|
||||||
PublicKey emptyLocalKey;
|
PublicKey emptyLocalKey;
|
||||||
ManifestCache manifests;
|
ManifestCache manifests;
|
||||||
@@ -520,7 +530,8 @@ private:
|
|||||||
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
||||||
|
|
||||||
std::vector<std::string> cfgPublishers;
|
std::vector<std::string> cfgPublishers;
|
||||||
hash_set<PublicKey> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
|
hash_set<NodeID> secondAddedValidators;
|
||||||
|
|
||||||
// BFT: n >= 3f+1
|
// BFT: n >= 3f+1
|
||||||
std::size_t const n = 40;
|
std::size_t const n = 40;
|
||||||
@@ -535,15 +546,18 @@ private:
|
|||||||
cfgKeys.push_back (toBase58(
|
cfgKeys.push_back (toBase58(
|
||||||
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
||||||
if (cfgKeys.size () <= n - 5)
|
if (cfgKeys.size () <= n - 5)
|
||||||
activeValidators.emplace (valKey);
|
activeValidators.emplace (calcNodeID(valKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
emptyLocalKey, cfgKeys, cfgPublishers));
|
emptyLocalKey, cfgKeys, cfgPublishers));
|
||||||
|
|
||||||
// onConsensusStart should make all available configured
|
// updateTrusted should make all available configured
|
||||||
// validators trusted
|
// validators trusted
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.added == activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
// Add 1 to n because I'm not on a published list.
|
// Add 1 to n because I'm not on a published list.
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == n + 1 - f);
|
BEAST_EXPECT(trustedKeys->quorum () == n + 1 - f);
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
@@ -564,11 +578,19 @@ private:
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Quorum should be 80% with all listed validators active
|
// Quorum should be 80% with all listed validators active
|
||||||
hash_set<PublicKey> activeValidators;
|
hash_set<NodeID> activeValidatorsNew{activeValidators};
|
||||||
for (auto const valKey : cfgKeys)
|
for (auto const valKey : cfgKeys)
|
||||||
activeValidators.emplace (*parseBase58<PublicKey>(
|
{
|
||||||
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
auto const ins = activeValidatorsNew.emplace(
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
calcNodeID(*parseBase58<PublicKey>(
|
||||||
|
TokenType::TOKEN_NODE_PUBLIC, valKey)));
|
||||||
|
if(ins.second)
|
||||||
|
secondAddedValidators.insert(*ins.first);
|
||||||
|
}
|
||||||
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidatorsNew);
|
||||||
|
BEAST_EXPECT(changes.added == secondAddedValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == cfgKeys.size() * 4/5);
|
BEAST_EXPECT(trustedKeys->quorum () == cfgKeys.size() * 4/5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -586,10 +608,13 @@ private:
|
|||||||
|
|
||||||
auto const signingKeys1 = randomKeyPair(KeyType::secp256k1);
|
auto const signingKeys1 = randomKeyPair(KeyType::secp256k1);
|
||||||
auto const signingPublic1 = signingKeys1.first;
|
auto const signingPublic1 = signingKeys1.first;
|
||||||
activeValidators.emplace (masterPublic);
|
activeValidators.emplace (calcNodeID(masterPublic));
|
||||||
|
|
||||||
// Should not trust ephemeral signing key if there is no manifest
|
// Should not trust ephemeral signing key if there is no manifest
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.added == asNodeIDs({masterPublic}));
|
||||||
|
BEAST_EXPECT(changes.removed == secondAddedValidators);
|
||||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||||
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
||||||
BEAST_EXPECT(!trustedKeys->listed (signingPublic1));
|
BEAST_EXPECT(!trustedKeys->listed (signingPublic1));
|
||||||
@@ -603,7 +628,9 @@ private:
|
|||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
manifests.applyManifest(std::move (*m1)) ==
|
manifests.applyManifest(std::move (*m1)) ==
|
||||||
ManifestDisposition::accepted);
|
ManifestDisposition::accepted);
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
changes = trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
BEAST_EXPECT(changes.added.empty());
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == n + 2 - f);
|
BEAST_EXPECT(trustedKeys->quorum () == n + 2 - f);
|
||||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||||
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
||||||
@@ -620,7 +647,9 @@ private:
|
|||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
manifests.applyManifest(std::move (*m2)) ==
|
manifests.applyManifest(std::move (*m2)) ==
|
||||||
ManifestDisposition::accepted);
|
ManifestDisposition::accepted);
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
changes = trustedKeys->updateTrusted (activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
BEAST_EXPECT(changes.added.empty());
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == n + 2 - f);
|
BEAST_EXPECT(trustedKeys->quorum () == n + 2 - f);
|
||||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||||
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
BEAST_EXPECT(trustedKeys->trusted (masterPublic));
|
||||||
@@ -632,7 +661,7 @@ private:
|
|||||||
// Should not trust keys from revoked master public key
|
// Should not trust keys from revoked master public key
|
||||||
auto const signingKeysMax = randomKeyPair(KeyType::secp256k1);
|
auto const signingKeysMax = randomKeyPair(KeyType::secp256k1);
|
||||||
auto const signingPublicMax = signingKeysMax.first;
|
auto const signingPublicMax = signingKeysMax.first;
|
||||||
activeValidators.emplace (signingPublicMax);
|
activeValidators.emplace (calcNodeID(signingPublicMax));
|
||||||
auto mMax = Manifest::make_Manifest (makeManifestString (
|
auto mMax = Manifest::make_Manifest (makeManifestString (
|
||||||
masterPublic, masterPrivate,
|
masterPublic, masterPrivate,
|
||||||
signingPublicMax, signingKeysMax.second,
|
signingPublicMax, signingKeysMax.second,
|
||||||
@@ -644,7 +673,9 @@ private:
|
|||||||
ManifestDisposition::accepted);
|
ManifestDisposition::accepted);
|
||||||
BEAST_EXPECT(manifests.getSigningKey (masterPublic) == masterPublic);
|
BEAST_EXPECT(manifests.getSigningKey (masterPublic) == masterPublic);
|
||||||
BEAST_EXPECT(manifests.revoked (masterPublic));
|
BEAST_EXPECT(manifests.revoked (masterPublic));
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
changes = trustedKeys->updateTrusted (activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed == asNodeIDs({masterPublic}));
|
||||||
|
BEAST_EXPECT(changes.added.empty());
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == n + 1 - f);
|
BEAST_EXPECT(trustedKeys->quorum () == n + 1 - f);
|
||||||
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
BEAST_EXPECT(trustedKeys->listed (masterPublic));
|
||||||
BEAST_EXPECT(!trustedKeys->trusted (masterPublic));
|
BEAST_EXPECT(!trustedKeys->trusted (masterPublic));
|
||||||
@@ -670,7 +701,10 @@ private:
|
|||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
emptyLocalKey, emptyCfgKeys, cfgPublishers));
|
emptyLocalKey, emptyCfgKeys, cfgPublishers));
|
||||||
|
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
BEAST_EXPECT(changes.added.empty());
|
||||||
BEAST_EXPECT(trustedKeys->quorum () ==
|
BEAST_EXPECT(trustedKeys->quorum () ==
|
||||||
std::numeric_limits<std::size_t>::max());
|
std::numeric_limits<std::size_t>::max());
|
||||||
}
|
}
|
||||||
@@ -680,7 +714,7 @@ private:
|
|||||||
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
||||||
|
|
||||||
std::vector<PublicKey> keys ({ randomNode (), randomNode () });
|
std::vector<PublicKey> keys ({ randomNode (), randomNode () });
|
||||||
hash_set<PublicKey> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
std::vector<std::string> cfgKeys ({
|
std::vector<std::string> cfgKeys ({
|
||||||
toBase58 (TokenType::TOKEN_NODE_PUBLIC, keys[0]),
|
toBase58 (TokenType::TOKEN_NODE_PUBLIC, keys[0]),
|
||||||
toBase58 (TokenType::TOKEN_NODE_PUBLIC, keys[1])});
|
toBase58 (TokenType::TOKEN_NODE_PUBLIC, keys[1])});
|
||||||
@@ -688,7 +722,10 @@ private:
|
|||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
emptyLocalKey, cfgKeys, cfgPublishers));
|
emptyLocalKey, cfgKeys, cfgPublishers));
|
||||||
|
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
BEAST_EXPECT(changes.added == asNodeIDs({keys[0], keys[1]}));
|
||||||
|
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
||||||
for (auto const& key : keys)
|
for (auto const& key : keys)
|
||||||
@@ -704,16 +741,21 @@ private:
|
|||||||
auto const node = randomNode ();
|
auto const node = randomNode ();
|
||||||
std::vector<std::string> cfgKeys ({
|
std::vector<std::string> cfgKeys ({
|
||||||
toBase58 (TokenType::TOKEN_NODE_PUBLIC, node)});
|
toBase58 (TokenType::TOKEN_NODE_PUBLIC, node)});
|
||||||
hash_set<PublicKey> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
|
|
||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
emptyLocalKey, cfgKeys, cfgPublishers));
|
emptyLocalKey, cfgKeys, cfgPublishers));
|
||||||
|
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
BEAST_EXPECT(changes.added == asNodeIDs({node}));
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == minQuorum);
|
BEAST_EXPECT(trustedKeys->quorum () == minQuorum);
|
||||||
|
|
||||||
activeValidators.emplace (node);
|
activeValidators.emplace (calcNodeID(node));
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
changes = trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
BEAST_EXPECT(changes.added.empty());
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 1);
|
BEAST_EXPECT(trustedKeys->quorum () == 1);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -722,7 +764,7 @@ private:
|
|||||||
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
||||||
|
|
||||||
std::vector<PublicKey> keys ({ randomNode (), randomNode () });
|
std::vector<PublicKey> keys ({ randomNode (), randomNode () });
|
||||||
hash_set<PublicKey> activeValidators ({ keys[0] });
|
hash_set<NodeID> activeValidators (asNodeIDs({ keys[0] }));
|
||||||
std::vector<std::string> cfgKeys ({
|
std::vector<std::string> cfgKeys ({
|
||||||
toBase58 (TokenType::TOKEN_NODE_PUBLIC, keys[0]),
|
toBase58 (TokenType::TOKEN_NODE_PUBLIC, keys[0]),
|
||||||
toBase58 (TokenType::TOKEN_NODE_PUBLIC, keys[1])});
|
toBase58 (TokenType::TOKEN_NODE_PUBLIC, keys[1])});
|
||||||
@@ -731,7 +773,11 @@ private:
|
|||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
localKey, cfgKeys, cfgPublishers));
|
localKey, cfgKeys, cfgPublishers));
|
||||||
|
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
BEAST_EXPECT(
|
||||||
|
changes.added == asNodeIDs({keys[0], keys[1], localKey}));
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
||||||
|
|
||||||
// local validator key is always trusted
|
// local validator key is always trusted
|
||||||
@@ -758,7 +804,8 @@ private:
|
|||||||
emptyLocalKey, emptyCfgKeys, cfgKeys));
|
emptyLocalKey, emptyCfgKeys, cfgKeys));
|
||||||
|
|
||||||
std::vector<Validator> list ({randomValidator(), randomValidator()});
|
std::vector<Validator> list ({randomValidator(), randomValidator()});
|
||||||
hash_set<PublicKey> activeValidators ({ list[0].masterPublic, list[1].masterPublic });
|
hash_set<NodeID> activeValidators(
|
||||||
|
asNodeIDs({list[0].masterPublic, list[1].masterPublic}));
|
||||||
|
|
||||||
// do not apply expired list
|
// do not apply expired list
|
||||||
auto const version = 1;
|
auto const version = 1;
|
||||||
@@ -773,16 +820,21 @@ private:
|
|||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest, blob, sig, version));
|
manifest, blob, sig, version));
|
||||||
|
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
for(Validator const & val : list)
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
{
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
BEAST_EXPECT(trustedKeys->trusted (val.masterPublic));
|
BEAST_EXPECT(changes.added == activeValidators);
|
||||||
BEAST_EXPECT(trustedKeys->trusted (val.signingPublic));
|
for(Validator const & val : list)
|
||||||
}
|
{
|
||||||
|
BEAST_EXPECT(trustedKeys->trusted (val.masterPublic));
|
||||||
|
BEAST_EXPECT(trustedKeys->trusted (val.signingPublic));
|
||||||
|
}
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
||||||
|
|
||||||
env.timeKeeper().set(expiration);
|
env.timeKeeper().set(expiration);
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
changes = trustedKeys->updateTrusted (activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed == activeValidators);
|
||||||
|
BEAST_EXPECT(changes.added.empty());
|
||||||
BEAST_EXPECT(! trustedKeys->trusted (list[0].masterPublic));
|
BEAST_EXPECT(! trustedKeys->trusted (list[0].masterPublic));
|
||||||
BEAST_EXPECT(! trustedKeys->trusted (list[1].masterPublic));
|
BEAST_EXPECT(! trustedKeys->trusted (list[1].masterPublic));
|
||||||
BEAST_EXPECT(trustedKeys->quorum () ==
|
BEAST_EXPECT(trustedKeys->quorum () ==
|
||||||
@@ -790,7 +842,7 @@ private:
|
|||||||
|
|
||||||
// (Re)trust validators from new valid list
|
// (Re)trust validators from new valid list
|
||||||
std::vector<Validator> list2 ({list[0], randomValidator()});
|
std::vector<Validator> list2 ({list[0], randomValidator()});
|
||||||
activeValidators.insert(list2[1].masterPublic);
|
activeValidators.insert(calcNodeID(list2[1].masterPublic));
|
||||||
auto const sequence2 = 2;
|
auto const sequence2 = 2;
|
||||||
NetClock::time_point const expiration2 =
|
NetClock::time_point const expiration2 =
|
||||||
env.timeKeeper().now() + 60s;
|
env.timeKeeper().now() + 60s;
|
||||||
@@ -802,14 +854,18 @@ private:
|
|||||||
trustedKeys->applyList (
|
trustedKeys->applyList (
|
||||||
manifest, blob2, sig2, version));
|
manifest, blob2, sig2, version));
|
||||||
|
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
changes = trustedKeys->updateTrusted (activeValidators);
|
||||||
for(Validator const & val : list2)
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
{
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(trustedKeys->trusted (val.masterPublic));
|
changes.added ==
|
||||||
BEAST_EXPECT(trustedKeys->trusted (val.signingPublic));
|
asNodeIDs({list2[0].masterPublic, list2[1].masterPublic}));
|
||||||
}
|
for(Validator const & val : list2)
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(trustedKeys->trusted (val.masterPublic));
|
||||||
|
BEAST_EXPECT(trustedKeys->trusted (val.signingPublic));
|
||||||
|
}
|
||||||
BEAST_EXPECT(! trustedKeys->trusted (list[1].masterPublic));
|
BEAST_EXPECT(! trustedKeys->trusted (list[1].masterPublic));
|
||||||
BEAST_EXPECT(! trustedKeys->trusted (list[1].signingPublic));
|
BEAST_EXPECT(! trustedKeys->trusted (list[1].signingPublic));
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
BEAST_EXPECT(trustedKeys->quorum () == 2);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -818,7 +874,8 @@ private:
|
|||||||
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
||||||
|
|
||||||
std::vector<std::string> cfgPublishers;
|
std::vector<std::string> cfgPublishers;
|
||||||
hash_set<PublicKey> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
|
hash_set<PublicKey> activeKeys;
|
||||||
|
|
||||||
std::vector<std::string> cfgKeys;
|
std::vector<std::string> cfgKeys;
|
||||||
cfgKeys.reserve(9);
|
cfgKeys.reserve(9);
|
||||||
@@ -828,15 +885,19 @@ private:
|
|||||||
auto const valKey = randomNode();
|
auto const valKey = randomNode();
|
||||||
cfgKeys.push_back (toBase58(
|
cfgKeys.push_back (toBase58(
|
||||||
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
||||||
activeValidators.emplace (valKey);
|
activeValidators.emplace (calcNodeID(valKey));
|
||||||
|
activeKeys.emplace(valKey);
|
||||||
|
|
||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
emptyLocalKey, cfgKeys, cfgPublishers));
|
emptyLocalKey, cfgKeys, cfgPublishers));
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
BEAST_EXPECT(changes.added == asNodeIDs({valKey}));
|
||||||
BEAST_EXPECT(trustedKeys->quorum () ==
|
BEAST_EXPECT(trustedKeys->quorum () ==
|
||||||
((cfgKeys.size() <= 6) ? cfgKeys.size()/2 + 1 :
|
((cfgKeys.size() <= 6) ? cfgKeys.size()/2 + 1 :
|
||||||
cfgKeys.size() * 2/3 + 1));
|
cfgKeys.size() * 2/3 + 1));
|
||||||
for (auto const& key : activeValidators)
|
for (auto const& key : activeKeys)
|
||||||
BEAST_EXPECT(trustedKeys->trusted (key));
|
BEAST_EXPECT(trustedKeys->trusted (key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -847,8 +908,8 @@ private:
|
|||||||
|
|
||||||
auto const localKey = randomNode();
|
auto const localKey = randomNode();
|
||||||
std::vector<std::string> cfgPublishers;
|
std::vector<std::string> cfgPublishers;
|
||||||
hash_set<PublicKey> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
|
hash_set<PublicKey> activeKeys;
|
||||||
std::vector<std::string> cfgKeys {
|
std::vector<std::string> cfgKeys {
|
||||||
toBase58(TokenType::TOKEN_NODE_PUBLIC, localKey)};
|
toBase58(TokenType::TOKEN_NODE_PUBLIC, localKey)};
|
||||||
cfgKeys.reserve(9);
|
cfgKeys.reserve(9);
|
||||||
@@ -858,17 +919,25 @@ private:
|
|||||||
auto const valKey = randomNode();
|
auto const valKey = randomNode();
|
||||||
cfgKeys.push_back (toBase58(
|
cfgKeys.push_back (toBase58(
|
||||||
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
TokenType::TOKEN_NODE_PUBLIC, valKey));
|
||||||
activeValidators.emplace (valKey);
|
activeValidators.emplace (calcNodeID(valKey));
|
||||||
|
activeKeys.emplace(valKey);
|
||||||
|
|
||||||
BEAST_EXPECT(trustedKeys->load (
|
BEAST_EXPECT(trustedKeys->load (
|
||||||
localKey, cfgKeys, cfgPublishers));
|
localKey, cfgKeys, cfgPublishers));
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
if (cfgKeys.size() > 2)
|
||||||
|
BEAST_EXPECT(changes.added == asNodeIDs({valKey}));
|
||||||
|
else
|
||||||
|
BEAST_EXPECT(
|
||||||
|
changes.added == asNodeIDs({localKey, valKey}));
|
||||||
|
|
||||||
BEAST_EXPECT(trustedKeys->quorum () ==
|
BEAST_EXPECT(trustedKeys->quorum () ==
|
||||||
((cfgKeys.size() <= 6) ? cfgKeys.size()/2 + 1 :
|
((cfgKeys.size() <= 6) ? cfgKeys.size()/2 + 1 :
|
||||||
(cfgKeys.size() + 1) * 2/3 + 1));
|
(cfgKeys.size() + 1) * 2/3 + 1));
|
||||||
|
|
||||||
for (auto const& key : activeValidators)
|
for (auto const& key : activeKeys)
|
||||||
BEAST_EXPECT(trustedKeys->trusted (key));
|
BEAST_EXPECT(trustedKeys->trusted (key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -878,15 +947,15 @@ private:
|
|||||||
auto trustedKeys = std::make_unique <ValidatorList> (
|
auto trustedKeys = std::make_unique <ValidatorList> (
|
||||||
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
manifests, manifests, env.timeKeeper(), beast::Journal ());
|
||||||
|
|
||||||
hash_set<PublicKey> activeValidators;
|
hash_set<NodeID> activeValidators;
|
||||||
|
|
||||||
std::vector<Validator> valKeys;
|
std::vector<Validator> valKeys;
|
||||||
valKeys.reserve(n);
|
valKeys.reserve(n);
|
||||||
|
|
||||||
while (valKeys.size () != n)
|
while (valKeys.size () != n)
|
||||||
{
|
{
|
||||||
valKeys.push_back (randomValidator());
|
valKeys.push_back (randomValidator());
|
||||||
activeValidators.emplace (valKeys.back().masterPublic);
|
activeValidators.emplace(
|
||||||
|
calcNodeID(valKeys.back().masterPublic));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addPublishedList = [this, &env, &trustedKeys, &valKeys]()
|
auto addPublishedList = [this, &env, &trustedKeys, &valKeys]()
|
||||||
@@ -923,17 +992,24 @@ private:
|
|||||||
for (auto i = 0; i < 3; ++i)
|
for (auto i = 0; i < 3; ++i)
|
||||||
addPublishedList();
|
addPublishedList();
|
||||||
|
|
||||||
trustedKeys->onConsensusStart (activeValidators);
|
TrustChanges changes =
|
||||||
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
|
|
||||||
// Minimum quorum should be used
|
// Minimum quorum should be used
|
||||||
BEAST_EXPECT(trustedKeys->quorum () == (valKeys.size() * 2/3 + 1));
|
BEAST_EXPECT(trustedKeys->quorum () == (valKeys.size() * 2/3 + 1));
|
||||||
|
|
||||||
|
hash_set<NodeID> added;
|
||||||
std::size_t nTrusted = 0;
|
std::size_t nTrusted = 0;
|
||||||
for (auto const& key : activeValidators)
|
for (auto const& val : valKeys)
|
||||||
{
|
{
|
||||||
if (trustedKeys->trusted (key))
|
if (trustedKeys->trusted (val.masterPublic))
|
||||||
|
{
|
||||||
|
added.insert(calcNodeID(val.masterPublic));
|
||||||
++nTrusted;
|
++nTrusted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BEAST_EXPECT(changes.added == added);
|
||||||
|
BEAST_EXPECT(changes.removed.empty());
|
||||||
|
|
||||||
// The number of trusted keys should be 125% of the minimum quorum
|
// The number of trusted keys should be 125% of the minimum quorum
|
||||||
BEAST_EXPECT(nTrusted ==
|
BEAST_EXPECT(nTrusted ==
|
||||||
@@ -979,9 +1055,9 @@ private:
|
|||||||
manifests, manifests, env.app().timeKeeper(), journal);
|
manifests, manifests, env.app().timeKeeper(), journal);
|
||||||
|
|
||||||
std::vector<Validator> validators = {randomValidator()};
|
std::vector<Validator> validators = {randomValidator()};
|
||||||
hash_set<PublicKey> activeKeys;
|
hash_set<NodeID> activeValidators;
|
||||||
for(Validator const & val : validators)
|
for(Validator const & val : validators)
|
||||||
activeKeys.insert(val.masterPublic);
|
activeValidators.insert(calcNodeID(val.masterPublic));
|
||||||
// Store prepared list data to control when it is applied
|
// Store prepared list data to control when it is applied
|
||||||
struct PreparedList
|
struct PreparedList
|
||||||
{
|
{
|
||||||
@@ -1053,7 +1129,7 @@ private:
|
|||||||
// Advance past the first list's expiration, but it remains the
|
// Advance past the first list's expiration, but it remains the
|
||||||
// earliest expiration
|
// earliest expiration
|
||||||
env.timeKeeper().set(prep1.expiration + 1s);
|
env.timeKeeper().set(prep1.expiration + 1s);
|
||||||
trustedKeys->onConsensusStart(activeKeys);
|
trustedKeys->updateTrusted(activeValidators);
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
trustedKeys->expires() &&
|
trustedKeys->expires() &&
|
||||||
trustedKeys->expires().get() == prep1.expiration);
|
trustedKeys->expires().get() == prep1.expiration);
|
||||||
@@ -1066,7 +1142,7 @@ public:
|
|||||||
testGenesisQuorum ();
|
testGenesisQuorum ();
|
||||||
testConfigLoad ();
|
testConfigLoad ();
|
||||||
testApplyList ();
|
testApplyList ();
|
||||||
testUpdate ();
|
testUpdateTrusted ();
|
||||||
testExpires ();
|
testExpires ();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
struct StaleData
|
struct StaleData
|
||||||
{
|
{
|
||||||
std::vector<Validation> stale;
|
std::vector<Validation> stale;
|
||||||
hash_map<PeerKey, Validation> flushed;
|
hash_map<PeerID, Validation> flushed;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic Validations adaptor that saves stale/flushed data into
|
// Generic Validations adaptor that saves stale/flushed data into
|
||||||
@@ -216,7 +216,7 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
flush(hash_map<PeerKey, Validation>&& remaining)
|
flush(hash_map<PeerID, Validation>&& remaining)
|
||||||
{
|
{
|
||||||
staleData_.flushed = std::move(remaining);
|
staleData_.flushed = std::move(remaining);
|
||||||
}
|
}
|
||||||
@@ -250,8 +250,7 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
ValStatus
|
ValStatus
|
||||||
add(Validation const& v)
|
add(Validation const& v)
|
||||||
{
|
{
|
||||||
PeerKey masterKey{v.nodeID(), 0};
|
return tv_.add(v.nodeID(), v);
|
||||||
return tv_.add(masterKey, v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TestValidations&
|
TestValidations&
|
||||||
@@ -284,7 +283,7 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
return staleData_.stale;
|
return staleData_.stale;
|
||||||
}
|
}
|
||||||
|
|
||||||
hash_map<PeerKey, Validation> const&
|
hash_map<PeerID, Validation> const&
|
||||||
flushed() const
|
flushed() const
|
||||||
{
|
{
|
||||||
return staleData_.flushed;
|
return staleData_.flushed;
|
||||||
@@ -462,7 +461,7 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
|
|
||||||
std::vector<Trigger> triggers = {
|
std::vector<Trigger> triggers = {
|
||||||
[&](TestValidations& vals) { vals.currentTrusted(); },
|
[&](TestValidations& vals) { vals.currentTrusted(); },
|
||||||
[&](TestValidations& vals) { vals.getCurrentPublicKeys(); },
|
[&](TestValidations& vals) { vals.getCurrentNodeIDs(); },
|
||||||
[&](TestValidations& vals) { vals.getPreferred(genesisLedger); },
|
[&](TestValidations& vals) { vals.getPreferred(genesisLedger); },
|
||||||
[&](TestValidations& vals) {
|
[&](TestValidations& vals) {
|
||||||
vals.getNodesAfter(ledgerA, ledgerA.id());
|
vals.getNodesAfter(ledgerA, ledgerA.id());
|
||||||
@@ -610,9 +609,9 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
ValStatus::current == harness.add(node.validate(ledgerA)));
|
ValStatus::current == harness.add(node.validate(ledgerA)));
|
||||||
|
|
||||||
{
|
{
|
||||||
hash_set<PeerKey> const expectedKeys = {a.masterKey(),
|
hash_set<PeerID> const expectedKeys = {a.nodeID(),
|
||||||
b.masterKey()};
|
b.nodeID()};
|
||||||
BEAST_EXPECT(harness.vals().getCurrentPublicKeys() == expectedKeys);
|
BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
harness.clock().advance(3s);
|
harness.clock().advance(3s);
|
||||||
@@ -626,14 +625,14 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
ValStatus::current == harness.add(node.partial(ledgerAC)));
|
ValStatus::current == harness.add(node.partial(ledgerAC)));
|
||||||
|
|
||||||
{
|
{
|
||||||
hash_set<PeerKey> const expectedKeys = {a.masterKey(),
|
hash_set<PeerID> const expectedKeys = {a.nodeID(),
|
||||||
b.masterKey()};
|
b.nodeID()};
|
||||||
BEAST_EXPECT(harness.vals().getCurrentPublicKeys() == expectedKeys);
|
BEAST_EXPECT(harness.vals().getCurrentNodeIDs() == expectedKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass enough time for them to go stale
|
// Pass enough time for them to go stale
|
||||||
harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
|
harness.clock().advance(harness.parms().validationCURRENT_LOCAL);
|
||||||
BEAST_EXPECT(harness.vals().getCurrentPublicKeys().empty());
|
BEAST_EXPECT(harness.vals().getCurrentNodeIDs().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -719,7 +718,7 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
if (val.trusted())
|
if (val.trusted())
|
||||||
trustedValidations[val.ledgerID()].emplace_back(val);
|
trustedValidations[val.ledgerID()].emplace_back(val);
|
||||||
}
|
}
|
||||||
// d diagrees
|
// d disagrees
|
||||||
{
|
{
|
||||||
auto const val = d.validate(ledgerB);
|
auto const val = d.validate(ledgerB);
|
||||||
BEAST_EXPECT(ValStatus::current == harness.add(val));
|
BEAST_EXPECT(ValStatus::current == harness.add(val));
|
||||||
@@ -786,21 +785,21 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
Ledger ledgerA = h["a"];
|
Ledger ledgerA = h["a"];
|
||||||
Ledger ledgerAB = h["ab"];
|
Ledger ledgerAB = h["ab"];
|
||||||
|
|
||||||
hash_map<PeerKey, Validation> expected;
|
hash_map<PeerID, Validation> expected;
|
||||||
for (auto const& node : {a, b, c})
|
for (auto const& node : {a, b, c})
|
||||||
{
|
{
|
||||||
auto const val = node.validate(ledgerA);
|
auto const val = node.validate(ledgerA);
|
||||||
BEAST_EXPECT(ValStatus::current == harness.add(val));
|
BEAST_EXPECT(ValStatus::current == harness.add(val));
|
||||||
expected.emplace(node.masterKey(), val);
|
expected.emplace(node.nodeID(), val);
|
||||||
}
|
}
|
||||||
Validation staleA = expected.find(a.masterKey())->second;
|
Validation staleA = expected.find(a.nodeID())->second;
|
||||||
|
|
||||||
// Send in a new validation for a, saving the new one into the expected
|
// Send in a new validation for a, saving the new one into the expected
|
||||||
// map after setting the proper prior ledger ID it replaced
|
// map after setting the proper prior ledger ID it replaced
|
||||||
harness.clock().advance(1s);
|
harness.clock().advance(1s);
|
||||||
auto newVal = a.validate(ledgerAB);
|
auto newVal = a.validate(ledgerAB);
|
||||||
BEAST_EXPECT(ValStatus::current == harness.add(newVal));
|
BEAST_EXPECT(ValStatus::current == harness.add(newVal));
|
||||||
expected.find(a.masterKey())->second = newVal;
|
expected.find(a.nodeID())->second = newVal;
|
||||||
|
|
||||||
// Now flush
|
// Now flush
|
||||||
harness.vals().flush();
|
harness.vals().flush();
|
||||||
@@ -1056,6 +1055,97 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
|
BEAST_EXPECT(enforcer(clock.now(), Seq{1}, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testTrustChanged()
|
||||||
|
{
|
||||||
|
testcase("TrustChanged");
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
auto checker = [this](
|
||||||
|
TestValidations& vals,
|
||||||
|
hash_set<PeerID> const& listed,
|
||||||
|
std::vector<Validation> const& trustedVals) {
|
||||||
|
Ledger::ID testID = trustedVals.empty() ? this->genesisLedger.id()
|
||||||
|
: trustedVals[0].ledgerID();
|
||||||
|
BEAST_EXPECT(vals.currentTrusted() == trustedVals);
|
||||||
|
BEAST_EXPECT(vals.getCurrentNodeIDs() == listed);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
vals.getNodesAfter(this->genesisLedger, genesisLedger.id()) ==
|
||||||
|
trustedVals.size());
|
||||||
|
BEAST_EXPECT(
|
||||||
|
vals.getPreferred(this->genesisLedger).second == testID);
|
||||||
|
BEAST_EXPECT(vals.getTrustedForLedger(testID) == trustedVals);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
vals.numTrustedForLedger(testID) == trustedVals.size());
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
// Trusted to untrusted
|
||||||
|
LedgerHistoryHelper h;
|
||||||
|
TestHarness harness(h.oracle);
|
||||||
|
Node a = harness.makeNode();
|
||||||
|
Ledger ledgerAB = h["ab"];
|
||||||
|
Validation v = a.validate(ledgerAB);
|
||||||
|
BEAST_EXPECT(ValStatus::current == harness.add(v));
|
||||||
|
|
||||||
|
hash_set<PeerID> listed({a.nodeID()});
|
||||||
|
std::vector<Validation> trustedVals({v});
|
||||||
|
checker(harness.vals(), listed, trustedVals);
|
||||||
|
|
||||||
|
trustedVals.clear();
|
||||||
|
harness.vals().trustChanged({}, {a.nodeID()});
|
||||||
|
checker(harness.vals(), listed, trustedVals);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Untrusted to trusted
|
||||||
|
LedgerHistoryHelper h;
|
||||||
|
TestHarness harness(h.oracle);
|
||||||
|
Node a = harness.makeNode();
|
||||||
|
a.untrust();
|
||||||
|
Ledger ledgerAB = h["ab"];
|
||||||
|
Validation v = a.validate(ledgerAB);
|
||||||
|
BEAST_EXPECT(ValStatus::current == harness.add(v));
|
||||||
|
|
||||||
|
hash_set<PeerID> listed({a.nodeID()});
|
||||||
|
std::vector<Validation> trustedVals;
|
||||||
|
checker(harness.vals(), listed, trustedVals);
|
||||||
|
|
||||||
|
trustedVals.push_back(v);
|
||||||
|
harness.vals().trustChanged({a.nodeID()}, {});
|
||||||
|
checker(harness.vals(), listed, trustedVals);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Trusted but not acquired -> untrusted
|
||||||
|
LedgerHistoryHelper h;
|
||||||
|
TestHarness harness(h.oracle);
|
||||||
|
Node a = harness.makeNode();
|
||||||
|
Validation v =
|
||||||
|
a.validate(Ledger::ID{2}, Ledger::Seq{2}, 0s, 0s, true);
|
||||||
|
BEAST_EXPECT(ValStatus::current == harness.add(v));
|
||||||
|
|
||||||
|
hash_set<PeerID> listed({a.nodeID()});
|
||||||
|
std::vector<Validation> trustedVals({v});
|
||||||
|
auto& vals = harness.vals();
|
||||||
|
BEAST_EXPECT(vals.currentTrusted() == trustedVals);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
vals.getPreferred(genesisLedger).second == v.ledgerID());
|
||||||
|
BEAST_EXPECT(
|
||||||
|
vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
|
||||||
|
|
||||||
|
trustedVals.clear();
|
||||||
|
harness.vals().trustChanged({}, {a.nodeID()});
|
||||||
|
// make acquiring ledger available
|
||||||
|
h["ab"];
|
||||||
|
BEAST_EXPECT(vals.currentTrusted() == trustedVals);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
vals.getPreferred(genesisLedger).second == genesisLedger.id());
|
||||||
|
BEAST_EXPECT(
|
||||||
|
vals.getNodesAfter(genesisLedger, genesisLedger.id()) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
@@ -1072,6 +1162,7 @@ class Validations_test : public beast::unit_test::suite
|
|||||||
testAcquireValidatedLedger();
|
testAcquireValidatedLedger();
|
||||||
testNumTrustedForLedger();
|
testNumTrustedForLedger();
|
||||||
testSeqEnforcer();
|
testSeqEnforcer();
|
||||||
|
testTrustChanged();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ struct Peer
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
flush(hash_map<PeerKey, Validation>&& remaining)
|
flush(hash_map<PeerID, Validation>&& remaining)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,9 +681,9 @@ struct Peer
|
|||||||
{
|
{
|
||||||
v.setTrusted();
|
v.setTrusted();
|
||||||
v.setSeen(now());
|
v.setSeen(now());
|
||||||
ValStatus const res = validations.add(v.key(), v);
|
ValStatus const res = validations.add(v.nodeID(), v);
|
||||||
|
|
||||||
if(res == ValStatus::stale || res == ValStatus::repeatID)
|
if(res == ValStatus::stale)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Acquire will try to get from network if not already local
|
// Acquire will try to get from network if not already local
|
||||||
@@ -875,8 +875,10 @@ struct Peer
|
|||||||
|
|
||||||
issue(StartRound{bestLCL, lastClosedLedger});
|
issue(StartRound{bestLCL, lastClosedLedger});
|
||||||
|
|
||||||
|
// Not yet modeling dynamic UNL.
|
||||||
|
hash_set<PeerID> nowUntrusted;
|
||||||
consensus.startRound(
|
consensus.startRound(
|
||||||
now(), bestLCL, lastClosedLedger, runAsValidator);
|
now(), bestLCL, lastClosedLedger, nowUntrusted, runAsValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the consensus process assuming it is not yet running
|
// Start the consensus process assuming it is not yet running
|
||||||
@@ -895,7 +897,7 @@ struct Peer
|
|||||||
{
|
{
|
||||||
// We don't care about the actual epochs, but do want the
|
// We don't care about the actual epochs, but do want the
|
||||||
// generated NetClock time to be well past its epoch to ensure
|
// generated NetClock time to be well past its epoch to ensure
|
||||||
// any subtractions of two NetClock::time_point in the consensu
|
// any subtractions of two NetClock::time_point in the consensus
|
||||||
// code are positive. (e.g. proposeFRESHNESS)
|
// code are positive. (e.g. proposeFRESHNESS)
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|||||||
@@ -173,6 +173,12 @@ public:
|
|||||||
trusted_ = true;
|
trusted_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setUntrusted()
|
||||||
|
{
|
||||||
|
trusted_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
setSeen(NetClock::time_point seen)
|
setSeen(NetClock::time_point seen)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -308,11 +308,11 @@ public:
|
|||||||
|
|
||||||
env.app().validatorSites().start();
|
env.app().validatorSites().start();
|
||||||
env.app().validatorSites().join();
|
env.app().validatorSites().join();
|
||||||
std::set<PublicKey> startKeys;
|
hash_set<NodeID> startKeys;
|
||||||
for (auto const& val : validators)
|
for (auto const& val : validators)
|
||||||
startKeys.insert(val.masterPublic);
|
startKeys.insert(calcNodeID(val.masterPublic));
|
||||||
|
|
||||||
env.app().validators().onConsensusStart(startKeys);
|
env.app().validators().updateTrusted(startKeys);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto const jrr = env.rpc("server_info")[jss::result];
|
auto const jrr = env.rpc("server_info")[jss::result];
|
||||||
|
|||||||
Reference in New Issue
Block a user