Files
rippled/src/libxrpl/protocol/Rules.cpp
Ed Hennis 33f4c92b61 Expand Number to support the full integer range (#6025)
- Refactor Number internals away from int64 to uint64 & a sign flag
  - ctors and accessors use `rep`. Very few things expose
    `internalrep`.
  - An exception is "unchecked" and the new "normalized", which explicitly
    take an internalrep. But with those special control flags, it's easier
    to distinguish and control when they are used.

- For now, skip the larger mantissas in AMM transactions and tests

- Remove trailing zeros from scientific notation Number strings
  - Update tests. This has the happy side effect of making some of the string
    representations _more_ consistent between the small and large
    mantissa ranges.

- Add semi-automatic rounding of STNumbers based on Asset types
  - Create a new SField metadata enum, sMD_NeedsAsset, which indicates
    the field should be associated with an Asset so it can be rounded.
  - Add a new STTakesAsset intermediate class to handle the Asset
    association to a derived ST class. Currently only used in STNumber,
    but could be used by other types in the future.
  - Add "associateAsset" which takes an SLE and an Asset, finds the
    sMD_NeedsAsset fields, and associates the Asset to them. In the case
    of STNumber, that both stores the Asset, and rounds the value
    immediately.
  - Transactors only need to add a call to associateAsset _after_ all of
    the STNumbers have been set. Unfortunately, the inner workings of
    STObject do not do the association correctly with uninitialized
    fields.
  - When serializing an STNumber that has an Asset, round it before
    serializing.
  - Add an override of roundToAsset, which rounds a Number value in place
    to an Asset, but without any additional scale.
  - Update and fix a bunch of Loan-related tests to accommodate the
    expanded Number class.

---------

Co-authored-by: Vito <5780819+Tapanito@users.noreply.github.com>
2026-01-13 21:01:11 +00:00

155 lines
3.8 KiB
C++

#include <xrpl/protocol/Rules.h>
// Do not remove. Forces Rules.h to stay first, to verify it can compile
// without any hidden dependencies
#include <xrpl/basics/LocalValue.h>
#include <xrpl/basics/Number.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/hardened_hash.h>
#include <xrpl/beast/hash/uhash.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/STVector256.h>
#include <memory>
#include <optional>
#include <unordered_set>
#include <utility>
namespace xrpl {
namespace {
// Use a static inside a function to help prevent order-of-initialization issues
LocalValue<std::optional<Rules>>&
getCurrentTransactionRulesRef()
{
static LocalValue<std::optional<Rules>> r;
return r;
}
} // namespace
std::optional<Rules> const&
getCurrentTransactionRules()
{
return *getCurrentTransactionRulesRef();
}
void
setCurrentTransactionRules(std::optional<Rules> 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.
bool enableLargeNumbers = !r ||
(r->enabled(featureSingleAssetVault) ||
r->enabled(featureLendingProtocol));
Number::setMantissaScale(
enableLargeNumbers ? MantissaRange::large : MantissaRange::small);
*getCurrentTransactionRulesRef() = std::move(r);
}
class Rules::Impl
{
private:
std::unordered_set<uint256, hardened_hash<>> set_;
std::optional<uint256> digest_;
std::unordered_set<uint256, beast::uhash<>> const& presets_;
public:
explicit Impl(std::unordered_set<uint256, beast::uhash<>> const& presets)
: presets_(presets)
{
}
Impl(
std::unordered_set<uint256, beast::uhash<>> const& presets,
std::optional<uint256> const& digest,
STVector256 const& amendments)
: digest_(digest), presets_(presets)
{
set_.reserve(amendments.size());
set_.insert(amendments.begin(), amendments.end());
}
std::unordered_set<uint256, beast::uhash<>> const&
presets() const
{
return presets_;
}
bool
enabled(uint256 const& feature) const
{
if (presets_.count(feature) > 0)
return true;
return set_.count(feature) > 0;
}
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<uint256, beast::uhash<>> const& presets)
: impl_(std::make_shared<Impl>(presets))
{
}
Rules::Rules(
std::unordered_set<uint256, beast::uhash<>> const& presets,
std::optional<uint256> const& digest,
STVector256 const& amendments)
: impl_(std::make_shared<Impl>(presets, digest, amendments))
{
}
std::unordered_set<uint256, beast::uhash<>> 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)
{
auto const& rules = getCurrentTransactionRules();
return rules && rules->enabled(feature);
}
} // namespace xrpl