#include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { namespace { // Use a static inside a function to help prevent order-of-initialization issues LocalValue>& getCurrentTransactionRulesRef() { static LocalValue> kR; return kR; } } // namespace std::optional const& getCurrentTransactionRules() { return *getCurrentTransactionRulesRef(); } void setCurrentTransactionRules(std::optional r) { // Make global changes associated with the rules before the value is moved. // Push the appropriate setting, instead of having the class pull every time // the value is needed. That could get expensive fast. // Declare the range this way to keep clang-tidy from complaining auto const range = [&r]() { // If any new conditions with new amendments are added to "enableLargeNumbers", those // amendments must also be added to useRulesGuards. bool const enableLargeNumbers = !r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol)); // If enableLargeNumbers is true, then useRulesGuard must also return true. // However, the reverse is not true. Other amendments can cause the rules guard to be used, // even though large numbers are _not_ used. XRPL_ASSERT( !r || !enableLargeNumbers || useRulesGuards(*r), "setCurrentTransactionRules : rule decisions match"); if (enableLargeNumbers) { static_assert( MantissaRange::MantissaScale::Large == MantissaRange::MantissaScale::Large330); if (!r || r->enabled(fixCleanup3_3_0)) { return MantissaRange::MantissaScale::Large330; } if (r->enabled(fixCleanup3_2_0)) { return MantissaRange::MantissaScale::Large320; } return MantissaRange::MantissaScale::LargeLegacy; } return MantissaRange::MantissaScale::Small; }(); Number::setMantissaScale(range); *getCurrentTransactionRulesRef() = std::move(r); } bool useRulesGuards(Rules const& rules) { // The list of amendments used here - to decide whether to create a RulesGuard - must be a // superset of the list used to determine "enableLargeNumbers" in setCurrentTransactionRules. // Additional amendments can be added if desired. // // As soon as any one of these amendments is retired, this whole function can be removed, along // with createGuards, and any other callers, and the first set of guards can be created directly // at the call site, without using optional. return rules.enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol) || rules.enabled(fixCleanup3_2_0) || rules.enabled(fixCleanup3_3_0); } void createGuards( Rules const& rules, std::optional& rulesGuard, std::optional& mantissaScaleGuard) { if (useRulesGuards(rules)) { // raii classes for the current ledger rules. If the rules are set, the MantissaRange will // be updated, too. rulesGuard.emplace(rules); } else { // Without those features enabled, always use the old number rules. mantissaScaleGuard.emplace(MantissaRange::MantissaScale::Small); } } class Rules::Impl { private: std::unordered_set> set_; std::optional digest_; std::unordered_set> const& presets_; public: explicit Impl(std::unordered_set> const& presets) : presets_(presets) { } Impl( std::unordered_set> const& presets, std::optional const& digest, STVector256 const& amendments) : digest_(digest), presets_(presets) { set_.reserve(amendments.size()); set_.insert(amendments.begin(), amendments.end()); } [[nodiscard]] std::unordered_set> const& presets() const { return presets_; } [[nodiscard]] bool enabled(uint256 const& feature) const { if (presets_.contains(feature)) return true; return set_.contains(feature); } bool operator==(Impl const& other) const { if (!digest_ && !other.digest_) return true; if (!digest_ || !other.digest_) return false; XRPL_ASSERT( presets_ == other.presets_, "xrpl::Rules::Impl::operator==(Impl) const : input presets do " "match"); return *digest_ == *other.digest_; } }; Rules::Rules(std::unordered_set> const& presets) : impl_(std::make_shared(presets)) { } Rules::Rules( std::unordered_set> const& presets, std::optional const& digest, STVector256 const& amendments) : impl_(std::make_shared(presets, digest, amendments)) { } std::unordered_set> const& Rules::presets() const { return impl_->presets(); } bool Rules::enabled(uint256 const& feature) const { XRPL_ASSERT(impl_, "xrpl::Rules::enabled : initialized"); return impl_->enabled(feature); } bool Rules::operator==(Rules const& other) const { XRPL_ASSERT(impl_ && other.impl_, "xrpl::Rules::operator==(Rules) const : both initialized"); if (impl_.get() == other.impl_.get()) return true; return *impl_ == *other.impl_; } bool Rules::operator!=(Rules const& other) const { return !(*this == other); } bool isFeatureEnabled(uint256 const& feature, bool resultIfNoRules) { auto const& rules = getCurrentTransactionRules(); if (!rules) return resultIfNoRules; return rules->enabled(feature); } bool isFeatureEnabled(uint256 const& feature) { return isFeatureEnabled(feature, false); } } // namespace xrpl