Compare commits

...

11 Commits

Author SHA1 Message Date
Bronek Kozicki
87d0527b93 Merge branch 'develop' into Bronek/maximum_feature_name_size 2026-03-06 18:55:19 +00:00
Bronek Kozicki
f6153e4d74 Add -1 to account for the string terminator 2026-03-06 18:55:06 +00:00
Bronek Kozicki
5d11334b2d Improve comments in Feature.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-06 17:24:59 +00:00
Bart
1650258fc1 Merge branch 'develop' into Bronek/maximum_feature_name_size 2026-03-03 15:43:11 -05:00
Ed Hennis
72cee507b5 Fix formatting 2026-03-03 14:33:16 -05:00
Ed Hennis
7c9a77702f Merge branch 'develop' into Bronek/maximum_feature_name_size 2026-03-03 15:11:56 -04:00
Bronek Kozicki
ba2ac69f55 Add hardcoded hash test back 2025-07-14 17:32:15 +01:00
Bronek Kozicki
242b11f314 For discussion 2025-07-14 11:03:20 +01:00
Bronek Kozicki
6af70476e8 Merge branch 'develop' into Bronek/maximum_feature_name_size 2025-07-14 10:38:39 +01:00
Bronek Kozicki
4152dc53ba Shrink to 31 bytes, enforced in compilation 2025-07-11 15:00:39 +01:00
Bronek Kozicki
eb95da9cd3 Enforce maximum feature name size 2025-07-11 14:24:31 +01:00
3 changed files with 56 additions and 7 deletions

View File

@@ -64,6 +64,24 @@
namespace xrpl {
// Feature names must not exceed this length (in characters, excluding the null terminator).
static constexpr std::size_t maxFeatureNameSize = 63;
// Reserve this exact feature-name length (in characters/bytes, excluding the null terminator)
// so that a 32-byte uint256 (for example, in WASM or other interop contexts) can be used
// as a compact, fixed-size feature selector without conflicting with human-readable names.
static constexpr std::size_t reservedFeatureNameSize = 32;
// Exposed for testing, used only by enforceValidFeatureNameSize in Feature.cpp
template <std::size_t N>
constexpr auto
validFeatureNameSize(char const (*)[N]) -> bool
{
// The -1 is to account for the null terminator in the string literal.
return N - 1 != reservedFeatureNameSize && //
N - 1 != reservedFeatureNameSize + 1 && //
N - 1 <= maxFeatureNameSize;
}
enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes };
enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported };

View File

@@ -395,10 +395,20 @@ featureToName(uint256 const& f)
#pragma push_macro("XRPL_RETIRE_FIX")
#undef XRPL_RETIRE_FIX
template <std::size_t N>
constexpr auto
enforceValidFeatureNameSize(char const (&n)[N]) -> char const*
{
static_assert(validFeatureNameSize<N>(nullptr), "Invalid feature name size");
return n;
}
#define XRPL_FEATURE(name, supported, vote) \
uint256 const feature##name = registerFeature(#name, supported, vote);
uint256 const feature##name = \
registerFeature(enforceValidFeatureNameSize(#name), supported, vote);
#define XRPL_FIX(name, supported, vote) \
uint256 const fix##name = registerFeature("fix" #name, supported, vote);
uint256 const fix##name = \
registerFeature(enforceValidFeatureNameSize("fix" #name), supported, vote);
// clang-format off
#define XRPL_RETIRE_FEATURE(name) \

View File

@@ -2,6 +2,7 @@
#include <xrpl/ledger/AmendmentTable.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/jss.h>
namespace xrpl {
@@ -168,16 +169,18 @@ class Feature_test : public beast::unit_test::suite
using namespace test::jtx;
Env env{*this};
auto jrr = env.rpc("feature", "fixAMMOverflowOffer")[jss::result];
std::string const name = "fixAMMOverflowOffer";
auto jrr = env.rpc("feature", name)[jss::result];
BEAST_EXPECTS(jrr[jss::status] == jss::success, "status");
jrr.removeMember(jss::status);
BEAST_EXPECT(jrr.size() == 1);
BEAST_EXPECT(jrr.isMember(
"12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E"
"2A87F1D8107"));
auto const expected = to_string(sha512Half(Slice(name.data(), name.size())));
char const sha[] = "12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E2A87F1D8107";
BEAST_EXPECT(expected == sha);
BEAST_EXPECT(jrr.isMember(expected));
auto feature = *(jrr.begin());
BEAST_EXPECTS(feature[jss::name] == "fixAMMOverflowOffer", "name");
BEAST_EXPECTS(feature[jss::name] == name, "name");
BEAST_EXPECTS(!feature[jss::enabled].asBool(), "enabled");
BEAST_EXPECTS(feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(), "vetoed");
BEAST_EXPECTS(feature[jss::supported].asBool(), "supported");
@@ -186,6 +189,24 @@ class Feature_test : public beast::unit_test::suite
jrr = env.rpc("feature", "fMM")[jss::result];
BEAST_EXPECT(jrr[jss::error] == "badFeature");
BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
// Test feature name size checks
char const ok63Name[] = "123456789012345678901234567890123456789012345678901234567890123";
static_assert(validFeatureNameSize(&ok63Name));
char const bad64Name[] = "1234567890123456789012345678901234567890123456789012345678901234";
static_assert(!validFeatureNameSize(&bad64Name));
char const ok31Name[] = "1234567890123456789012345678901";
static_assert(validFeatureNameSize(&ok31Name));
char const bad32Name[] = "12345678901234567890123456789012";
static_assert(!validFeatureNameSize(&bad32Name));
char const bad33Name[] = "123456789012345678901234567890123";
static_assert(!validFeatureNameSize(&bad33Name));
char const ok34Name[] = "1234567890123456789012345678901234";
static_assert(validFeatureNameSize(&ok34Name));
}
void