//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2019 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef PROTOCOL_UNITS_H_INCLUDED #define PROTOCOL_UNITS_H_INCLUDED #include #include #include #include #include #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; template using enable_if_unit_t = typename std::enable_if_t< std::is_class_v && std::is_object_v && std::is_object_v>; /** `is_usable_unit_v` 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 > constexpr bool is_usable_unit_v = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || 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_; protected: template static constexpr bool is_compatible_v = std::is_arithmetic_v && std::is_arithmetic_v && std::is_convertible_v; template > static constexpr bool is_compatiblevalue_v = is_compatible_v && std::is_same_v; template using enable_if_compatible_t = typename std::enable_if_t>; template using enable_if_compatiblevalue_t = typename std::enable_if_t>; 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 < class Other, class = std::enable_if_t< is_compatible_v && is_safetocasttovalue_v>> constexpr ValueUnit(ValueUnit const& value) : 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 std::enable_if_t, 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 > constexpr bool operator==(ValueUnit const& other) const { return value_ == other.value(); } constexpr bool operator==(value_type other) const { return value_ == other; } template > 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 */ constexpr value_type fee() const { return value_; } template constexpr double decimalFromReference(ValueUnit reference) const { return static_cast(value_) / reference.value(); } // `is_usable_unit_v` 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. std::enable_if_t, Json::Value> jsonClipped() const { 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, const ValueUnit& q) { return os << q.value(); } template std::string to_string(ValueUnit const& amount) { return std::to_string(amount.value()); } template > constexpr bool can_muldiv_source_v = std::is_convertible_v; template > constexpr bool can_muldiv_dest_v = can_muldiv_source_v && // Dest is also a source std::is_convertible_v && sizeof(typename Dest::value_type) >= sizeof(std::uint64_t); template < class Source1, class Source2, class = enable_if_unit_t, class = enable_if_unit_t> constexpr bool can_muldiv_sources_v = can_muldiv_source_v && can_muldiv_source_v && std::is_same_v; template < class Source1, class Source2, class Dest, class = enable_if_unit_t, class = enable_if_unit_t, class = enable_if_unit_t> constexpr bool can_muldiv_v = can_muldiv_sources_v && can_muldiv_dest_v; // Source and Dest can be the same by default template < class Source1, class Source2, class Dest, class = enable_if_unit_t, class = enable_if_unit_t, class = enable_if_unit_t> constexpr bool can_muldiv_commute_v = can_muldiv_v && !std::is_same_v; template using enable_muldiv_source_t = typename std::enable_if_t>; template using enable_muldiv_dest_t = typename std::enable_if_t>; template using enable_muldiv_sources_t = typename std::enable_if_t>; template using enable_muldiv_t = typename std::enable_if_t>; template using enable_muldiv_commute_t = typename std::enable_if_t>; template ValueUnit scalar(T value) { return ValueUnit{value}; } template < class Source1, class Source2, class Dest, class = enable_muldiv_t> 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 < class Source1, class Source2, class Dest, class = unit::enable_muldiv_t> std::optional mulDiv(Source1 value, Dest mul, Source2 div) { return unit::mulDivU(value, mul, div); } template < class Source1, class Source2, class Dest, class = unit::enable_muldiv_commute_t> 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 < class Source1, class Source2, class = unit::enable_muldiv_sources_t> 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 < class Source1, class Source2, class = unit::enable_muldiv_sources_t> std::optional mulDiv(std::uint64_t value, Source1 mul, Source2 div) { // Multiplication is commutative return mulDiv(mul, value, div); } template constexpr std::enable_if_t< std::is_same_v && std::is_integral_v && std::is_integral_v, Dest> safe_cast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{safe_cast(s.value())}; } template constexpr std::enable_if_t< std::is_same_v && std::is_integral_v && std::is_integral_v, Dest> unsafe_cast(Src s) noexcept { // Dest may not have an explicit value constructor return Dest{unsafe_cast(s.value())}; } } // namespace ripple #endif // PROTOCOL_UNITS_H_INCLUDED