#pragma once #include #include #include #include #include #include /** * @page Feature How to add new features * * Steps required to add new features to the code: * * 1) Add the appropriate XRPL_FEATURE or XRPL_FIX macro definition for the * feature to features.macro with the feature's name, `Supported::no`, and * `VoteBehavior::DefaultNo`. * * 2) Use the generated variable name as the parameter to `view.rules.enabled()` * to control flow into new code that this feature limits. (featureName or * fixName) * * 3) If the feature development is COMPLETE, and the feature is ready to be * SUPPORTED, change the macro parameter in features.macro to Supported::yes. * * 4) In general, any newly supported amendments (`Supported::yes`) should have * a `VoteBehavior::DefaultNo` indefinitely so that external governance can * make the decision on when to activate it. High priority bug fixes can be * an exception to this rule. In such cases, ensure the fix has been * clearly communicated to the community using appropriate channels, * then change the macro parameter in features.macro to * `VoteBehavior::DefaultYes`. The communication process is beyond * the scope of these instructions. * 5) If a supported feature (`Supported::yes`) was _ever_ in a released * version, it can never be changed back to `Supported::no`, because * it _may_ still become enabled at any time. This would cause newer * versions of `xrpld` to become amendment blocked. * Instead, to prevent newer versions from voting on the feature, use * `VoteBehavior::Obsolete`. Obsolete features can not be voted for * by any versions of `xrpld` built with that setting, but will still * work correctly if they get enabled. If a feature remains obsolete * for long enough that _all_ clients that could vote for it are * amendment blocked, the feature can be removed from the code * as if it was unsupported. * * * When a feature has been enabled for several years, the conditional code * may be removed, and the feature "retired". To retire a feature: * * 1) MOVE the macro definition in features.macro to the "retired features" * section at the end of the file, and change the macro to XRPL_RETIRE. * * The feature must remain registered and supported indefinitely because it * may exist in the Amendments object on ledger. There is no need to vote * for it because there's nothing to vote for. If the feature definition is * removed completely from the code, any instances running that code will get * amendment blocked. Removing the feature from the ledger is beyond the scope * of these instructions. * */ 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; // Both validFeatureNameSize and validFeatureName are consteval functions that can be used in // static_asserts to validate feature names at compile time. They are only used inside // enforceValidFeatureName in Feature.cpp, but are exposed here for testing. The expected // parameter `auto fn` is a constexpr lambda which returns a const char*, making it available // for compile-time evaluation. Read more in https://accu.org/journals/overload/30/172/wu/ consteval auto validFeatureNameSize(auto fn) -> bool { constexpr char const* n = fn(); // Note, std::strlen is not constexpr, we need to implement our own here. constexpr std::size_t N = [](auto n) { std::size_t ret = 0; for (auto ptr = n; *ptr != '\0'; ret++, ++ptr) ; return ret; }(n); return N != reservedFeatureNameSize && // N <= maxFeatureNameSize; } consteval auto validFeatureName(auto fn) -> bool { constexpr char const* n = fn(); // Prevent the use of visually confusable characters and enforce that feature names // are always valid ASCII. This is needed because C++ allows Unicode identifiers. // Characters below 0x20 are nonprintable control characters, and characters with the 0x80 bit // set are non-ASCII (e.g. UTF-8 encoding of Unicode), so both are disallowed. for (auto ptr = n; *ptr != '\0'; ++ptr) { if (*ptr & 0x80 || *ptr < 0x20) return false; } return true; } enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes = 1 }; enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported = 1 }; /** All amendments libxrpl knows about. */ std::map const& allAmendments(); namespace detail { #pragma push_macro("XRPL_FEATURE") #undef XRPL_FEATURE #pragma push_macro("XRPL_FIX") #undef XRPL_FIX #pragma push_macro("XRPL_RETIRE_FEATURE") #undef XRPL_RETIRE_FEATURE #pragma push_macro("XRPL_RETIRE_FIX") #undef XRPL_RETIRE_FIX // NOLINTBEGIN(bugprone-macro-parentheses) #define XRPL_FEATURE(name, supported, vote) +1 #define XRPL_FIX(name, supported, vote) +1 #define XRPL_RETIRE_FEATURE(name) +1 #define XRPL_RETIRE_FIX(name) +1 // NOLINTEND(bugprone-macro-parentheses) // This value SHOULD be equal to the number of amendments registered in // Feature.cpp. Because it's only used to reserve storage, and determine how // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // the actual number of amendments. A LogicError on startup will verify this. static constexpr std::size_t numFeatures = (0 + #include ); #undef XRPL_RETIRE_FEATURE #pragma pop_macro("XRPL_RETIRE_FEATURE") #undef XRPL_RETIRE_FIX #pragma pop_macro("XRPL_RETIRE_FIX") #undef XRPL_FIX #pragma pop_macro("XRPL_FIX") #undef XRPL_FEATURE #pragma pop_macro("XRPL_FEATURE") /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated ledger */ std::map const& supportedAmendments(); /** Amendments that this server won't vote for by default. This function is only used in unit tests. */ std::size_t numDownVotedAmendments(); /** Amendments that this server will vote for by default. This function is only used in unit tests. */ std::size_t numUpVotedAmendments(); } // namespace detail std::optional getRegisteredFeature(std::string const& name); size_t featureToBitsetIndex(uint256 const& f); uint256 bitsetIndexToFeature(size_t i); std::string featureToName(uint256 const& f); class FeatureBitset : private std::bitset { using base = std::bitset; template void initFromFeatures(uint256 const& f, Fs&&... fs) { set(f); if constexpr (sizeof...(fs) > 0) initFromFeatures(std::forward(fs)...); } public: using base::bitset; using base::operator==; using base::all; using base::any; using base::count; using base::flip; using base::none; using base::reset; using base::set; using base::size; using base::test; using base::operator[]; using base::to_string; using base::to_ullong; using base::to_ulong; FeatureBitset() = default; explicit FeatureBitset(base const& b) : base(b) { XRPL_ASSERT(b.count() == count(), "xrpl::FeatureBitset::FeatureBitset(base) : count match"); } template explicit FeatureBitset(uint256 const& f, Fs&&... fs) { initFromFeatures(f, std::forward(fs)...); XRPL_ASSERT( count() == (sizeof...(fs) + 1), "xrpl::FeatureBitset::FeatureBitset(uint256) : count and " "sizeof... do match"); } template explicit FeatureBitset(Col const& fs) { for (auto const& f : fs) set(featureToBitsetIndex(f)); XRPL_ASSERT( fs.size() == count(), "xrpl::FeatureBitset::FeatureBitset(Container auto) : count and " "size do match"); } auto operator[](uint256 const& f) { return base::operator[](featureToBitsetIndex(f)); } auto operator[](uint256 const& f) const { return base::operator[](featureToBitsetIndex(f)); } FeatureBitset& set(uint256 const& f, bool value = true) { base::set(featureToBitsetIndex(f), value); return *this; } FeatureBitset& reset(uint256 const& f) { base::reset(featureToBitsetIndex(f)); return *this; } FeatureBitset& flip(uint256 const& f) { base::flip(featureToBitsetIndex(f)); return *this; } FeatureBitset& operator&=(FeatureBitset const& rhs) { base::operator&=(rhs); return *this; } FeatureBitset& operator|=(FeatureBitset const& rhs) { base::operator|=(rhs); return *this; } FeatureBitset operator~() const { return FeatureBitset{base::operator~()}; } friend FeatureBitset operator&(FeatureBitset const& lhs, FeatureBitset const& rhs) { return FeatureBitset{static_cast(lhs) & static_cast(rhs)}; } friend FeatureBitset operator&(FeatureBitset const& lhs, uint256 const& rhs) { return lhs & FeatureBitset{rhs}; } friend FeatureBitset operator&(uint256 const& lhs, FeatureBitset const& rhs) { return FeatureBitset{lhs} & rhs; } friend FeatureBitset operator|(FeatureBitset const& lhs, FeatureBitset const& rhs) { return FeatureBitset{static_cast(lhs) | static_cast(rhs)}; } friend FeatureBitset operator|(FeatureBitset const& lhs, uint256 const& rhs) { return lhs | FeatureBitset{rhs}; } friend FeatureBitset operator|(uint256 const& lhs, FeatureBitset const& rhs) { return FeatureBitset{lhs} | rhs; } friend FeatureBitset operator^(FeatureBitset const& lhs, FeatureBitset const& rhs) { return FeatureBitset{static_cast(lhs) ^ static_cast(rhs)}; } friend FeatureBitset operator^(FeatureBitset const& lhs, uint256 const& rhs) { return lhs ^ FeatureBitset{rhs}; } friend FeatureBitset operator^(uint256 const& lhs, FeatureBitset const& rhs) { return FeatureBitset{lhs} ^ rhs; } // set difference friend FeatureBitset operator-(FeatureBitset const& lhs, FeatureBitset const& rhs) { return lhs & ~rhs; } friend FeatureBitset operator-(FeatureBitset const& lhs, uint256 const& rhs) { return lhs - FeatureBitset{rhs}; } friend FeatureBitset operator-(uint256 const& lhs, FeatureBitset const& rhs) { return FeatureBitset{lhs} - rhs; } }; template void foreachFeature(FeatureBitset bs, F&& f) { for (size_t i = 0; i < bs.size(); ++i) { if (bs[i]) f(bitsetIndexToFeature(i)); } } #pragma push_macro("XRPL_FEATURE") #undef XRPL_FEATURE #pragma push_macro("XRPL_FIX") #undef XRPL_FIX #pragma push_macro("XRPL_RETIRE_FEATURE") #undef XRPL_RETIRE_FEATURE #pragma push_macro("XRPL_RETIRE_FIX") #undef XRPL_RETIRE_FIX #define XRPL_FEATURE(name, supported, vote) extern uint256 const feature##name; #define XRPL_FIX(name, supported, vote) extern uint256 const fix##name; #define XRPL_RETIRE_FEATURE(name) #define XRPL_RETIRE_FIX(name) #include #undef XRPL_RETIRE_FEATURE #pragma pop_macro("XRPL_RETIRE_FEATURE") #undef XRPL_RETIRE_FIX #pragma pop_macro("XRPL_RETIRE_FIX") #undef XRPL_FIX #pragma pop_macro("XRPL_FIX") #undef XRPL_FEATURE #pragma pop_macro("XRPL_FEATURE") } // namespace xrpl