mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Support UNLs with future effective dates:
* Creates a version 2 of the UNL file format allowing publishers to pre-publish the next UNL while the current one is still valid. * Version 1 of the UNL file format is still valid and backward compatible. * Also causes rippled to lock down if it has no valid UNLs, similar to being amendment blocked, except reversible. * Resolves #3548 * Resolves #3470
This commit is contained in:
@@ -433,6 +433,8 @@ PeerImp::supportsFeature(ProtocolFeature f) const
|
||||
{
|
||||
case ProtocolFeature::ValidatorListPropagation:
|
||||
return protocol_ >= make_protocol(2, 1);
|
||||
case ProtocolFeature::ValidatorList2Propagation:
|
||||
return protocol_ >= make_protocol(2, 2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -817,29 +819,27 @@ PeerImp::doProtocolStart()
|
||||
// Send all the validator lists that have been loaded
|
||||
if (supportsFeature(ProtocolFeature::ValidatorListPropagation))
|
||||
{
|
||||
app_.validators().for_each_available([&](std::string const& manifest,
|
||||
std::string const& blob,
|
||||
std::string const& signature,
|
||||
std::uint32_t version,
|
||||
PublicKey const& pubKey,
|
||||
std::size_t sequence,
|
||||
uint256 const& hash) {
|
||||
protocol::TMValidatorList vl;
|
||||
app_.validators().for_each_available(
|
||||
[&](std::string const& manifest,
|
||||
std::uint32_t version,
|
||||
std::map<std::size_t, ValidatorBlobInfo> const& blobInfos,
|
||||
PublicKey const& pubKey,
|
||||
std::size_t maxSequence,
|
||||
uint256 const& hash) {
|
||||
ValidatorList::sendValidatorList(
|
||||
*this,
|
||||
0,
|
||||
pubKey,
|
||||
maxSequence,
|
||||
version,
|
||||
manifest,
|
||||
blobInfos,
|
||||
app_.getHashRouter(),
|
||||
p_journal_);
|
||||
|
||||
vl.set_manifest(manifest);
|
||||
vl.set_blob(blob);
|
||||
vl.set_signature(signature);
|
||||
vl.set_version(version);
|
||||
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Sending validator list for " << strHex(pubKey)
|
||||
<< " with sequence " << sequence << " to "
|
||||
<< remote_address_.to_string() << " (" << id_ << ")";
|
||||
send(std::make_shared<Message>(vl, protocol::mtVALIDATORLIST));
|
||||
// Don't send it next time.
|
||||
app_.getHashRouter().addSuppressionPeer(hash, id_);
|
||||
setPublisherListSequence(pubKey, sequence);
|
||||
});
|
||||
// Don't send it next time.
|
||||
app_.getHashRouter().addSuppressionPeer(hash, id_);
|
||||
});
|
||||
}
|
||||
|
||||
if (auto m = overlay_.getManifestsMessage())
|
||||
@@ -1859,6 +1859,202 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMHaveTransactionSet> const& m)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PeerImp::onValidatorListMessage(
|
||||
std::string const& messageType,
|
||||
std::string const& manifest,
|
||||
std::uint32_t version,
|
||||
std::vector<ValidatorBlobInfo> const& blobs)
|
||||
{
|
||||
// If there are no blobs, the message is malformed (possibly because of
|
||||
// ValidatorList class rules), so charge accordingly and skip processing.
|
||||
if (blobs.empty())
|
||||
{
|
||||
JLOG(p_journal_.warn()) << "Ignored malformed " << messageType
|
||||
<< " from peer " << remote_address_;
|
||||
// This shouldn't ever happen with a well-behaved peer
|
||||
fee_ = Resource::feeHighBurdenPeer;
|
||||
return;
|
||||
}
|
||||
|
||||
auto const hash = sha512Half(manifest, blobs, version);
|
||||
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Received " << messageType << " from " << remote_address_.to_string()
|
||||
<< " (" << id_ << ")";
|
||||
|
||||
if (!app_.getHashRouter().addSuppressionPeer(hash, id_))
|
||||
{
|
||||
JLOG(p_journal_.debug())
|
||||
<< messageType << ": received duplicate " << messageType;
|
||||
// Charging this fee here won't hurt the peer in the normal
|
||||
// course of operation (ie. refresh every 5 minutes), but
|
||||
// will add up if the peer is misbehaving.
|
||||
fee_ = Resource::feeUnwantedData;
|
||||
return;
|
||||
}
|
||||
|
||||
auto const applyResult = app_.validators().applyListsAndBroadcast(
|
||||
manifest,
|
||||
version,
|
||||
blobs,
|
||||
remote_address_.to_string(),
|
||||
hash,
|
||||
app_.overlay(),
|
||||
app_.getHashRouter(),
|
||||
app_.getOPs());
|
||||
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Processed " << messageType << " version " << version << " from "
|
||||
<< (applyResult.publisherKey ? strHex(*applyResult.publisherKey)
|
||||
: "unknown or invalid publisher")
|
||||
<< " from " << remote_address_.to_string() << " (" << id_
|
||||
<< ") with best result " << to_string(applyResult.bestDisposition());
|
||||
|
||||
// Act based on the best result
|
||||
switch (applyResult.bestDisposition())
|
||||
{
|
||||
// New list
|
||||
case ListDisposition::accepted:
|
||||
// Newest list is expired, and that needs to be broadcast, too
|
||||
case ListDisposition::expired:
|
||||
// Future list
|
||||
case ListDisposition::pending: {
|
||||
std::lock_guard<std::mutex> sl(recentLock_);
|
||||
|
||||
assert(applyResult.publisherKey);
|
||||
auto const& pubKey = *applyResult.publisherKey;
|
||||
#ifndef NDEBUG
|
||||
if (auto const iter = publisherListSequences_.find(pubKey);
|
||||
iter != publisherListSequences_.end())
|
||||
{
|
||||
assert(iter->second < applyResult.sequence);
|
||||
}
|
||||
#endif
|
||||
publisherListSequences_[pubKey] = applyResult.sequence;
|
||||
}
|
||||
break;
|
||||
case ListDisposition::same_sequence:
|
||||
case ListDisposition::known_sequence:
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
std::lock_guard<std::mutex> sl(recentLock_);
|
||||
assert(applyResult.sequence && applyResult.publisherKey);
|
||||
assert(
|
||||
publisherListSequences_[*applyResult.publisherKey] <=
|
||||
applyResult.sequence);
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
|
||||
break;
|
||||
case ListDisposition::stale:
|
||||
case ListDisposition::untrusted:
|
||||
case ListDisposition::invalid:
|
||||
case ListDisposition::unsupported_version:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Charge based on the worst result
|
||||
switch (applyResult.worstDisposition())
|
||||
{
|
||||
case ListDisposition::accepted:
|
||||
case ListDisposition::expired:
|
||||
case ListDisposition::pending:
|
||||
// No charges for good data
|
||||
break;
|
||||
case ListDisposition::same_sequence:
|
||||
case ListDisposition::known_sequence:
|
||||
// Charging this fee here won't hurt the peer in the normal
|
||||
// course of operation (ie. refresh every 5 minutes), but
|
||||
// will add up if the peer is misbehaving.
|
||||
fee_ = Resource::feeUnwantedData;
|
||||
break;
|
||||
case ListDisposition::stale:
|
||||
// There are very few good reasons for a peer to send an
|
||||
// old list, particularly more than once.
|
||||
fee_ = Resource::feeBadData;
|
||||
break;
|
||||
case ListDisposition::untrusted:
|
||||
// Charging this fee here won't hurt the peer in the normal
|
||||
// course of operation (ie. refresh every 5 minutes), but
|
||||
// will add up if the peer is misbehaving.
|
||||
fee_ = Resource::feeUnwantedData;
|
||||
break;
|
||||
case ListDisposition::invalid:
|
||||
// This shouldn't ever happen with a well-behaved peer
|
||||
fee_ = Resource::feeInvalidSignature;
|
||||
break;
|
||||
case ListDisposition::unsupported_version:
|
||||
// During a version transition, this may be legitimate.
|
||||
// If it happens frequently, that's probably bad.
|
||||
fee_ = Resource::feeBadData;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Log based on all the results.
|
||||
for (auto const [disp, count] : applyResult.dispositions)
|
||||
{
|
||||
switch (disp)
|
||||
{
|
||||
// New list
|
||||
case ListDisposition::accepted:
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Applied " << count << " new " << messageType
|
||||
<< "(s) from peer " << remote_address_;
|
||||
break;
|
||||
// Newest list is expired, and that needs to be broadcast, too
|
||||
case ListDisposition::expired:
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Applied " << count << " expired " << messageType
|
||||
<< "(s) from peer " << remote_address_;
|
||||
break;
|
||||
// Future list
|
||||
case ListDisposition::pending:
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Processed " << count << " future " << messageType
|
||||
<< "(s) from peer " << remote_address_;
|
||||
break;
|
||||
case ListDisposition::same_sequence:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Ignored " << count << " " << messageType
|
||||
<< "(s) with current sequence from peer "
|
||||
<< remote_address_;
|
||||
break;
|
||||
case ListDisposition::known_sequence:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Ignored " << count << " " << messageType
|
||||
<< "(s) with future sequence from peer " << remote_address_;
|
||||
break;
|
||||
case ListDisposition::stale:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Ignored " << count << "stale " << messageType
|
||||
<< "(s) from peer " << remote_address_;
|
||||
break;
|
||||
case ListDisposition::untrusted:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Ignored " << count << " untrusted " << messageType
|
||||
<< "(s) from peer " << remote_address_;
|
||||
break;
|
||||
case ListDisposition::unsupported_version:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Ignored " << count << "unsupported version "
|
||||
<< messageType << "(s) from peer " << remote_address_;
|
||||
break;
|
||||
case ListDisposition::invalid:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Ignored " << count << "invalid " << messageType
|
||||
<< "(s) from peer " << remote_address_;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PeerImp::onMessage(std::shared_ptr<protocol::TMValidatorList> const& m)
|
||||
{
|
||||
@@ -1873,117 +2069,11 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMValidatorList> const& m)
|
||||
fee_ = Resource::feeUnwantedData;
|
||||
return;
|
||||
}
|
||||
auto const& manifest = m->manifest();
|
||||
auto const& blob = m->blob();
|
||||
auto const& signature = m->signature();
|
||||
auto const version = m->version();
|
||||
auto const hash = sha512Half(manifest, blob, signature, version);
|
||||
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Received validator list from " << remote_address_.to_string()
|
||||
<< " (" << id_ << ")";
|
||||
|
||||
if (!app_.getHashRouter().addSuppressionPeer(hash, id_))
|
||||
{
|
||||
JLOG(p_journal_.debug())
|
||||
<< "ValidatorList: received duplicate validator list";
|
||||
// Charging this fee here won't hurt the peer in the normal
|
||||
// course of operation (ie. refresh every 5 minutes), but
|
||||
// will add up if the peer is misbehaving.
|
||||
fee_ = Resource::feeUnwantedData;
|
||||
return;
|
||||
}
|
||||
|
||||
auto const applyResult = app_.validators().applyListAndBroadcast(
|
||||
manifest,
|
||||
blob,
|
||||
signature,
|
||||
version,
|
||||
remote_address_.to_string(),
|
||||
hash,
|
||||
app_.overlay(),
|
||||
app_.getHashRouter());
|
||||
auto const disp = applyResult.disposition;
|
||||
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Processed validator list from "
|
||||
<< (applyResult.publisherKey ? strHex(*applyResult.publisherKey)
|
||||
: "unknown or invalid publisher")
|
||||
<< " from " << remote_address_.to_string() << " (" << id_
|
||||
<< ") with result " << to_string(disp);
|
||||
|
||||
switch (disp)
|
||||
{
|
||||
case ListDisposition::accepted:
|
||||
JLOG(p_journal_.debug())
|
||||
<< "Applied new validator list from peer "
|
||||
<< remote_address_;
|
||||
{
|
||||
std::lock_guard<std::mutex> sl(recentLock_);
|
||||
|
||||
assert(applyResult.sequence && applyResult.publisherKey);
|
||||
auto const& pubKey = *applyResult.publisherKey;
|
||||
#ifndef NDEBUG
|
||||
if (auto const iter = publisherListSequences_.find(pubKey);
|
||||
iter != publisherListSequences_.end())
|
||||
{
|
||||
assert(iter->second < *applyResult.sequence);
|
||||
}
|
||||
#endif
|
||||
publisherListSequences_[pubKey] = *applyResult.sequence;
|
||||
}
|
||||
break;
|
||||
case ListDisposition::same_sequence:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Validator list with current sequence from peer "
|
||||
<< remote_address_;
|
||||
// Charging this fee here won't hurt the peer in the normal
|
||||
// course of operation (ie. refresh every 5 minutes), but
|
||||
// will add up if the peer is misbehaving.
|
||||
fee_ = Resource::feeUnwantedData;
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
std::lock_guard<std::mutex> sl(recentLock_);
|
||||
assert(applyResult.sequence && applyResult.publisherKey);
|
||||
assert(
|
||||
publisherListSequences_[*applyResult.publisherKey] ==
|
||||
*applyResult.sequence);
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
|
||||
break;
|
||||
case ListDisposition::stale:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Stale validator list from peer " << remote_address_;
|
||||
// There are very few good reasons for a peer to send an
|
||||
// old list, particularly more than once.
|
||||
fee_ = Resource::feeBadData;
|
||||
break;
|
||||
case ListDisposition::untrusted:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Untrusted validator list from peer " << remote_address_;
|
||||
// Charging this fee here won't hurt the peer in the normal
|
||||
// course of operation (ie. refresh every 5 minutes), but
|
||||
// will add up if the peer is misbehaving.
|
||||
fee_ = Resource::feeUnwantedData;
|
||||
break;
|
||||
case ListDisposition::invalid:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Invalid validator list from peer " << remote_address_;
|
||||
// This shouldn't ever happen with a well-behaved peer
|
||||
fee_ = Resource::feeInvalidSignature;
|
||||
break;
|
||||
case ListDisposition::unsupported_version:
|
||||
JLOG(p_journal_.warn())
|
||||
<< "Unsupported version validator list from peer "
|
||||
<< remote_address_;
|
||||
// During a version transition, this may be legitimate.
|
||||
// If it happens frequently, that's probably bad.
|
||||
fee_ = Resource::feeBadData;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
onValidatorListMessage(
|
||||
"ValidatorList",
|
||||
m->manifest(),
|
||||
m->version(),
|
||||
ValidatorList::parseBlobs(*m));
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
@@ -1993,6 +2083,45 @@ PeerImp::onMessage(std::shared_ptr<protocol::TMValidatorList> const& m)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PeerImp::onMessage(
|
||||
std::shared_ptr<protocol::TMValidatorListCollection> const& m)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!supportsFeature(ProtocolFeature::ValidatorList2Propagation))
|
||||
{
|
||||
JLOG(p_journal_.debug())
|
||||
<< "ValidatorListCollection: received validator list from peer "
|
||||
<< "using protocol version " << to_string(protocol_)
|
||||
<< " which shouldn't support this feature.";
|
||||
fee_ = Resource::feeUnwantedData;
|
||||
return;
|
||||
}
|
||||
else if (m->version() < 2)
|
||||
{
|
||||
JLOG(p_journal_.debug())
|
||||
<< "ValidatorListCollection: received invalid validator list "
|
||||
"version "
|
||||
<< m->version() << " from peer using protocol version "
|
||||
<< to_string(protocol_);
|
||||
fee_ = Resource::feeBadData;
|
||||
return;
|
||||
}
|
||||
onValidatorListMessage(
|
||||
"ValidatorListCollection",
|
||||
m->manifest(),
|
||||
m->version(),
|
||||
ValidatorList::parseBlobs(*m));
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(p_journal_.warn()) << "ValidatorListCollection: Exception, "
|
||||
<< e.what() << " from peer " << remote_address_;
|
||||
fee_ = Resource::feeBadData;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PeerImp::onMessage(std::shared_ptr<protocol::TMValidation> const& m)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user