mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 14:20:56 +00:00
fix: Increase manifest protocol message size cap and fix manifests relay
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
This commit is contained in:
committed by
Ayaz Salikhov
parent
15d08770bf
commit
54cfdda00b
@@ -549,6 +549,45 @@
|
||||
# only be used for local testing and debugging. Do not disable
|
||||
# on mainnet.
|
||||
#
|
||||
# max_untrusted_count = <number>
|
||||
#
|
||||
# The number of manifests the server keeps for validators it does not
|
||||
# list, and the number it sends and processes in a single peer protocol
|
||||
# message. Once the server holds this many, a manifest for a new
|
||||
# unlisted validator is rejected, so peer gossip cannot grow the cache
|
||||
# without end.
|
||||
#
|
||||
# This option can take any value between 50 and 1000, inclusive. If
|
||||
# the option is not present the server uses its built-in value.
|
||||
#
|
||||
# The current default (which is subject to change) is 300.
|
||||
#
|
||||
# max_trusted_count = <number>
|
||||
#
|
||||
# The number of manifests for listed validators to allow for when
|
||||
# sizing peer protocol messages. Manifests for listed validators are
|
||||
# never dropped, whether sending or receiving, because doing so would
|
||||
# delay a validator key change reaching this server. Set this above the
|
||||
# number of validators the server lists.
|
||||
#
|
||||
# Together the two counts above set the largest manifest message the
|
||||
# server accepts: bigger messages are discarded without reading them,
|
||||
# and without penalising the sender. Raising either means the server
|
||||
# accepts and sends bigger messages than a peer using the defaults, and
|
||||
# those peers will discard what this server sends. Lowering either below
|
||||
# what peers send makes this server discard their manifest messages,
|
||||
# which it does without recording anything.
|
||||
#
|
||||
# This option can take any value between 50 and 1000, inclusive. If
|
||||
# the option is not present the server uses its built-in value.
|
||||
#
|
||||
# The current default (which is subject to change) is 300.
|
||||
#
|
||||
# NOTE: These two options (max_untrusted_count and max_trusted_count)
|
||||
# are transitional. They exist to bound manifest-message size and cache
|
||||
# growth during the network upgrade. They may be removed in a future
|
||||
# release once the fleet has upgraded, and should not be relied upon as
|
||||
# stable configuration.
|
||||
#
|
||||
# [transaction_queue] EXPERIMENTAL
|
||||
#
|
||||
|
||||
@@ -119,7 +119,9 @@ struct Keys
|
||||
static constexpr auto kLogInterval = "log_interval";
|
||||
static constexpr auto kMaxDivergedTime = "max_diverged_time";
|
||||
static constexpr auto kMaxLedgerCountsToStore = "max_ledger_counts_to_store";
|
||||
static constexpr auto kMaxTrustedCount = "max_trusted_count";
|
||||
static constexpr auto kMaxUnknownTime = "max_unknown_time";
|
||||
static constexpr auto kMaxUntrustedCount = "max_untrusted_count";
|
||||
static constexpr auto kMaximumTxnInLedger = "maximum_txn_in_ledger";
|
||||
static constexpr auto kMaximumTxnPerAccount = "maximum_txn_per_account";
|
||||
static constexpr auto kMemoryLevel = "memory_level";
|
||||
|
||||
@@ -201,21 +201,67 @@ constexpr std::size_t kMaxManifestBytes = 358;
|
||||
constexpr std::size_t kMaxManifestBase64 = base64::encodedSize(kMaxManifestBytes);
|
||||
|
||||
/**
|
||||
* Maximum number of manifests carried in a single TMManifests message.
|
||||
* Default number of untrusted manifests to store in cache and allowed
|
||||
* in one Manifest message.
|
||||
*
|
||||
* Outbound, the TMManifests message sent to a peer includes every trusted
|
||||
* manifest and fills the rest of this budget with untrusted gossip, so it
|
||||
* never exceeds this size. Inbound, trusted manifests are always processed
|
||||
* and untrusted ones are processed up to this many, so a peer sending its
|
||||
* whole cache cannot force unbounded work.
|
||||
* Bounds unlisted validators two ways. In the cache, a manifest for a
|
||||
* brand-new unlisted key is rejected once this many are held, so peer gossip
|
||||
* cannot grow the cache without end. In a TMManifests message, this many are
|
||||
* sent and processed, so a peer sending its whole cache cannot force unbounded
|
||||
* work.
|
||||
*
|
||||
* The trusted set is tiny relative to this bound, so trusted manifests are
|
||||
* not dropped in practice. This is a transitional per-message cap; the cache
|
||||
* already bounds untrusted manifests (see kMaxUntrustedCount), so it is no
|
||||
* longer needed once the network has upgraded past nodes that send their
|
||||
* whole cache in one message.
|
||||
* Operators can override this with `[overlay] max_untrusted_count`. Both users
|
||||
* read the configured value and fall back to this default.
|
||||
*/
|
||||
constexpr std::size_t kMaxManifestsPerMessage = 200;
|
||||
constexpr std::size_t kMaxUntrustedCount = 300;
|
||||
|
||||
/**
|
||||
* Default number of trusted manifests allowed in a Manifest message.
|
||||
* Not used atm while creating the message, but used to calculate the higher limit on
|
||||
* received message size. Introduced to maintain consistency. Future implementation
|
||||
* will use this limit.
|
||||
*
|
||||
* Trusted manifests are never dropped: every one this node holds is sent, and
|
||||
* every one received is processed, since dropping one would delay a validator
|
||||
* key rotation. This count only sizes the largest message accepted, so it must
|
||||
* stay above any realistic validator list. Cap can be increased in the config
|
||||
* file if messages get rejected with actual trusted manifest count crossing
|
||||
* configured(or else default) value.
|
||||
* Operators can override this with `[overlay] max_trusted_count`.
|
||||
*/
|
||||
constexpr std::size_t kMaxTrustedCount = 300;
|
||||
|
||||
/**
|
||||
* Number of untrusted manifests to store in cache and allowed
|
||||
* in one Manifest message..
|
||||
*
|
||||
* Returns the operator's override when one is configured, otherwise
|
||||
* @ref kMaxUntrustedCount. Config stores an override rather than the default
|
||||
* itself because the core module cannot depend on this module.
|
||||
*
|
||||
* @param configured The value from `[overlay] max_untrusted_count`, or
|
||||
* `std::nullopt` when the operator did not set it.
|
||||
*/
|
||||
constexpr std::size_t
|
||||
untrustedManifestCount(std::optional<std::size_t> const& configured)
|
||||
{
|
||||
return configured.value_or(kMaxUntrustedCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of trusted manifests allowed in a Manifest message.
|
||||
*
|
||||
* Not a cap on how many are sent or processed; see @ref kMaxTrustedCount.
|
||||
* but used to calculate the higher limit on received message size.
|
||||
*
|
||||
* @param configured The value from `[overlay] max_trusted_count`, or
|
||||
* `std::nullopt` when the operator did not set it.
|
||||
*/
|
||||
constexpr std::size_t
|
||||
trustedManifestCount(std::optional<std::size_t> const& configured)
|
||||
{
|
||||
return configured.value_or(kMaxTrustedCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs Manifest from serialized string
|
||||
@@ -361,9 +407,10 @@ private:
|
||||
/**
|
||||
* Maximum number of untrusted master keys kept in the cache.
|
||||
*
|
||||
* Once reached, a manifest for a brand-new unlisted key is rejected.
|
||||
* Once reached, a manifest for a brand-new unlisted key is rejected. Set
|
||||
* from the config, defaulting to @ref kMaxUntrustedCount.
|
||||
*/
|
||||
static constexpr std::size_t kMaxUntrustedCount = 100;
|
||||
std::size_t const maxUntrustedCount_;
|
||||
|
||||
/**
|
||||
* Running count of manifests rejected because the untrusted cap was full.
|
||||
@@ -381,7 +428,17 @@ private:
|
||||
static constexpr std::uint64_t kUntrustedRejectCount = 10000;
|
||||
|
||||
public:
|
||||
explicit ManifestCache(beast::Journal j = beast::Journal(beast::Journal::getNullSink())) : j_(j)
|
||||
/**
|
||||
* @param j Journal for logging.
|
||||
*
|
||||
* @param maxUntrustedCount Untrusted master keys to keep. Pass the
|
||||
* configured value; defaults to @ref kMaxUntrustedCount. Taken as a
|
||||
* parameter because this module cannot depend on the config.
|
||||
*/
|
||||
explicit ManifestCache(
|
||||
beast::Journal j = beast::Journal(beast::Journal::getNullSink()),
|
||||
std::size_t maxUntrustedCount = kMaxUntrustedCount)
|
||||
: j_(j), maxUntrustedCount_(maxUntrustedCount)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -487,7 +487,7 @@ ManifestCache::applyManifest(Manifest m, ManifestRateLimitCapPolicy const cap)
|
||||
lock.owns_lock(), "xrpl::ManifestCache::applyManifest::atUntrustedCap : locked");
|
||||
(void)lock; // not used. parameter is present to ensure the mutex is
|
||||
// locked when the lambda is called.
|
||||
if (iter == map_.end() && !uncapped && untrustedKeys_.size() >= kMaxUntrustedCount)
|
||||
if (iter == map_.end() && !uncapped && untrustedKeys_.size() >= maxUntrustedCount_)
|
||||
{
|
||||
// Log each rejection at debug, but warn only once per interval so a
|
||||
// flood does not fill the log.
|
||||
|
||||
@@ -1575,6 +1575,87 @@ r.ripple.com:51235
|
||||
|
||||
// Above upper bound
|
||||
BEAST_EXPECT(!testDiverged("901"));
|
||||
|
||||
testcase("overlay: manifest counts");
|
||||
|
||||
// Both keys share one range and one parse path, so exercise each
|
||||
// through the same helper.
|
||||
auto testCount = [](std::string const& key,
|
||||
std::string const& value) -> std::optional<std::size_t> {
|
||||
try
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString("[overlay]\n" + key + "=" + value);
|
||||
return key == "max_trusted_count" ? c.maxTrustedCount : c.maxUntrustedCount;
|
||||
}
|
||||
catch (std::runtime_error const&)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
for (auto const* key : {"max_untrusted_count", "max_trusted_count"})
|
||||
{
|
||||
// Failures. A bad value must surface as std::runtime_error, not
|
||||
// the std::bad_cast that the underlying parse throws.
|
||||
BEAST_EXPECT(!testCount(key, "none"));
|
||||
BEAST_EXPECT(!testCount(key, "0.5"));
|
||||
BEAST_EXPECT(!testCount(key, "400 manifests"));
|
||||
BEAST_EXPECT(!testCount(key, "-1"));
|
||||
|
||||
// Below lower bound
|
||||
BEAST_EXPECT(!testCount(key, "0"));
|
||||
BEAST_EXPECT(!testCount(key, "49"));
|
||||
|
||||
// In bounds
|
||||
BEAST_EXPECT(testCount(key, "50") == 50);
|
||||
BEAST_EXPECT(testCount(key, "51") == 51);
|
||||
BEAST_EXPECT(testCount(key, "300") == 300);
|
||||
BEAST_EXPECT(testCount(key, "400") == 400);
|
||||
BEAST_EXPECT(testCount(key, "999") == 999);
|
||||
BEAST_EXPECT(testCount(key, "1000") == 1000);
|
||||
|
||||
// Above upper bound
|
||||
BEAST_EXPECT(!testCount(key, "1001"));
|
||||
}
|
||||
|
||||
// Each key is independent: setting one leaves the other unset.
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString("[overlay]\nmax_untrusted_count=500");
|
||||
BEAST_EXPECT(c.maxUntrustedCount == 500);
|
||||
BEAST_EXPECT(!c.maxTrustedCount);
|
||||
}
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString("[overlay]\nmax_trusted_count=500");
|
||||
BEAST_EXPECT(c.maxTrustedCount == 500);
|
||||
BEAST_EXPECT(!c.maxUntrustedCount);
|
||||
}
|
||||
|
||||
// Both can be set together.
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString("[overlay]\nmax_untrusted_count=250\nmax_trusted_count=750");
|
||||
BEAST_EXPECT(c.maxUntrustedCount == 250);
|
||||
BEAST_EXPECT(c.maxTrustedCount == 750);
|
||||
}
|
||||
|
||||
// Unset leaves no override, so the use sites fall back to the defaults.
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString("[overlay]\nip_limit=64");
|
||||
BEAST_EXPECT(!c.maxUntrustedCount);
|
||||
BEAST_EXPECT(!c.maxTrustedCount);
|
||||
}
|
||||
|
||||
// No [overlay] section at all leaves both unset too.
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString("");
|
||||
BEAST_EXPECT(!c.maxUntrustedCount);
|
||||
BEAST_EXPECT(!c.maxTrustedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
#include <xrpl/resource/Fees.h>
|
||||
#include <xrpl/resource/ResourceManager.h>
|
||||
#include <xrpl/server/LoadFeeTrack.h>
|
||||
#include <xrpl/server/Manifest.h>
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
#include <xrpl/server/Wallet.h>
|
||||
#include <xrpl/server/detail/ServerImpl.h>
|
||||
@@ -428,8 +429,14 @@ public:
|
||||
, cluster_(std::make_unique<Cluster>(logs_->journal("Overlay")))
|
||||
, peerReservations_(
|
||||
std::make_unique<PeerReservationTable>(logs_->journal("PeerReservationTable")))
|
||||
, validatorManifests_(std::make_unique<ManifestCache>(logs_->journal("ManifestCache")))
|
||||
, publisherManifests_(std::make_unique<ManifestCache>(logs_->journal("ManifestCache")))
|
||||
, validatorManifests_(
|
||||
std::make_unique<ManifestCache>(
|
||||
logs_->journal("ManifestCache"),
|
||||
untrustedManifestCount(config_->maxUntrustedCount)))
|
||||
, publisherManifests_(
|
||||
std::make_unique<ManifestCache>(
|
||||
logs_->journal("ManifestCache"),
|
||||
untrustedManifestCount(config_->maxUntrustedCount)))
|
||||
, validators_(
|
||||
std::make_unique<ValidatorList>(
|
||||
*validatorManifests_,
|
||||
@@ -1190,6 +1197,15 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
|
||||
JLOG(journal_.info()) << "Process starting: " << BuildInfo::getFullVersionString()
|
||||
<< ", Instance Cookie: " << instanceCookie_;
|
||||
|
||||
// Log the resolved manifest counts, whether configured or defaulted, so a
|
||||
// shared log shows what the server is running without needing its config.
|
||||
JLOG(journal_.warn()) << "Manifest counts: max_untrusted_count "
|
||||
<< untrustedManifestCount(config_->maxUntrustedCount)
|
||||
<< (config_->maxUntrustedCount ? " (configured)" : " (default)")
|
||||
<< ", max_trusted_count "
|
||||
<< trustedManifestCount(config_->maxTrustedCount)
|
||||
<< (config_->maxTrustedCount ? " (configured)" : " (default)");
|
||||
|
||||
if (numberOfThreads(*config_) < 2)
|
||||
{
|
||||
JLOG(journal_.warn()) << "Limited to a single I/O service thread by "
|
||||
|
||||
@@ -295,6 +295,23 @@ public:
|
||||
// How long can a peer remain in the "diverged" state
|
||||
std::chrono::seconds maxDivergedTime{300};
|
||||
|
||||
// Optional overrides for how many manifests are kept in the cache and
|
||||
// carried in one TMManifests message, split by whether this node lists the
|
||||
// validator. Unset means use the built-in defaults (kMaxUntrustedCount and
|
||||
// kMaxTrustedCount in Manifest.h). Kept as overrides here, rather than the
|
||||
// defaults themselves, so the core module need not depend on the server
|
||||
// module that owns the constants.
|
||||
std::optional<std::size_t> maxUntrustedCount;
|
||||
std::optional<std::size_t> maxTrustedCount;
|
||||
|
||||
// Bounds for both counts above. The lower bound leaves room for a small
|
||||
// network or a deliberately tight limit; note that setting a count below
|
||||
// what peers actually send means their manifest messages are dropped for
|
||||
// being oversized. The upper bound keeps the implied message size well
|
||||
// under the overall protocol message limit.
|
||||
static constexpr std::size_t kMinManifestCount = 50;
|
||||
static constexpr std::size_t kMaxManifestCount = 1000;
|
||||
|
||||
// Enable the beta API version
|
||||
bool betaRpcApi = false;
|
||||
|
||||
|
||||
@@ -923,6 +923,38 @@ Config::loadFromString(std::string const& fileContents)
|
||||
std::string("Invalid value 'max_diverged_time' in ") + Sections::kOverlay +
|
||||
": the time must be between 60 and 900 seconds, inclusive.");
|
||||
}
|
||||
|
||||
// Both manifest counts parse and validate identically, so read them
|
||||
// the same way. Returns nullopt when the key is absent, leaving the
|
||||
// built-in default in effect at the use site.
|
||||
auto manifestCount = [&sec](char const* key) -> std::optional<std::size_t> {
|
||||
std::optional<std::size_t> count;
|
||||
|
||||
try
|
||||
{
|
||||
if (auto val = sec.get(key))
|
||||
count = beast::lexicalCastThrow<std::size_t>(*val);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
std::string("Invalid value '") + key + "' in " + Sections::kOverlay +
|
||||
": must be of the form '<number>' representing a count of manifests.");
|
||||
}
|
||||
|
||||
if (count && (*count < kMinManifestCount || *count > kMaxManifestCount))
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
std::string("Invalid value '") + key + "' in " + Sections::kOverlay +
|
||||
": the count must be between " + std::to_string(kMinManifestCount) + " and " +
|
||||
std::to_string(kMaxManifestCount) + ", inclusive.");
|
||||
}
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
maxUntrustedCount = manifestCount(Keys::kMaxUntrustedCount);
|
||||
maxTrustedCount = manifestCount(Keys::kMaxTrustedCount);
|
||||
}
|
||||
|
||||
if (getSingleSection(secConfig, Sections::kAmendmentMajorityTime, strTemp, j_))
|
||||
|
||||
@@ -24,12 +24,37 @@ constexpr std::size_t kMaximumMessageSize = megabytes(64);
|
||||
// so we define a separate limit for them.
|
||||
constexpr std::size_t kMaximumPingMessageSize = kilobytes(1);
|
||||
|
||||
// Upper bound on the wire size of a TMManifests message: kMaxManifestsPerMessage entries
|
||||
// of at most kMaxManifestBytes each, plus a small allowance for protobuf
|
||||
// framing per entry.
|
||||
// Allowance for protobuf framing around each manifest in a TMManifests message.
|
||||
constexpr std::size_t kManifestFramingBytes = 8;
|
||||
constexpr std::size_t kMaximumManifestsMessageSize =
|
||||
kMaxManifestsPerMessage * (kMaxManifestBytes + kManifestFramingBytes);
|
||||
|
||||
/**
|
||||
* Upper bound on the wire size of a TMManifests message.
|
||||
*
|
||||
* Allows both counts' worth of entries at @ref kMaxManifestBytes each, plus
|
||||
* framing per entry. Messages larger than this are dropped before parsing,
|
||||
* which bounds the work an oversized message can cause.
|
||||
*
|
||||
* @param trustedCount Trusted manifests per message.
|
||||
*
|
||||
* @param untrustedCount Untrusted manifests per message.
|
||||
*
|
||||
* @note A node that raises either count accepts larger messages than a peer
|
||||
* running the defaults, and the messages it sends may be dropped by such a
|
||||
* peer. Lowering either count below what peers send drops their manifest
|
||||
* messages, including any trusted key rotations they carry, and the drop
|
||||
* is not recorded on either side.
|
||||
*/
|
||||
constexpr std::size_t
|
||||
maximumManifestsMessageSize(std::size_t const trustedCount, std::size_t const untrustedCount)
|
||||
{
|
||||
return (trustedCount + untrustedCount) * (kMaxManifestBytes + kManifestFramingBytes);
|
||||
}
|
||||
|
||||
// The message size the defaults imply must stay within the overall protocol
|
||||
// message limit. The same check for the largest configurable counts lives in
|
||||
// OverlayImpl.h, where the configured bound is visible.
|
||||
static_assert(
|
||||
maximumManifestsMessageSize(kMaxTrustedCount, kMaxUntrustedCount) < kMaximumMessageSize);
|
||||
|
||||
// VFALCO NOTE If we forward declare Message and write out shared_ptr
|
||||
// instead of using the in-class type alias, we can remove the
|
||||
|
||||
@@ -667,7 +667,10 @@ OverlayImpl::onManifests(
|
||||
auto const& journal = from->pJournal();
|
||||
|
||||
// Process every trusted manifest, but stop processing untrusted ones once
|
||||
// kMaxManifestsPerMessage of them have been handled, so the work stays bounded.
|
||||
// the configured untrusted count has been handled, so the work stays
|
||||
// bounded. Trusted manifests are always processed: dropping one would delay
|
||||
// a validator key rotation reaching this node.
|
||||
auto const maxUntrusted = untrustedManifestCount(app_.config().maxUntrustedCount);
|
||||
auto const total = static_cast<std::size_t>(m->list_size());
|
||||
std::size_t untrusted = 0;
|
||||
bool skippedUntrusted = false;
|
||||
@@ -686,23 +689,18 @@ OverlayImpl::onManifests(
|
||||
// first avoids holding the two locks in opposite orders.
|
||||
bool const isTrusted = app_.getValidators().listed(mo->masterKey);
|
||||
|
||||
// Bound untrusted work: process at most kMaxManifestsPerMessage
|
||||
// untrusted manifests, but never skip a trusted one. Trusted
|
||||
// manifests are not counted against the cap.
|
||||
// Bound untrusted work: process at most maxUntrusted untrusted
|
||||
// manifests, but never skip a trusted one. Trusted manifests are
|
||||
// not counted against the cap.
|
||||
if (!isTrusted)
|
||||
{
|
||||
if (untrusted >= kMaxManifestsPerMessage)
|
||||
if (untrusted >= maxUntrusted)
|
||||
{
|
||||
skippedUntrusted = true;
|
||||
continue;
|
||||
}
|
||||
++untrusted;
|
||||
}
|
||||
// Updates to a known key are relayed even when untrusted. Use
|
||||
// getSequence, not getManifest, to avoid copying the cached payload
|
||||
// on this hot path.
|
||||
bool const isKnown =
|
||||
app_.getValidatorManifests().getSequence(mo->masterKey).has_value();
|
||||
|
||||
auto const result = app_.getValidatorManifests().applyManifest(
|
||||
std::move(*mo),
|
||||
@@ -721,22 +719,17 @@ OverlayImpl::onManifests(
|
||||
"deserialization succeeded");
|
||||
// NOLINTBEGIN(bugprone-unchecked-optional-access) assert above
|
||||
app_.getOPs().pubManifest(*mo);
|
||||
// NOLINTEND(bugprone-unchecked-optional-access)
|
||||
|
||||
relay.add_list()->set_stobject(s);
|
||||
|
||||
// Relay only trusted manifests or updates to known keys, so
|
||||
// untrusted gossip for a brand-new key cannot be amplified.
|
||||
// Persist to the wallet DB only for trusted keys, so untrusted
|
||||
// gossip never survives a restart.
|
||||
if (isTrusted || isKnown)
|
||||
if (isTrusted)
|
||||
{
|
||||
relay.add_list()->set_stobject(s);
|
||||
|
||||
if (isTrusted)
|
||||
{
|
||||
auto db = app_.getWalletDB().checkoutDb();
|
||||
addValidatorManifest(*db, serialized);
|
||||
}
|
||||
auto db = app_.getWalletDB().checkoutDb();
|
||||
addValidatorManifest(*db, serialized);
|
||||
}
|
||||
// NOLINTEND(bugprone-unchecked-optional-access)
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -749,13 +742,12 @@ OverlayImpl::onManifests(
|
||||
if (skippedUntrusted)
|
||||
{
|
||||
// The sender exceeded the untrusted per-message cap. Charge it (once,
|
||||
// here) so a flood of untrusted manifests is penalized, while an honest
|
||||
// message of trusted manifests never is.
|
||||
// here) so a flood of untrusted manifests is penalized.
|
||||
from->charge(Resource::kFeeMalformedRequest, "too many untrusted manifests");
|
||||
|
||||
JLOG(journal.warn()) << "Manifests: message had " << total
|
||||
<< " entries; processed all trusted plus the first "
|
||||
<< kMaxManifestsPerMessage << " untrusted";
|
||||
<< " entries; processed all trusted plus the first " << maxUntrusted
|
||||
<< " untrusted";
|
||||
}
|
||||
|
||||
if (!relay.list().empty())
|
||||
@@ -1283,10 +1275,9 @@ OverlayImpl::getManifestsMessage()
|
||||
});
|
||||
|
||||
// Phase 2: no cache lock held, so trust checks are safe. Include every
|
||||
// trusted manifest, then fill any remaining headroom up to
|
||||
// kMaxManifestsPerMessage with untrusted gossip, so the whole message
|
||||
// stays within the per-message cap the receiver enforces (trusted
|
||||
// count is tiny in practice, so this effectively never drops trusted).
|
||||
// trusted manifest, then fill any remaining headroom up to the
|
||||
// configured untrusted count with gossip. Trusted manifests are never
|
||||
// dropped; the trusted count only sizes the accepted message.
|
||||
std::vector<CachedManifest const*> selected;
|
||||
std::vector<CachedManifest const*> untrusted;
|
||||
for (auto const& e : cached)
|
||||
@@ -1302,7 +1293,8 @@ OverlayImpl::getManifestsMessage()
|
||||
}
|
||||
|
||||
// Cap untrusted only; trusted manifests are all included above.
|
||||
auto const take = std::min(kMaxManifestsPerMessage, untrusted.size());
|
||||
auto const take =
|
||||
std::min(untrustedManifestCount(app_.config().maxUntrustedCount), untrusted.size());
|
||||
selected.insert(selected.end(), untrusted.begin(), untrusted.begin() + take);
|
||||
|
||||
// Shuffle the order. Cryptographic randomness is not needed here.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpld/overlay/Message.h>
|
||||
#include <xrpld/overlay/Overlay.h>
|
||||
#include <xrpld/overlay/Peer.h>
|
||||
@@ -54,6 +55,13 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// The largest counts an operator can configure must still imply a message size
|
||||
// within the overall protocol message limit. The same check for the defaults
|
||||
// lives in Message.h.
|
||||
static_assert(
|
||||
maximumManifestsMessageSize(Config::kMaxManifestCount, Config::kMaxManifestCount) <
|
||||
kMaximumMessageSize);
|
||||
|
||||
class PeerImp;
|
||||
class BasicConfig;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <xrpl/resource/Consumer.h>
|
||||
#include <xrpl/resource/Fees.h>
|
||||
#include <xrpl/server/Handoff.h>
|
||||
#include <xrpl/server/Manifest.h>
|
||||
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
@@ -464,6 +465,21 @@ public:
|
||||
return compressionEnabled_ == Compressed::On;
|
||||
}
|
||||
|
||||
/**
|
||||
* Largest TMManifests message this node accepts, in bytes.
|
||||
*
|
||||
* Read by invokeProtocolMessage to drop oversized messages before
|
||||
* parsing. Not part of the Peer interface: the message handler is a
|
||||
* template parameter, so only PeerImp needs to provide this.
|
||||
*/
|
||||
[[nodiscard]] std::size_t
|
||||
maxManifestsMessageSize() const
|
||||
{
|
||||
return maximumManifestsMessageSize(
|
||||
trustedManifestCount(app_.config().maxTrustedCount),
|
||||
untrustedManifestCount(app_.config().maxUntrustedCount));
|
||||
}
|
||||
|
||||
bool
|
||||
txReduceRelayEnabled() const override
|
||||
{
|
||||
|
||||
@@ -375,13 +375,16 @@ invokeProtocolMessage(Buffers const& buffers, Handler& handler, std::size_t& hin
|
||||
}
|
||||
|
||||
// Drop an oversized TMManifests without penalty: consume the bytes and
|
||||
// return no error, so the connection is preserved.
|
||||
if (header->messageType == protocol::mtMANIFESTS &&
|
||||
(header->payloadWireSize > kMaximumManifestsMessageSize ||
|
||||
header->uncompressedSize > kMaximumManifestsMessageSize))
|
||||
// return no error, so the connection is preserved. The limit follows this
|
||||
// node's configured manifests-per-message count.
|
||||
if (header->messageType == protocol::mtMANIFESTS)
|
||||
{
|
||||
result.first = header->totalWireSize;
|
||||
return result;
|
||||
auto const maxSize = handler.maxManifestsMessageSize();
|
||||
if (header->payloadWireSize > maxSize || header->uncompressedSize > maxSize)
|
||||
{
|
||||
result.first = header->totalWireSize;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
Reference in New Issue
Block a user