#ifndef PROTOCOL_UNITS_H_INCLUDED #define PROTOCOL_UNITS_H_INCLUDED #include #include #include #include #include #include #include #include #include namespace ripple { namespace unit { /** "drops" are the smallest divisible amount of XRP. This is what most of the code uses. */ struct dropTag; /** "fee levels" are used by the transaction queue to compare the relative cost of transactions that require different levels of effort to process. See also: src/ripple/app/misc/FeeEscalation.md#fee-level */ struct feelevelTag; /** unitless values are plain scalars wrapped in a ValueUnit. They are used for calculations in this header. */ struct unitlessTag; /** Units to represent basis points (bips) and 1/10 basis points */ class BipsTag; class TenthBipsTag; // These names don't have to be too descriptive, because we're in the "unit" // namespace. template concept Valid = std::is_class_v && std::is_object_v && std::is_object_v; /** `Usable` is checked to ensure that only values with known valid type tags can be used (sometimes transparently) in non-unit contexts. At the time of implementation, this includes all known tags, but more may be added in the future, and they should not be added automatically unless determined to be appropriate. */ template concept Usable = Valid && (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v); template concept Compatible = Valid && std::is_arithmetic_v && std::is_arithmetic_v && std::is_convertible_v; template concept Integral = std::is_integral_v; template concept IntegralValue = Integral; template concept CastableValue = IntegralValue && IntegralValue && std::is_same_v; template class ValueUnit : private boost::totally_ordered>, private boost::additive>, private boost::equality_comparable, T>, private boost::dividable, T>, private boost::modable, T>, private boost::unit_steppable> { public: using unit_type = UnitTag; using value_type = T; private: value_type value_; public: ValueUnit() = default; constexpr ValueUnit(ValueUnit const& other) = default; constexpr ValueUnit& operator=(ValueUnit const& other) = default; constexpr explicit ValueUnit(beast::Zero) : value_(0) { } constexpr ValueUnit& operator=(beast::Zero) { value_ = 0; return *this; } constexpr explicit ValueUnit(value_type value) : value_(value) { } constexpr ValueUnit& operator=(value_type value) { value_ = value; return *this; } /** Instances with the same unit, and a type that is "safe" to convert to this one can be converted implicitly */ template Other> constexpr ValueUnit(ValueUnit const& value) requires SafeToCast : ValueUnit(safe_cast(value.value())) { } constexpr ValueUnit operator+(value_type const& rhs) const { return ValueUnit{value_ + rhs}; } friend constexpr ValueUnit operator+(value_type lhs, ValueUnit const& rhs) { // addition is commutative return rhs + lhs; } constexpr ValueUnit operator-(value_type const& rhs) const { return ValueUnit{value_ - rhs}; } friend constexpr ValueUnit operator-(value_type lhs, ValueUnit const& rhs) { // subtraction is NOT commutative, but (lhs + (-rhs)) is addition, which // is return -rhs + lhs; } constexpr ValueUnit operator*(value_type const& rhs) const { return ValueUnit{value_ * rhs}; } friend constexpr ValueUnit operator*(value_type lhs, ValueUnit const& rhs) { // multiplication is commutative return rhs * lhs; } constexpr value_type operator/(ValueUnit const& rhs) const { return value_ / rhs.value_; } ValueUnit& operator+=(ValueUnit const& other) { value_ += other.value(); return *this; } ValueUnit& operator-=(ValueUnit const& other) { value_ -= other.value(); return *this; } ValueUnit& operator++() { ++value_; return *this; } ValueUnit& operator--() { --value_; return *this; } ValueUnit& operator*=(value_type const& rhs) { value_ *= rhs; return *this; } ValueUnit& operator/=(value_type const& rhs) { value_ /= rhs; return *this; } template ValueUnit& operator%=(value_type const& rhs) { value_ %= rhs; return *this; } ValueUnit operator-() const { static_assert( std::is_signed_v, "- operator illegal on unsigned value types"); return ValueUnit{-value_}; } constexpr bool operator==(ValueUnit const& other) const { return value_ == other.value_; } template Other> constexpr bool operator==(ValueUnit const& other) const { return value_ == other.value(); } constexpr bool operator==(value_type other) const { return value_ == other; } template Other> constexpr bool operator!=(ValueUnit const& other) const { return !operator==(other); } constexpr bool operator<(ValueUnit const& other) const { return value_ < other.value_; } /** Returns true if the amount is not zero */ explicit constexpr operator bool() const noexcept { return value_ != 0; } /** Return the sign of the amount */ constexpr int signum() const noexcept { return (value_ < 0) ? -1 : (value_ ? 1 : 0); } /** Returns the number of drops */ // TODO: Move this to a new class, maybe with the old "TaggedFee" name constexpr value_type fee() const { return value_; } template constexpr double decimalFromReference(ValueUnit reference) const { return static_cast(value_) / reference.value(); } // `Usable` is checked to ensure that only values with // known valid type tags can be converted to JSON. At the time // of implementation, that includes all known tags, but more may // be added in the future. Json::Value jsonClipped() const requires Usable { if constexpr (std::is_integral_v) { using jsontype = std::conditional_t< std::is_signed_v, Json::Int, Json::UInt>; constexpr auto min = std::numeric_limits::min(); constexpr auto max = std::numeric_limits::max(); if (value_ < min) return min; if (value_ > max) return max; return static_cast(value_); } else { return value_; } } /** Returns the underlying value. Code SHOULD NOT call this function unless the type has been abstracted away, e.g. in a templated function. */ constexpr value_type value() const { return value_; } friend std::istream& operator>>(std::istream& s, ValueUnit& val) { s >> val.value_; return s; } }; // Output Values as just their numeric value. template std::basic_ostream& operator<<(std::basic_ostream& os, ValueUnit const& q) { return os << q.value(); } template std::string to_string(ValueUnit const& amount) { return std::to_string(amount.value()); } template concept muldivSource = Valid && std::is_convertible_v; template concept muldivDest = muldivSource && // Dest is also a source std::is_convertible_v && sizeof(typename Dest::value_type) >= sizeof(std::uint64_t); template concept muldivSources = muldivSource && muldivSource && std::is_same_v; template concept muldivable = muldivSources && muldivDest; // Source and Dest can be the same by default template concept muldivCommutable = muldivable && !std::is_same_v; template ValueUnit scalar(T value) { return ValueUnit{value}; } template Dest> std::optional mulDivU(Source1 value, Dest mul, Source2 div) { // values can never be negative in any context. if (value.value() < 0 || mul.value() < 0 || div.value() < 0) { // split the asserts so if one hits, the user can tell which // without a debugger. XRPL_ASSERT( value.value() >= 0, "ripple::unit::mulDivU : minimum value input"); XRPL_ASSERT( mul.value() >= 0, "ripple::unit::mulDivU : minimum mul input"); XRPL_ASSERT( div.value() > 0, "ripple::unit::mulDivU : minimum div input"); return std::nullopt; } using desttype = typename Dest::value_type; constexpr auto max = std::numeric_limits::max(); // Shortcuts, since these happen a lot in the real world if (value == div) return mul; if (mul.value() == div.value()) { if (value.value() > max) return std::nullopt; return Dest{static_cast(value.value())}; } using namespace boost::multiprecision; uint128_t product; product = multiply( product, static_cast(value.value()), static_cast(mul.value())); auto quotient = product / div.value(); if (quotient > max) return std::nullopt; return Dest{static_cast(quotient)}; } } // namespace unit // Fee Levels template using FeeLevel = unit::ValueUnit; using FeeLevel64 = FeeLevel; using FeeLevelDouble = FeeLevel; // Basis points (Bips) template using Bips = unit::ValueUnit; using Bips16 = Bips; using Bips32 = Bips; template using TenthBips = unit::ValueUnit; using TenthBips16 = TenthBips; using TenthBips32 = TenthBips; template Dest> std::optional mulDiv(Source1 value, Dest mul, Source2 div) { return unit::mulDivU(value, mul, div); } template < class Source1, class Source2, unit::muldivCommutable Dest> std::optional mulDiv(Dest value, Source1 mul, Source2 div) { // Multiplication is commutative return unit::mulDivU(mul, value, div); } template std::optional mulDiv(std::uint64_t value, Dest mul, std::uint64_t div) { // Give the scalars a non-tag so the // unit-handling version gets called. return unit::mulDivU(unit::scalar(value), mul, unit::scalar(div)); } template std::optional mulDiv(Dest value, std::uint64_t mul, std::uint64_t div) { // Multiplication is commutative return mulDiv(mul, value, div); } template Source2> std::optional mulDiv(Source1 value, std::uint64_t mul, Source2 div) { // Give the scalars a dimensionless unit so the // unit-handling version gets called. auto unitresult = unit::mulDivU(value, unit::scalar(mul), div); if (!unitresult) return std::nullopt; return unitresult->value(); } template Source2> std::optional mulDiv(std::uint64_t value, Source1 mul, Source2 div) { // Multiplication is commutative return mulDiv(mul, value, div); } template Src> constexpr Dest safe_cast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{safe_cast(s.value())}; } template constexpr Dest safe_cast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{safe_cast(s)}; } template Src> constexpr Dest unsafe_cast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{unsafe_cast(s.value())}; } template constexpr Dest unsafe_cast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{unsafe_cast(s)}; } } // namespace ripple #endif // PROTOCOL_UNITS_H_INCLUDED