Merge remote-tracking branch 'upstream/release/3.3.x' into mathbunnyru/merge-3.3.0-to-develop

* upstream/release/3.3.x: (41 commits)
  chore: Bump version to 3.3.0
  chore: Bump version to 3.3.0-rc7
  fix: Increase manifest protocol message size cap and fix manifests relay
  fix: Cap untrusted manifests per message and drop oversized ones
  chore: Bump version to 3.2.1
  chore: Bump version to 3.2.1-rc1
  fix: Cap untrusted manifests per message and drop oversized ones
  fix: Reject oversized validator manifest before decoding
  fix: Reduce untrusted manifest cache cap to 100
  fix: Bound untrusted manifest cache
  chore: Bump version to 3.3.0-rc6
  feat: Package validator-keys inside rippled
  chore: Bump version to 3.3.0-rc5
  fix: Switch SponsorshipSet to use a delta for sfFeeAmount
  fix: Re-revert "fix: Set request size limits and differential pricing for get-object-by-hash calls"
  chore: Bump version to 3.3.0-rc4
  fix: Revert "fix: Set request size limits and differential pricing for get-object-by-hash calls"
  chore: Bump version to 3.3.0-rc3
  fix: Reduce untrusted manifest cache cap to 100
  fix: Revert "fix: Reject oversized SHAMap nodes in gotStaleData and fetch-pack path"
  ...
This commit is contained in:
Ayaz Salikhov
2026-08-07 16:00:25 +01:00
109 changed files with 4419 additions and 1540 deletions

View File

@@ -38,6 +38,7 @@ set(test_modules
shamap
tx
protocol_autogen
server
)
if(NOT WIN32)
list(APPEND test_modules net)

View File

@@ -32,7 +32,7 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip)
auto const previousTxnIDValue = canonical_UINT256();
auto const previousTxnLgrSeqValue = canonical_UINT32();
auto const domainIDValue = canonical_UINT256();
auto const mutableFlagsValue = canonical_UINT32();
auto const immutableFlagsValue = canonical_UINT32();
auto const referenceHoldingValue = canonical_UINT256();
auto const issuerEncryptionKeyValue = canonical_VL();
auto const auditorEncryptionKeyValue = canonical_VL();
@@ -53,7 +53,7 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip)
builder.setLockedAmount(lockedAmountValue);
builder.setMPTokenMetadata(mPTokenMetadataValue);
builder.setDomainID(domainIDValue);
builder.setMutableFlags(mutableFlagsValue);
builder.setImmutableFlags(immutableFlagsValue);
builder.setReferenceHolding(referenceHoldingValue);
builder.setIssuerEncryptionKey(issuerEncryptionKeyValue);
builder.setAuditorEncryptionKey(auditorEncryptionKeyValue);
@@ -153,11 +153,11 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip)
}
{
auto const& expected = mutableFlagsValue;
auto const actualOpt = entry.getMutableFlags();
auto const& expected = immutableFlagsValue;
auto const actualOpt = entry.getImmutableFlags();
ASSERT_TRUE(actualOpt.has_value());
expectEqualField(expected, *actualOpt, "sfMutableFlags");
EXPECT_TRUE(entry.hasMutableFlags());
expectEqualField(expected, *actualOpt, "sfImmutableFlags");
EXPECT_TRUE(entry.hasImmutableFlags());
}
{
@@ -217,7 +217,7 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip)
auto const previousTxnIDValue = canonical_UINT256();
auto const previousTxnLgrSeqValue = canonical_UINT32();
auto const domainIDValue = canonical_UINT256();
auto const mutableFlagsValue = canonical_UINT32();
auto const immutableFlagsValue = canonical_UINT32();
auto const referenceHoldingValue = canonical_UINT256();
auto const issuerEncryptionKeyValue = canonical_VL();
auto const auditorEncryptionKeyValue = canonical_VL();
@@ -237,7 +237,7 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip)
sle->at(sfPreviousTxnID) = previousTxnIDValue;
sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue;
sle->at(sfDomainID) = domainIDValue;
sle->at(sfMutableFlags) = mutableFlagsValue;
sle->at(sfImmutableFlags) = immutableFlagsValue;
sle->at(sfReferenceHolding) = referenceHoldingValue;
sle->at(sfIssuerEncryptionKey) = issuerEncryptionKeyValue;
sle->at(sfAuditorEncryptionKey) = auditorEncryptionKeyValue;
@@ -391,16 +391,16 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip)
}
{
auto const& expected = mutableFlagsValue;
auto const& expected = immutableFlagsValue;
auto const fromSleOpt = entryFromSle.getMutableFlags();
auto const fromBuilderOpt = entryFromBuilder.getMutableFlags();
auto const fromSleOpt = entryFromSle.getImmutableFlags();
auto const fromBuilderOpt = entryFromBuilder.getImmutableFlags();
ASSERT_TRUE(fromSleOpt.has_value());
ASSERT_TRUE(fromBuilderOpt.has_value());
expectEqualField(expected, *fromSleOpt, "sfMutableFlags");
expectEqualField(expected, *fromBuilderOpt, "sfMutableFlags");
expectEqualField(expected, *fromSleOpt, "sfImmutableFlags");
expectEqualField(expected, *fromBuilderOpt, "sfImmutableFlags");
}
{
@@ -531,8 +531,8 @@ TEST(MPTokenIssuanceTests, OptionalFieldsReturnNullopt)
EXPECT_FALSE(entry.getMPTokenMetadata().has_value());
EXPECT_FALSE(entry.hasDomainID());
EXPECT_FALSE(entry.getDomainID().has_value());
EXPECT_FALSE(entry.hasMutableFlags());
EXPECT_FALSE(entry.getMutableFlags().has_value());
EXPECT_FALSE(entry.hasImmutableFlags());
EXPECT_FALSE(entry.getImmutableFlags().has_value());
EXPECT_FALSE(entry.hasReferenceHolding());
EXPECT_FALSE(entry.getReferenceHolding().has_value());
EXPECT_FALSE(entry.hasIssuerEncryptionKey());

View File

@@ -34,7 +34,7 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderSettersRoundTrip)
auto const maximumAmountValue = canonical_UINT64();
auto const mPTokenMetadataValue = canonical_VL();
auto const domainIDValue = canonical_UINT256();
auto const mutableFlagsValue = canonical_UINT32();
auto const immutableFlagsValue = canonical_UINT32();
MPTokenIssuanceCreateBuilder builder{
accountValue,
@@ -48,7 +48,7 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderSettersRoundTrip)
builder.setMaximumAmount(maximumAmountValue);
builder.setMPTokenMetadata(mPTokenMetadataValue);
builder.setDomainID(domainIDValue);
builder.setMutableFlags(mutableFlagsValue);
builder.setImmutableFlags(immutableFlagsValue);
auto tx = builder.build(publicKey, secretKey);
@@ -107,11 +107,11 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderSettersRoundTrip)
}
{
auto const& expected = mutableFlagsValue;
auto const actualOpt = tx.getMutableFlags();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present";
expectEqualField(expected, *actualOpt, "sfMutableFlags");
EXPECT_TRUE(tx.hasMutableFlags());
auto const& expected = immutableFlagsValue;
auto const actualOpt = tx.getImmutableFlags();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfImmutableFlags should be present";
expectEqualField(expected, *actualOpt, "sfImmutableFlags");
EXPECT_TRUE(tx.hasImmutableFlags());
}
}
@@ -135,7 +135,7 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderFromStTxRoundTrip)
auto const maximumAmountValue = canonical_UINT64();
auto const mPTokenMetadataValue = canonical_VL();
auto const domainIDValue = canonical_UINT256();
auto const mutableFlagsValue = canonical_UINT32();
auto const immutableFlagsValue = canonical_UINT32();
// Build an initial transaction
MPTokenIssuanceCreateBuilder initialBuilder{
@@ -149,7 +149,7 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderFromStTxRoundTrip)
initialBuilder.setMaximumAmount(maximumAmountValue);
initialBuilder.setMPTokenMetadata(mPTokenMetadataValue);
initialBuilder.setDomainID(domainIDValue);
initialBuilder.setMutableFlags(mutableFlagsValue);
initialBuilder.setImmutableFlags(immutableFlagsValue);
auto initialTx = initialBuilder.build(publicKey, secretKey);
@@ -204,10 +204,10 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderFromStTxRoundTrip)
}
{
auto const& expected = mutableFlagsValue;
auto const actualOpt = rebuiltTx.getMutableFlags();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present";
expectEqualField(expected, *actualOpt, "sfMutableFlags");
auto const& expected = immutableFlagsValue;
auto const actualOpt = rebuiltTx.getImmutableFlags();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfImmutableFlags should be present";
expectEqualField(expected, *actualOpt, "sfImmutableFlags");
}
}
@@ -275,8 +275,8 @@ TEST(TransactionsMPTokenIssuanceCreateTests, OptionalFieldsReturnNullopt)
EXPECT_FALSE(tx.getMPTokenMetadata().has_value());
EXPECT_FALSE(tx.hasDomainID());
EXPECT_FALSE(tx.getDomainID().has_value());
EXPECT_FALSE(tx.hasMutableFlags());
EXPECT_FALSE(tx.getMutableFlags().has_value());
EXPECT_FALSE(tx.hasImmutableFlags());
EXPECT_FALSE(tx.getImmutableFlags().has_value());
}
}

View File

@@ -34,7 +34,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip)
auto const domainIDValue = canonical_UINT256();
auto const mPTokenMetadataValue = canonical_VL();
auto const transferFeeValue = canonical_UINT16();
auto const mutableFlagsValue = canonical_UINT32();
auto const immutableFlagsValue = canonical_UINT32();
auto const issuerEncryptionKeyValue = canonical_VL();
auto const auditorEncryptionKeyValue = canonical_VL();
@@ -50,7 +50,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip)
builder.setDomainID(domainIDValue);
builder.setMPTokenMetadata(mPTokenMetadataValue);
builder.setTransferFee(transferFeeValue);
builder.setMutableFlags(mutableFlagsValue);
builder.setImmutableFlags(immutableFlagsValue);
builder.setIssuerEncryptionKey(issuerEncryptionKeyValue);
builder.setAuditorEncryptionKey(auditorEncryptionKeyValue);
@@ -109,11 +109,11 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip)
}
{
auto const& expected = mutableFlagsValue;
auto const actualOpt = tx.getMutableFlags();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present";
expectEqualField(expected, *actualOpt, "sfMutableFlags");
EXPECT_TRUE(tx.hasMutableFlags());
auto const& expected = immutableFlagsValue;
auto const actualOpt = tx.getImmutableFlags();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfImmutableFlags should be present";
expectEqualField(expected, *actualOpt, "sfImmutableFlags");
EXPECT_TRUE(tx.hasImmutableFlags());
}
{
@@ -153,7 +153,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip)
auto const domainIDValue = canonical_UINT256();
auto const mPTokenMetadataValue = canonical_VL();
auto const transferFeeValue = canonical_UINT16();
auto const mutableFlagsValue = canonical_UINT32();
auto const immutableFlagsValue = canonical_UINT32();
auto const issuerEncryptionKeyValue = canonical_VL();
auto const auditorEncryptionKeyValue = canonical_VL();
@@ -169,7 +169,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip)
initialBuilder.setDomainID(domainIDValue);
initialBuilder.setMPTokenMetadata(mPTokenMetadataValue);
initialBuilder.setTransferFee(transferFeeValue);
initialBuilder.setMutableFlags(mutableFlagsValue);
initialBuilder.setImmutableFlags(immutableFlagsValue);
initialBuilder.setIssuerEncryptionKey(issuerEncryptionKeyValue);
initialBuilder.setAuditorEncryptionKey(auditorEncryptionKeyValue);
@@ -225,10 +225,10 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip)
}
{
auto const& expected = mutableFlagsValue;
auto const actualOpt = rebuiltTx.getMutableFlags();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present";
expectEqualField(expected, *actualOpt, "sfMutableFlags");
auto const& expected = immutableFlagsValue;
auto const actualOpt = rebuiltTx.getImmutableFlags();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfImmutableFlags should be present";
expectEqualField(expected, *actualOpt, "sfImmutableFlags");
}
{
@@ -310,8 +310,8 @@ TEST(TransactionsMPTokenIssuanceSetTests, OptionalFieldsReturnNullopt)
EXPECT_FALSE(tx.getMPTokenMetadata().has_value());
EXPECT_FALSE(tx.hasTransferFee());
EXPECT_FALSE(tx.getTransferFee().has_value());
EXPECT_FALSE(tx.hasMutableFlags());
EXPECT_FALSE(tx.getMutableFlags().has_value());
EXPECT_FALSE(tx.hasImmutableFlags());
EXPECT_FALSE(tx.getImmutableFlags().has_value());
EXPECT_FALSE(tx.hasIssuerEncryptionKey());
EXPECT_FALSE(tx.getIssuerEncryptionKey().has_value());
EXPECT_FALSE(tx.hasAuditorEncryptionKey());

View File

@@ -31,9 +31,9 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip)
// Transaction-specific field values
auto const counterpartySponsorValue = canonical_ACCOUNT();
auto const sponseeValue = canonical_ACCOUNT();
auto const feeAmountValue = canonical_AMOUNT();
auto const feeAmountDeltaValue = canonical_AMOUNT();
auto const maxFeeValue = canonical_AMOUNT();
auto const remainingOwnerCountValue = canonical_UINT32();
auto const remainingOwnerCountDeltaValue = canonical_INT32();
SponsorshipSetBuilder builder{
accountValue,
@@ -44,9 +44,9 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip)
// Set optional fields
builder.setCounterpartySponsor(counterpartySponsorValue);
builder.setSponsee(sponseeValue);
builder.setFeeAmount(feeAmountValue);
builder.setFeeAmountDelta(feeAmountDeltaValue);
builder.setMaxFee(maxFeeValue);
builder.setRemainingOwnerCount(remainingOwnerCountValue);
builder.setRemainingOwnerCountDelta(remainingOwnerCountDeltaValue);
auto tx = builder.build(publicKey, secretKey);
@@ -81,11 +81,11 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip)
}
{
auto const& expected = feeAmountValue;
auto const actualOpt = tx.getFeeAmount();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFeeAmount should be present";
expectEqualField(expected, *actualOpt, "sfFeeAmount");
EXPECT_TRUE(tx.hasFeeAmount());
auto const& expected = feeAmountDeltaValue;
auto const actualOpt = tx.getFeeAmountDelta();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFeeAmountDelta should be present";
expectEqualField(expected, *actualOpt, "sfFeeAmountDelta");
EXPECT_TRUE(tx.hasFeeAmountDelta());
}
{
@@ -97,11 +97,11 @@ TEST(TransactionsSponsorshipSetTests, BuilderSettersRoundTrip)
}
{
auto const& expected = remainingOwnerCountValue;
auto const actualOpt = tx.getRemainingOwnerCount();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRemainingOwnerCount should be present";
expectEqualField(expected, *actualOpt, "sfRemainingOwnerCount");
EXPECT_TRUE(tx.hasRemainingOwnerCount());
auto const& expected = remainingOwnerCountDeltaValue;
auto const actualOpt = tx.getRemainingOwnerCountDelta();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRemainingOwnerCountDelta should be present";
expectEqualField(expected, *actualOpt, "sfRemainingOwnerCountDelta");
EXPECT_TRUE(tx.hasRemainingOwnerCountDelta());
}
}
@@ -122,9 +122,9 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip)
// Transaction-specific field values
auto const counterpartySponsorValue = canonical_ACCOUNT();
auto const sponseeValue = canonical_ACCOUNT();
auto const feeAmountValue = canonical_AMOUNT();
auto const feeAmountDeltaValue = canonical_AMOUNT();
auto const maxFeeValue = canonical_AMOUNT();
auto const remainingOwnerCountValue = canonical_UINT32();
auto const remainingOwnerCountDeltaValue = canonical_INT32();
// Build an initial transaction
SponsorshipSetBuilder initialBuilder{
@@ -135,9 +135,9 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip)
initialBuilder.setCounterpartySponsor(counterpartySponsorValue);
initialBuilder.setSponsee(sponseeValue);
initialBuilder.setFeeAmount(feeAmountValue);
initialBuilder.setFeeAmountDelta(feeAmountDeltaValue);
initialBuilder.setMaxFee(maxFeeValue);
initialBuilder.setRemainingOwnerCount(remainingOwnerCountValue);
initialBuilder.setRemainingOwnerCountDelta(remainingOwnerCountDeltaValue);
auto initialTx = initialBuilder.build(publicKey, secretKey);
@@ -171,10 +171,10 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip)
}
{
auto const& expected = feeAmountValue;
auto const actualOpt = rebuiltTx.getFeeAmount();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFeeAmount should be present";
expectEqualField(expected, *actualOpt, "sfFeeAmount");
auto const& expected = feeAmountDeltaValue;
auto const actualOpt = rebuiltTx.getFeeAmountDelta();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfFeeAmountDelta should be present";
expectEqualField(expected, *actualOpt, "sfFeeAmountDelta");
}
{
@@ -185,10 +185,10 @@ TEST(TransactionsSponsorshipSetTests, BuilderFromStTxRoundTrip)
}
{
auto const& expected = remainingOwnerCountValue;
auto const actualOpt = rebuiltTx.getRemainingOwnerCount();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRemainingOwnerCount should be present";
expectEqualField(expected, *actualOpt, "sfRemainingOwnerCount");
auto const& expected = remainingOwnerCountDeltaValue;
auto const actualOpt = rebuiltTx.getRemainingOwnerCountDelta();
ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfRemainingOwnerCountDelta should be present";
expectEqualField(expected, *actualOpt, "sfRemainingOwnerCountDelta");
}
}
@@ -250,12 +250,12 @@ TEST(TransactionsSponsorshipSetTests, OptionalFieldsReturnNullopt)
EXPECT_FALSE(tx.getCounterpartySponsor().has_value());
EXPECT_FALSE(tx.hasSponsee());
EXPECT_FALSE(tx.getSponsee().has_value());
EXPECT_FALSE(tx.hasFeeAmount());
EXPECT_FALSE(tx.getFeeAmount().has_value());
EXPECT_FALSE(tx.hasFeeAmountDelta());
EXPECT_FALSE(tx.getFeeAmountDelta().has_value());
EXPECT_FALSE(tx.hasMaxFee());
EXPECT_FALSE(tx.getMaxFee().has_value());
EXPECT_FALSE(tx.hasRemainingOwnerCount());
EXPECT_FALSE(tx.getRemainingOwnerCount().has_value());
EXPECT_FALSE(tx.hasRemainingOwnerCountDelta());
EXPECT_FALSE(tx.getRemainingOwnerCountDelta().has_value());
}
}

View File

@@ -0,0 +1,60 @@
#include <xrpl/server/InfoSub.h>
#include <gtest/gtest.h>
#include <cstddef>
#include <limits>
using namespace xrpl;
// The per-connection subscription cap is enforced by the pure predicate
// exceedsSubscriptionCap(current, additional). Testing it directly (rather than
// by subscribing the real cap through a WebSocket, which would exceed the frame
// limit and drop the connection before the check runs) lets the boundary be
// asserted exactly.
TEST(InfoSubSubscriptionCap, Boundary)
{
constexpr std::size_t cap = kMaxSubscriptionsPerConnection;
// Empty connection: anything up to the cap is admitted, cap+1 is not.
EXPECT_FALSE(exceedsSubscriptionCap(0, 0));
EXPECT_FALSE(exceedsSubscriptionCap(0, cap));
EXPECT_TRUE(exceedsSubscriptionCap(0, cap + 1));
// Exactly at the cap: zero more is fine, one more is rejected.
EXPECT_FALSE(exceedsSubscriptionCap(cap, 0));
EXPECT_TRUE(exceedsSubscriptionCap(cap, 1));
// One below the cap: exactly one more reaches the cap; two exceed it.
EXPECT_FALSE(exceedsSubscriptionCap(cap - 1, 1));
EXPECT_TRUE(exceedsSubscriptionCap(cap - 1, 2));
}
TEST(InfoSubSubscriptionCap, NoOverflow)
{
constexpr std::size_t cap = kMaxSubscriptionsPerConnection;
constexpr std::size_t max = std::numeric_limits<std::size_t>::max();
// current + additional must not wrap: a huge additional is rejected even
// when current is 0 (the additional > cap term guards the subtraction).
EXPECT_TRUE(exceedsSubscriptionCap(0, max));
EXPECT_TRUE(exceedsSubscriptionCap(cap, max));
}
TEST(InfoSubSubscriptionCap, ExplicitCap)
{
// A configured override is honored: the boundary tracks the passed cap, not
// the built-in default. This is the seam doSubscribe uses to enforce a
// per-connection cap set via [max_subscriptions_per_connection].
constexpr std::size_t cap = 5;
EXPECT_FALSE(exceedsSubscriptionCap(0, cap, cap));
EXPECT_TRUE(exceedsSubscriptionCap(0, cap + 1, cap));
EXPECT_FALSE(exceedsSubscriptionCap(cap, 0, cap));
EXPECT_TRUE(exceedsSubscriptionCap(cap, 1, cap));
EXPECT_FALSE(exceedsSubscriptionCap(cap - 1, 1, cap));
EXPECT_TRUE(exceedsSubscriptionCap(cap - 1, 2, cap));
// The overflow guard still holds with a small explicit cap.
EXPECT_TRUE(exceedsSubscriptionCap(0, std::numeric_limits<std::size_t>::max(), cap));
}