Compare commits

..

5 Commits

Author SHA1 Message Date
Ed Hennis
a16aa5b12f Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-18 22:39:25 -05:00
Ed Hennis
ef2de81870 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-15 03:08:38 -05:00
Ed Hennis
fce6757260 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-13 12:19:10 -05:00
Ed Hennis
d759a0a2b0 Merge branch 'develop' into ximinez/fix/validator-cache 2025-11-12 14:12:51 -05:00
Ed Hennis
d2dda416e8 Use Validator List (VL) cache files in more scenarios
- If any [validator_list_keys] are not available after all
  [validator_list_sites] have had a chance to be queried, then fall
  back to loading cache files. Currently, cache files are only used if
  no sites are defined, or the request to one of them has an error. It
  does not include cases where not enough sites are defined, or if a
  site returns an invalid VL (or something else entirely).
- Resolves #5320
2025-11-10 19:53:02 -05:00
26 changed files with 431 additions and 1704 deletions

View File

@@ -1,13 +1,8 @@
#ifndef XRPL_BASICS_NUMBER_H_INCLUDED
#define XRPL_BASICS_NUMBER_H_INCLUDED
#ifdef _MSC_VER
#include <boost/multiprecision/cpp_int.hpp>
#endif
#include <cstdint>
#include <limits>
#include <optional>
#include <ostream>
#include <string>
@@ -18,71 +13,21 @@ class Number;
std::string
to_string(Number const& amount);
template <typename T>
constexpr std::optional<int>
logTen(T value)
{
int log = 0;
while (value >= 10 && value % 10 == 0)
{
value /= 10;
++log;
}
if (value == 1)
return log;
return std::nullopt;
}
template <typename T>
constexpr bool
isPowerOfTen(T value)
{
return logTen(value).has_value();
}
#ifdef _MSC_VER
using numberuint128 = boost::multiprecision::uint128_t;
using numberint128 = boost::multiprecision::int128_t;
#else // !defined(_MSC_VER)
using numberuint128 = __uint128_t;
using numberint128 = __int128_t;
#endif // !defined(_MSC_VER)
struct MantissaRange
{
using internalrep = numberint128;
enum mantissa_scale { small, large };
explicit constexpr MantissaRange(mantissa_scale scale_, internalrep min_)
: min(min_)
, max(min_ * 10 - 1)
, log(logTen(min).value_or(-1))
, scale(scale_)
{
}
internalrep min;
internalrep max;
int log;
mantissa_scale scale;
};
class Number
{
using uint128_t = numberuint128;
using int128_t = numberint128;
using rep = std::int64_t;
using internalrep = MantissaRange::internalrep;
internalrep mantissa_{0};
rep mantissa_{0};
int exponent_{std::numeric_limits<int>::lowest()};
public:
// The range for the mantissa when normalized
constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL;
constexpr static std::int64_t maxMantissa = 9'999'999'999'999'999LL;
// The range for the exponent when normalized
constexpr static int minExponent = -32768;
constexpr static int maxExponent = 32768;
// May need to make unchecked private
struct unchecked
{
explicit unchecked() = default;
@@ -91,13 +36,10 @@ public:
explicit constexpr Number() = default;
Number(rep mantissa);
explicit Number(internalrep mantissa, int exponent);
explicit constexpr Number(
internalrep mantissa,
int exponent,
unchecked) noexcept;
explicit Number(rep mantissa, int exponent);
explicit constexpr Number(rep mantissa, int exponent, unchecked) noexcept;
constexpr internalrep
constexpr rep
mantissa() const noexcept;
constexpr int
exponent() const noexcept;
@@ -199,7 +141,7 @@ public:
while (ret.exponent_ < 0 && ret.mantissa_ != 0)
{
ret.exponent_ += 1;
ret.mantissa_ /= internalrep(10);
ret.mantissa_ /= rep(10);
}
// We are guaranteed that normalize() will never throw an exception
// because exponent is either negative or zero at this point.
@@ -239,96 +181,23 @@ public:
static rounding_mode
setround(rounding_mode mode);
static MantissaRange::mantissa_scale
getMantissaScale();
static void
setMantissaScale(MantissaRange::mantissa_scale scale);
inline static internalrep
minMantissa()
{
return range_.get().min;
}
inline static internalrep
maxMantissa()
{
return range_.get().max;
}
inline static int
mantissaLog()
{
return range_.get().log;
}
/// oneSmall is needed because the ranges are private
constexpr static Number
oneSmall();
/// oneLarge is needed because the ranges are private
constexpr static Number
oneLarge();
// And one is needed because it needs to choose between oneSmall and
// oneLarge based on the current range
static Number
one();
template <class T>
[[nodiscard]]
std::pair<T, int>
normalizeToRange(T minMantissa, T maxMantissa) const;
private:
static thread_local rounding_mode mode_;
// The available ranges for mantissa
constexpr static MantissaRange smallRange{
MantissaRange::small,
1'000'000'000'000'000LL};
static_assert(isPowerOfTen(smallRange.min));
static_assert(smallRange.max == 9'999'999'999'999'999LL);
static_assert(smallRange.log == 15);
// maxint64 9,223,372,036,854,775,808
constexpr static MantissaRange largeRange{
MantissaRange::large,
1'000'000'000'000'000'000LL};
static_assert(isPowerOfTen(largeRange.min));
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
static_assert(largeRange.log == 18);
static_assert(largeRange.min < std::numeric_limits<std::int64_t>::max());
static_assert(largeRange.max > std::numeric_limits<std::int64_t>::max());
// The range for the mantissa when normalized.
// Use reference_wrapper to avoid making copies, and prevent accidentally
// changing the values inside the range.
static thread_local std::reference_wrapper<MantissaRange const> range_;
void
normalize();
static void
normalize(
internalrep& mantissa,
int& exponent,
internalrep const& minMantissa,
internalrep const& maxMantissa);
constexpr bool
isnormal() const noexcept;
class Guard;
};
inline constexpr Number::Number(
internalrep mantissa,
int exponent,
unchecked) noexcept
inline constexpr Number::Number(rep mantissa, int exponent, unchecked) noexcept
: mantissa_{mantissa}, exponent_{exponent}
{
}
inline Number::Number(internalrep mantissa, int exponent)
inline Number::Number(rep mantissa, int exponent)
: mantissa_{mantissa}, exponent_{exponent}
{
normalize();
@@ -338,7 +207,7 @@ inline Number::Number(rep mantissa) : Number{mantissa, 0}
{
}
inline constexpr Number::internalrep
inline constexpr Number::rep
Number::mantissa() const noexcept
{
return mantissa_;
@@ -367,7 +236,7 @@ Number::operator-() const noexcept
inline Number&
Number::operator++()
{
*this += one();
*this += Number{1000000000000000, -15, unchecked{}};
return *this;
}
@@ -382,7 +251,7 @@ Number::operator++(int)
inline Number&
Number::operator--()
{
*this -= one();
*this -= Number{1000000000000000, -15, unchecked{}};
return *this;
}
@@ -435,41 +304,29 @@ operator/(Number const& x, Number const& y)
inline constexpr Number
Number::min() noexcept
{
return Number{range_.get().min, minExponent, unchecked{}};
return Number{minMantissa, minExponent, unchecked{}};
}
inline constexpr Number
Number::max() noexcept
{
return Number{range_.get().max, maxExponent, unchecked{}};
return Number{maxMantissa, maxExponent, unchecked{}};
}
inline constexpr Number
Number::lowest() noexcept
{
return -Number{range_.get().max, maxExponent, unchecked{}};
return -Number{maxMantissa, maxExponent, unchecked{}};
}
inline constexpr bool
Number::isnormal() const noexcept
{
MantissaRange const& range = range_;
auto const abs_m = mantissa_ < 0 ? -mantissa_ : mantissa_;
return range.min <= abs_m && abs_m <= range.max &&
return minMantissa <= abs_m && abs_m <= maxMantissa &&
minExponent <= exponent_ && exponent_ <= maxExponent;
}
template <class T>
std::pair<T, int>
Number::normalizeToRange(T minMantissa, T maxMantissa) const
{
internalrep mantissa = mantissa_;
int exponent = exponent_;
Number::normalize(mantissa, exponent, minMantissa, maxMantissa);
return std::make_pair(static_cast<T>(mantissa), exponent);
}
inline constexpr Number
abs(Number x) noexcept
{
@@ -509,20 +366,6 @@ squelch(Number const& x, Number const& limit) noexcept
return x;
}
inline std::string
to_string(MantissaRange::mantissa_scale const& scale)
{
switch (scale)
{
case MantissaRange::small:
return "small";
case MantissaRange::large:
return "large";
default:
throw std::runtime_error("Bad scale");
}
}
class saveNumberRoundMode
{
Number::rounding_mode mode_;
@@ -561,33 +404,6 @@ public:
operator=(NumberRoundModeGuard const&) = delete;
};
// Sets the new scale and restores the old scale when it leaves scope. Since
// Number doesn't have that facility, we'll build it here.
//
// This class may only end up needed in tests
class NumberMantissaScaleGuard
{
MantissaRange::mantissa_scale saved_;
public:
explicit NumberMantissaScaleGuard(
MantissaRange::mantissa_scale scale) noexcept
: saved_{Number::getMantissaScale()}
{
Number::setMantissaScale(scale);
}
~NumberMantissaScaleGuard()
{
Number::setMantissaScale(saved_);
}
NumberMantissaScaleGuard(NumberMantissaScaleGuard const&) = delete;
NumberMantissaScaleGuard&
operator=(NumberMantissaScaleGuard const&) = delete;
};
} // namespace ripple
#endif // XRPL_BASICS_NUMBER_H_INCLUDED

View File

@@ -122,7 +122,7 @@ toAmount(
{
if (isXRP(issue))
return STAmount(issue, static_cast<std::int64_t>(n));
return STAmount(issue, n);
return STAmount(issue, n.mantissa(), n.exponent());
}
else
{

View File

@@ -84,19 +84,6 @@ public:
return holds<Issue>() && get<Issue>().native();
}
bool
integral() const
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue) {
if constexpr (std::is_same_v<TIss, Issue>)
return issue.native();
if constexpr (std::is_same_v<TIss, MPTIssue>)
return true;
},
issue_);
}
friend constexpr bool
operator==(Asset const& lhs, Asset const& rhs);

View File

@@ -26,10 +26,8 @@ class IOUAmount : private boost::totally_ordered<IOUAmount>,
private boost::additive<IOUAmount>
{
private:
using mantissa_type = std::int64_t;
using exponent_type = int;
mantissa_type mantissa_;
exponent_type exponent_;
std::int64_t mantissa_;
int exponent_;
/** Adjusts the mantissa and exponent to the proper range.
@@ -40,19 +38,11 @@ private:
void
normalize();
IOUAmount(std::pair<mantissa_type, exponent_type> parts)
: IOUAmount(parts.first, parts.second)
{
}
static std::pair<mantissa_type, exponent_type>
scaleNumber(Number const& number);
public:
IOUAmount() = default;
explicit IOUAmount(Number const& other);
IOUAmount(beast::Zero);
IOUAmount(mantissa_type mantissa, exponent_type exponent);
IOUAmount(std::int64_t mantissa, int exponent);
IOUAmount& operator=(beast::Zero);
@@ -81,10 +71,10 @@ public:
int
signum() const noexcept;
exponent_type
int
exponent() const noexcept;
mantissa_type
std::int64_t
mantissa() const noexcept;
static IOUAmount
@@ -102,7 +92,7 @@ inline IOUAmount::IOUAmount(beast::Zero)
*this = beast::zero;
}
inline IOUAmount::IOUAmount(mantissa_type mantissa, exponent_type exponent)
inline IOUAmount::IOUAmount(std::int64_t mantissa, int exponent)
: mantissa_(mantissa), exponent_(exponent)
{
normalize();
@@ -159,13 +149,13 @@ IOUAmount::signum() const noexcept
return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0);
}
inline IOUAmount::exponent_type
inline int
IOUAmount::exponent() const noexcept
{
return exponent_;
}
inline IOUAmount::mantissa_type
inline std::int64_t
IOUAmount::mantissa() const noexcept
{
return mantissa_;

View File

@@ -37,9 +37,6 @@ public:
bool
native() const;
bool
integral() const;
friend constexpr std::weak_ordering
operator<=>(Issue const& lhs, Issue const& rhs);
};

View File

@@ -46,12 +46,6 @@ public:
{
return false;
}
bool
integral() const
{
return true;
}
};
constexpr bool

View File

@@ -47,11 +47,9 @@ public:
static int const cMaxOffset = 80;
// Maximum native value supported by the code
constexpr static std::uint64_t cMinValue = 1'000'000'000'000'000ull;
static_assert(isPowerOfTen(cMinValue));
constexpr static std::uint64_t cMaxValue = cMinValue * 10 - 1;
static_assert(cMaxValue == 9'999'999'999'999'999ull);
static std::uint64_t const cMaxNative = 9'000'000'000'000'000'000ull;
static std::uint64_t const cMinValue = 1000000000000000ull;
static std::uint64_t const cMaxValue = 9999999999999999ull;
static std::uint64_t const cMaxNative = 9000000000000000000ull;
// Max native value on network.
static std::uint64_t const cMaxNativeN = 100000000000000000ull;
@@ -138,7 +136,7 @@ public:
template <AssetType A>
STAmount(A const& asset, Number const& number)
: STAmount(asset, scaleNumber(asset, number))
: STAmount(asset, number.mantissa(), number.exponent())
{
}
@@ -157,9 +155,6 @@ public:
int
exponent() const noexcept;
bool
integral() const noexcept;
bool
native() const noexcept;
@@ -282,22 +277,6 @@ public:
mpt() const;
private:
template <AssetType A>
STAmount(
A const& asset,
std::tuple<mantissa_type, exponent_type, bool> parts)
: STAmount(
asset,
std::get<mantissa_type>(parts),
std::get<exponent_type>(parts),
std::get<bool>(parts))
{
}
template <AssetType A>
static std::tuple<mantissa_type, exponent_type, bool>
scaleNumber(A const& asset, Number const& number);
static std::unique_ptr<STAmount>
construct(SerialIter&, SField const& name);
@@ -361,19 +340,10 @@ STAmount::STAmount(
, mIsNegative(negative)
{
// mValue is uint64, but needs to fit in the range of int64
if (Number::getMantissaScale() == MantissaRange::small)
{
XRPL_ASSERT(
mValue <= std::numeric_limits<std::int64_t>::max(),
"ripple::STAmount::STAmount(SField, A, std::uint64_t, int, bool) : "
"maximum mantissa input");
}
else
{
if (integral() && mValue > std::numeric_limits<std::int64_t>::max())
throw std::overflow_error(
"STAmount mantissa is too large " + std::to_string(mantissa));
}
XRPL_ASSERT(
mValue <= std::numeric_limits<std::int64_t>::max(),
"ripple::STAmount::STAmount(SField, A, std::uint64_t, int, bool) : "
"maximum mantissa input");
canonicalize();
}
@@ -465,12 +435,6 @@ STAmount::exponent() const noexcept
return mOffset;
}
inline bool
STAmount::integral() const noexcept
{
return mAsset.integral();
}
inline bool
STAmount::native() const noexcept
{
@@ -567,29 +531,12 @@ STAmount::operator=(XRPAmount const& amount)
return *this;
}
template <AssetType A>
inline std::tuple<STAmount::mantissa_type, STAmount::exponent_type, bool>
STAmount::scaleNumber(A const& asset, Number const& number)
{
bool const negative = number.mantissa() < 0;
Number const working{negative ? -number : number};
if (asset.integral())
{
return std::make_tuple(std::int64_t(working), 0, negative);
}
else
{
auto const [mantissa, exponent] =
working.normalizeToRange(cMinValue, cMaxValue);
return std::make_tuple(mantissa, exponent, negative);
}
}
inline STAmount&
STAmount::operator=(Number const& number)
{
std::tie(mValue, mOffset, mIsNegative) = scaleNumber(mAsset, number);
mIsNegative = number.mantissa() < 0;
mValue = mIsNegative ? -number.mantissa() : number.mantissa();
mOffset = number.exponent();
canonicalize();
return *this;
}
@@ -606,7 +553,7 @@ STAmount::clear()
{
// The -100 is used to allow 0 to sort less than a small positive values
// which have a negative exponent.
mOffset = integral() ? 0 : -100;
mOffset = native() ? 0 : -100;
mValue = 0;
mIsNegative = false;
}

View File

@@ -482,8 +482,6 @@ public:
value_type
operator*() const;
/// Do not use operator->() unless the field is required, or you've checked
/// that it's set.
T const*
operator->() const;
@@ -720,8 +718,6 @@ STObject::Proxy<T>::operator*() const -> value_type
return this->value();
}
/// Do not use operator->() unless the field is required, or you've checked that
/// it's set.
template <class T>
T const*
STObject::Proxy<T>::operator->() const

View File

@@ -23,7 +23,6 @@ systemName()
/** Number of drops in the genesis account. */
constexpr XRPAmount INITIAL_XRP{100'000'000'000 * DROPS_PER_XRP};
static_assert(INITIAL_XRP.drops() == 100'000'000'000'000'000);
/** Returns true if the amount does not exceed the initial XRP in existence. */
inline bool

View File

@@ -479,10 +479,10 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({
{sfAccount, soeREQUIRED},
{sfData, soeOPTIONAL},
{sfAsset, soeREQUIRED},
{sfAssetsTotal, soeDEFAULT},
{sfAssetsAvailable, soeDEFAULT},
{sfAssetsTotal, soeREQUIRED},
{sfAssetsAvailable, soeREQUIRED},
{sfAssetsMaximum, soeDEFAULT},
{sfLossUnrealized, soeDEFAULT},
{sfLossUnrealized, soeREQUIRED},
{sfShareMPTID, soeREQUIRED},
{sfWithdrawalPolicy, soeREQUIRED},
{sfScale, soeDEFAULT},

View File

@@ -1,6 +1,4 @@
#include <xrpl/basics/Number.h>
//
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <algorithm>
@@ -21,23 +19,10 @@ using uint128_t = boost::multiprecision::uint128_t;
#else // !defined(_MSC_VER)
using uint128_t = __uint128_t;
#endif // !defined(_MSC_VER)
static_assert(std::is_same_v<uint128_t, ripple::numberuint128>);
namespace std {
template <>
struct make_unsigned<ripple::numberint128>
{
using type = uint128_t;
};
} // namespace std
namespace ripple {
thread_local Number::rounding_mode Number::mode_ = Number::to_nearest;
thread_local std::reference_wrapper<MantissaRange const> Number::range_ =
largeRange;
Number::rounding_mode
Number::getround()
@@ -51,21 +36,6 @@ Number::setround(rounding_mode mode)
return std::exchange(mode_, mode);
}
MantissaRange::mantissa_scale
Number::getMantissaScale()
{
return range_.get().scale;
}
void
Number::setMantissaScale(MantissaRange::mantissa_scale scale)
{
// scale_ and range_ MUST stay in lockstep
if (scale != MantissaRange::small && scale != MantissaRange::large)
LogicError("Unknown mantissa scale");
range_ = scale == MantissaRange::small ? smallRange : largeRange;
}
// Guard
// The Guard class is used to tempoarily add extra digits of
@@ -92,9 +62,8 @@ public:
is_negative() const noexcept;
// add a digit
template <class T>
void
push(T d) noexcept;
push(unsigned d) noexcept;
// recover a digit
unsigned
@@ -105,10 +74,6 @@ public:
// tie, round towards even.
int
round() noexcept;
private:
void
doPush(unsigned d) noexcept;
};
inline void
@@ -130,20 +95,13 @@ Number::Guard::is_negative() const noexcept
}
inline void
Number::Guard::doPush(unsigned d) noexcept
Number::Guard::push(unsigned d) noexcept
{
xbit_ = xbit_ || (digits_ & 0x0000'0000'0000'000F) != 0;
digits_ >>= 4;
digits_ |= (d & 0x0000'0000'0000'000FULL) << 60;
}
template <class T>
inline void
Number::Guard::push(T d) noexcept
{
doPush(static_cast<unsigned>(d));
}
inline unsigned
Number::Guard::pop() noexcept
{
@@ -195,51 +153,20 @@ Number::Guard::round() noexcept
// Number
constexpr Number
Number::oneSmall()
{
return Number{
Number::smallRange.min, -Number::smallRange.log, Number::unchecked{}};
};
constexpr Number one{1000000000000000, -15, Number::unchecked{}};
constexpr Number oneSml = Number::oneSmall();
constexpr Number
Number::oneLarge()
{
return Number{
Number::largeRange.min, -Number::largeRange.log, Number::unchecked{}};
};
constexpr Number oneLrg = Number::oneLarge();
Number
Number::one()
{
if (&range_.get() == &smallRange)
return oneSml;
XRPL_ASSERT(&range_.get() == &largeRange, "Number::one() : valid range_");
return oneLrg;
}
// Use the member names in this static function for now so the diff is cleaner
void
Number::normalize(
internalrep& mantissa_,
int& exponent_,
internalrep const& minMantissa,
internalrep const& maxMantissa)
Number::normalize()
{
constexpr Number zero = Number{};
if (mantissa_ == 0)
{
mantissa_ = zero.mantissa_;
exponent_ = zero.exponent_;
*this = Number{};
return;
}
bool const negative = (mantissa_ < 0);
auto m = static_cast<std::make_unsigned_t<internalrep>>(
negative ? -mantissa_ : mantissa_);
auto m = static_cast<std::make_unsigned_t<rep>>(mantissa_);
if (negative)
m = -m;
while ((m < minMantissa) && (exponent_ > minExponent))
{
m *= 10;
@@ -259,8 +186,7 @@ Number::normalize(
mantissa_ = m;
if ((exponent_ < minExponent) || (mantissa_ < minMantissa))
{
mantissa_ = zero.mantissa_;
exponent_ = zero.exponent_;
*this = Number{};
return;
}
@@ -281,13 +207,6 @@ Number::normalize(
mantissa_ = -mantissa_;
}
void
Number::normalize()
{
normalize(
mantissa_, exponent_, Number::minMantissa(), Number::maxMantissa());
}
Number&
Number::operator+=(Number const& y)
{
@@ -347,8 +266,6 @@ Number::operator+=(Number const& y)
}
if (xn == yn)
{
auto const maxMantissa = Number::maxMantissa();
xm += ym;
if (xm > maxMantissa)
{
@@ -371,8 +288,6 @@ Number::operator+=(Number const& y)
}
else
{
auto const minMantissa = Number::minMantissa();
if (xm > ym)
{
xm = xm - ym;
@@ -473,9 +388,6 @@ Number::operator*=(Number const& y)
Guard g;
if (zn == -1)
g.set_negative();
auto const maxMantissa = Number::maxMantissa();
while (zm > maxMantissa)
{
// The following is optimization for:
@@ -484,7 +396,7 @@ Number::operator*=(Number const& y)
g.push(divu10(zm));
++ze;
}
xm = static_cast<internalrep>(zm);
xm = static_cast<rep>(zm);
xe = ze;
auto r = g.round();
if (r == 1 || (r == 0 && (xm & 1) == 1))
@@ -536,66 +448,11 @@ Number::operator/=(Number const& y)
dm = -dm;
dp = -1;
}
// Shift by 10^17 gives greatest precision while not overflowing
// uint128_t or the cast back to int64_t
// TODO: Can/should this be made bigger for largeRange?
// log(2^128,10) ~ 38.5
// largeRange.log = 18, fits in 10^19
// f can be up to 10^(38-19) = 10^19 safely
static_assert(smallRange.log == 15);
static_assert(largeRange.log == 18);
bool small = Number::getMantissaScale() == MantissaRange::small;
uint128_t const f =
small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL;
XRPL_ASSERT_PARTS(
f >= Number::minMantissa() * 10,
"Number::operator/=",
"factor expected size");
// unsigned denominator
auto const dmu = static_cast<uint128_t>(dm);
// correctionFactor can be anything between 10 and f, depending on how much
// extra precision we want to only use for rounding with the
// largeMantissa. Three digits seems like plenty, and is more than
// the smallMantissa uses.
uint128_t const correctionFactor = 1'000;
auto const numerator = uint128_t(nm) * f;
mantissa_ = numerator / dmu;
exponent_ = ne - de - (small ? 17 : 19);
if (!small)
{
// Virtually multiply numerator by correctionFactor. Since that would
// overflow in the existing uint128_t, we'll do that part separately.
// The math for this would work for small mantissas, but we need to
// preserve existing behavior.
//
// Consider:
// ((numerator * correctionFactor) / dmu) / correctionFactor
// = ((numerator / dmu) * correctionFactor) / correctionFactor)
//
// But that assumes infinite precision. With integer math, this is
// equivalent to
//
// = ((numerator / dmu * correctionFactor)
// + ((numerator % dmu) * correctionFactor) / dmu) / correctionFactor
//
// We have already set `mantissa_ = numerator / dmu`. Now we
// compute `remainder = numerator % dmu`, and if it is
// nonzero, we do the rest of the arithmetic. If it's zero, we can skip
// it.
auto const remainder = (numerator % dmu);
if (remainder != 0)
{
mantissa_ *= correctionFactor;
auto const correction = remainder * correctionFactor / dmu;
mantissa_ += correction;
// divide by 1000 by moving the exponent, so we don't lose the
// integer value we just computed
exponent_ -= 3;
}
}
// Shift by 10^17 gives greatest precision while not overflowing uint128_t
// or the cast back to int64_t
uint128_t const f = 100'000'000'000'000'000;
mantissa_ = static_cast<std::int64_t>(uint128_t(nm) * f / uint128_t(dm));
exponent_ = ne - de - 17;
mantissa_ *= np * dp;
normalize();
return *this;
@@ -603,7 +460,7 @@ Number::operator/=(Number const& y)
Number::operator rep() const
{
internalrep drops = mantissa_;
rep drops = mantissa_;
int offset = exponent_;
Guard g;
if (drops != 0)
@@ -618,16 +475,9 @@ Number::operator rep() const
g.push(drops % 10);
drops /= 10;
}
if (offset == 0 && drops > std::numeric_limits<rep>::max())
{
// If offset == 0, then the loop won't run, and the overflow check
// won't be made, but a int128 can overflow int64 by itself, so
// check here.
throw std::overflow_error("Number::operator rep() overflow");
}
for (; offset > 0; --offset)
{
if (drops > std::numeric_limits<rep>::max() / 10)
if (drops > std::numeric_limits<decltype(drops)>::max() / 10)
throw std::overflow_error("Number::operator rep() overflow");
drops *= 10;
}
@@ -639,7 +489,7 @@ Number::operator rep() const
if (g.is_negative())
drops = -drops;
}
return static_cast<rep>(drops);
return drops;
}
std::string
@@ -650,41 +500,30 @@ to_string(Number const& amount)
return "0";
auto const exponent = amount.exponent();
bool const negative = amount.mantissa() < 0;
auto const mantissa = [&]() {
auto mantissa = amount.mantissa();
if (negative)
{
mantissa = -mantissa;
}
XRPL_ASSERT(
mantissa < std::numeric_limits<std::uint64_t>::max(),
"ripple::to_string(Number) : mantissa fits in uin64_t");
return static_cast<std::uint64_t>(mantissa);
}();
auto mantissa = amount.mantissa();
// Use scientific notation for exponents that are too small or too large
auto const rangeLog = Number::mantissaLog();
if (((exponent != 0) &&
((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
if (((exponent != 0) && ((exponent < -25) || (exponent > -5))))
{
std::string ret = negative ? "-" : "";
ret.append(std::to_string(mantissa));
std::string ret = std::to_string(mantissa);
ret.append(1, 'e');
ret.append(std::to_string(exponent));
return ret;
}
// TODO: These numbers are probably wrong for largeRange
bool negative = false;
if (mantissa < 0)
{
mantissa = -mantissa;
negative = true;
}
XRPL_ASSERT(
exponent + 43 > 0, "ripple::to_string(Number) : minimum exponent");
auto const mantissaLog = Number::mantissaLog();
ptrdiff_t const pad_prefix = mantissaLog + 12;
ptrdiff_t const pad_suffix = mantissaLog + 8;
ptrdiff_t const pad_prefix = 27;
ptrdiff_t const pad_suffix = 23;
std::string const raw_value(std::to_string(mantissa));
std::string val;
@@ -694,7 +533,7 @@ to_string(Number const& amount)
val.append(raw_value);
val.append(pad_suffix, '0');
ptrdiff_t const offset(exponent + pad_prefix + mantissaLog + 1);
ptrdiff_t const offset(exponent + 43);
auto pre_from(val.begin());
auto const pre_to(val.begin() + offset);
@@ -755,7 +594,7 @@ Number
power(Number const& f, unsigned n)
{
if (n == 0)
return Number::one();
return one;
if (n == 1)
return f;
auto r = power(f, n / 2);
@@ -777,8 +616,6 @@ power(Number const& f, unsigned n)
Number
root(Number f, unsigned d)
{
auto const one = Number::one();
if (f == one || d == 1)
return f;
if (d == 0)
@@ -795,7 +632,7 @@ root(Number f, unsigned d)
return f;
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
auto e = f.exponent() + Number::mantissaLog() + 1;
auto e = f.exponent() + 16;
auto const di = static_cast<int>(d);
auto ex = [e = e, di = di]() // Euclidean remainder of e/d
{
@@ -844,8 +681,6 @@ root(Number f, unsigned d)
Number
root2(Number f)
{
auto const one = Number::one();
if (f == one)
return f;
if (f < Number{})
@@ -854,7 +689,7 @@ root2(Number f)
return f;
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
auto e = f.exponent() + Number::mantissaLog() + 1;
auto e = f.exponent() + 16;
if (e % 2 != 0)
++e;
f = Number{f.mantissa(), f.exponent() - e}; // f /= 10^e;
@@ -886,8 +721,6 @@ root2(Number f)
Number
power(Number const& f, unsigned n, unsigned d)
{
auto const one = Number::one();
if (f == one)
return f;
auto g = std::gcd(n, d);

View File

@@ -1,10 +1,8 @@
#include <xrpl/protocol/IOUAmount.h>
//
#include <xrpl/basics/LocalValue.h>
#include <xrpl/basics/Number.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/IOUAmount.h>
#include <boost/multiprecision/cpp_int.hpp>
@@ -42,19 +40,11 @@ setSTNumberSwitchover(bool v)
}
/* The range for the mantissa when normalized */
// log(2^63,10) ~ 18.96
//
static std::int64_t constexpr minMantissa = STAmount::cMinValue;
static std::int64_t constexpr maxMantissa = STAmount::cMaxValue;
static std::int64_t constexpr minMantissa = 1000000000000000ull;
static std::int64_t constexpr maxMantissa = 9999999999999999ull;
/* The range for the exponent when normalized */
static int constexpr minExponent = STAmount::cMinOffset;
static int constexpr maxExponent = STAmount::cMaxOffset;
std::pair<IOUAmount::mantissa_type, IOUAmount::exponent_type>
IOUAmount::scaleNumber(Number const& number)
{
return number.normalizeToRange(minMantissa, maxMantissa);
}
static int constexpr minExponent = -96;
static int constexpr maxExponent = 80;
IOUAmount
IOUAmount::minPositiveAmount()
@@ -74,7 +64,8 @@ IOUAmount::normalize()
if (getSTNumberSwitchover())
{
Number const v{mantissa_, exponent_};
std::tie(mantissa_, exponent_) = scaleNumber(v);
mantissa_ = v.mantissa();
exponent_ = v.exponent();
if (exponent_ > maxExponent)
Throw<std::overflow_error>("value overflow");
if (exponent_ < minExponent)
@@ -115,7 +106,8 @@ IOUAmount::normalize()
mantissa_ = -mantissa_;
}
IOUAmount::IOUAmount(Number const& other) : IOUAmount(scaleNumber(other))
IOUAmount::IOUAmount(Number const& other)
: mantissa_(other.mantissa()), exponent_(other.exponent())
{
if (exponent_ > maxExponent)
Throw<std::overflow_error>("value overflow");

View File

@@ -49,12 +49,6 @@ Issue::native() const
return *this == xrpIssue();
}
bool
Issue::integral() const
{
return native();
}
bool
isConsistent(Issue const& ac)
{

View File

@@ -1,12 +1,10 @@
#include <xrpl/protocol/Rules.h>
//
#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/Rules.h>
#include <xrpl/protocol/STVector256.h>
#include <memory>
@@ -35,15 +33,6 @@ getCurrentTransactionRules()
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);
}

View File

@@ -310,8 +310,8 @@ STAmount&
STAmount::operator=(IOUAmount const& iou)
{
XRPL_ASSERT(
integral() == false,
"ripple::STAmount::operator=(IOUAmount) : is not integral");
native() == false,
"ripple::STAmount::operator=(IOUAmount) : is not XRP");
mOffset = iou.exponent();
mIsNegative = iou < beast::zero;
if (mIsNegative)
@@ -851,9 +851,8 @@ STAmount::canonicalize()
if (getSTNumberSwitchover())
{
auto const value = unsafe_cast<std::int64_t>(mValue);
Number num(
mIsNegative ? -value : value, mOffset, Number::unchecked{});
mIsNegative ? -mValue : mValue, mOffset, Number::unchecked{});
auto set = [&](auto const& val) {
mIsNegative = val.value() < 0;
mValue = mIsNegative ? -val.value() : val.value();
@@ -1324,7 +1323,7 @@ multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
if (getSTNumberSwitchover())
{
auto const r = Number{v1} * Number{v2};
return STAmount{asset, r};
return STAmount{asset, r.mantissa(), r.exponent()};
}
std::uint64_t value1 = v1.mantissa();

View File

@@ -50,27 +50,8 @@ STNumber::add(Serializer& s) const
XRPL_ASSERT(
getFName().fieldType == getSType(),
"ripple::STNumber::add : field type match");
if (value_.mantissa() <= std::numeric_limits<std::int64_t>::max() &&
value_.mantissa() >= std::numeric_limits<std::int64_t>::min())
{
// If the mantissa fits in the range of std::int64_t, write it directly.
// This preserves the maximum available precision.
// With the small range, all numbers should be written this way. With
// the large range, it's likely that most numbers will be written this
// way.
s.add64(static_cast<std::int64_t>(value_.mantissa()));
s.add32(value_.exponent());
}
else
{
constexpr std::int64_t min = 100'000'000'000'000'000LL;
constexpr std::int64_t max = min * 10 - 1;
static_assert(
min < (std::numeric_limits<std::int64_t>::max() - 1 / 10));
auto const [mantissa, exponent] = value_.normalizeToRange(min, max);
s.add64(mantissa);
s.add32(exponent);
}
s.add64(value_.mantissa());
s.add32(value_.exponent());
}
Number const&
@@ -199,18 +180,16 @@ numberFromJson(SField const& field, Json::Value const& value)
else if (value.isString())
{
parts = partsFromString(value.asString());
// Number mantissas are much bigger than the allowable parsed values, so
// it can't be out of range.
static_assert(
std::numeric_limits<numberint128>::max() >
std::numeric_limits<decltype(parts.mantissa)>::max());
// Only strings can represent out-of-range values.
if (parts.mantissa > std::numeric_limits<std::int64_t>::max())
Throw<std::range_error>("too high");
}
else
{
Throw<std::runtime_error>("not a number");
}
numberint128 mantissa = parts.mantissa;
std::int64_t mantissa = parts.mantissa;
if (parts.negative)
mantissa = -mantissa;

View File

@@ -30,9 +30,6 @@ namespace test {
*/
struct AMM_test : public jtx::AMMTest
{
// Use small Number mantissas for the life of this test.
NumberMantissaScaleGuard sg_{ripple::MantissaRange::small};
private:
void
testInstanceCreate()
@@ -1387,14 +1384,15 @@ private:
// equal asset deposit: unit test to exercise the rounding-down of
// LPTokens in the AMMHelpers.cpp: adjustLPTokens calculations
// The LPTokens need to have 16 significant digits and a fractional part
for (Number const& deltaLPTokens :
for (Number const deltaLPTokens :
{Number{UINT64_C(100000'0000000009), -10},
Number{UINT64_C(100000'0000000001), -10}})
{
testAMM([&](AMM& ammAlice, Env& env) {
// initial LPToken balance
IOUAmount const initLPToken = ammAlice.getLPTokensBalance();
IOUAmount const newLPTokens{deltaLPTokens};
IOUAmount const newLPTokens{
deltaLPTokens.mantissa(), deltaLPTokens.exponent()};
// carol performs a two-asset deposit
ammAlice.deposit(
@@ -1419,9 +1417,11 @@ private:
Number const deltaXRP = fr * 1e10;
Number const deltaUSD = fr * 1e4;
STAmount const depositUSD = STAmount{USD, deltaUSD};
STAmount const depositUSD =
STAmount{USD, deltaUSD.mantissa(), deltaUSD.exponent()};
STAmount const depositXRP = STAmount{XRP, deltaXRP};
STAmount const depositXRP =
STAmount{XRP, deltaXRP.mantissa(), deltaXRP.exponent()};
// initial LPTokens (1e7) + newLPTokens
BEAST_EXPECT(ammAlice.expectBalances(
@@ -3014,11 +3014,6 @@ private:
using namespace jtx;
using namespace std::chrono;
// For now, just disable SAV entirely, which locks in the small Number
// mantissas
features =
features - featureSingleAssetVault /* - featureLendingProtocol */;
// Auction slot initially is owned by AMM creator, who pays 0 price.
// Bid 110 tokens. Pay bidMin.
@@ -3763,11 +3758,6 @@ private:
testcase("Basic Payment");
using namespace jtx;
// For now, just disable SAV entirely, which locks in the small Number
// mantissas
features =
features - featureSingleAssetVault /* - featureLendingProtocol */;
// Payment 100USD for 100XRP.
// Force one path with tfNoRippleDirect.
testAMM(
@@ -6486,8 +6476,6 @@ private:
Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
auto rules = env.current()->rules();
CurrentTransactionRulesGuard rg(rules);
NumberMantissaScaleGuard sg(MantissaRange::small);
for (auto const& t : tests)
{
auto getPool = [&](std::string const& v, bool isXRP) {

View File

@@ -2474,7 +2474,7 @@ class MPToken_test : public beast::unit_test::suite
alice.name(), makeMptID(env.seq(alice), alice));
Json::Value jv = claw(alice, mpt(1), bob);
jv[jss::Amount][jss::value] = std::to_string(maxMPTokenAmount + 1);
jv[jss::Amount][jss::value] = to_string(maxMPTokenAmount + 1);
Json::Value jv1;
jv1[jss::secret] = alice.name();
jv1[jss::tx_json] = jv;

View File

@@ -4525,7 +4525,7 @@ class Vault_test : public beast::unit_test::suite
BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
auto const strShareID = strHex(sle->at(sfShareMPTID));
BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));

File diff suppressed because it is too large Load Diff

View File

@@ -192,12 +192,6 @@ public:
return to_json(asset_);
}
bool
integral() const
{
return asset_.integral();
}
template <std::integral T>
PrettyAmount
operator()(T v, Number::rounding_mode rounding = Number::getround()) const
@@ -248,12 +242,6 @@ struct XRP_t
return xrpIssue();
}
bool
integral() const
{
return true;
}
/** Returns an amount of XRP as PrettyAmount,
which is trivially convertible to STAmount
@@ -378,11 +366,6 @@ public:
{
return issue();
}
bool
integral() const
{
return issue().integral();
}
/** Implicit conversion to Issue or Asset.
@@ -473,11 +456,6 @@ public:
{
return mptIssue();
}
bool
integral() const
{
return true;
}
/** Implicit conversion to MPTIssue or asset.

View File

@@ -29,8 +29,10 @@ struct STNumber_test : public beast::unit_test::suite
}
void
doRun()
run() override
{
static_assert(!std::is_convertible_v<STNumber*, Number*>);
{
STNumber const stnum{sfNumber};
BEAST_EXPECT(stnum.getSType() == STI_NUMBER);
@@ -125,40 +127,6 @@ struct STNumber_test : public beast::unit_test::suite
BEAST_EXPECT(
numberFromJson(sfNumber, "-0.000e6") == STNumber(sfNumber, 0));
{
NumberRoundModeGuard mg(Number::towards_zero);
// maxint64 9,223,372,036,854,775,807
auto const maxInt =
std::to_string(std::numeric_limits<std::int64_t>::max());
// minint64 -9,223,372,036,854,775,808
auto const minInt =
std::to_string(std::numeric_limits<std::int64_t>::min());
if (Number::getMantissaScale() == MantissaRange::small)
{
BEAST_EXPECT(
numberFromJson(sfNumber, maxInt) ==
STNumber(sfNumber, Number{9'223'372'036'854'775, 3}));
BEAST_EXPECT(
numberFromJson(sfNumber, minInt) ==
STNumber(sfNumber, Number{-9'223'372'036'854'775, 3}));
}
else
{
BEAST_EXPECT(
numberFromJson(sfNumber, maxInt) ==
STNumber(
sfNumber, Number{9'223'372'036'854'775'807, 0}));
BEAST_EXPECT(
numberFromJson(sfNumber, minInt) ==
STNumber(
sfNumber,
-Number{
numberint128(9'223'372'036'854'775) * 1000 +
808,
0}));
}
}
constexpr auto imin = std::numeric_limits<int>::min();
BEAST_EXPECT(
numberFromJson(sfNumber, imin) ==
@@ -311,21 +279,15 @@ struct STNumber_test : public beast::unit_test::suite
}
}
}
void
run() override
{
static_assert(!std::is_convertible_v<STNumber*, Number*>);
for (auto const scale : {MantissaRange::small, MantissaRange::large})
{
NumberMantissaScaleGuard sg(scale);
testcase << to_string(Number::getMantissaScale());
doRun();
}
}
};
BEAST_DEFINE_TESTSUITE(STNumber, protocol, ripple);
void
testCompile(std::ostream& out)
{
STNumber number{sfNumber, 42};
out << number;
}
} // namespace ripple

View File

@@ -129,7 +129,12 @@ ValidatorSite::load(
{
try
{
sites_.emplace_back(uri);
// This is not super efficient, but it doesn't happen often.
bool found = std::ranges::any_of(sites_, [&uri](auto const& site) {
return site.loadedResource->uri == uri;
});
if (!found)
sites_.emplace_back(uri);
}
catch (std::exception const& e)
{
@@ -191,6 +196,17 @@ ValidatorSite::setTimer(
std::lock_guard<std::mutex> const& site_lock,
std::lock_guard<std::mutex> const& state_lock)
{
if (!sites_.empty() && //
std::ranges::all_of(sites_, [](auto const& site) {
return site.lastRefreshStatus.has_value();
}))
{
// If all of the sites have been handled at least once (including
// errors and timeouts), call missingSite, which will load the cache
// files for any lists that are still unavailable.
missingSite(site_lock);
}
auto next = std::min_element(
sites_.begin(), sites_.end(), [](Site const& a, Site const& b) {
return a.nextRefresh < b.nextRefresh;
@@ -303,13 +319,16 @@ ValidatorSite::onRequestTimeout(std::size_t siteIdx, error_code const& ec)
// processes a network error. Usually, this function runs first,
// but on extremely rare occasions, the response handler can run
// first, which will leave activeResource empty.
auto const& site = sites_[siteIdx];
auto& site = sites_[siteIdx];
if (site.activeResource)
JLOG(j_.warn()) << "Request for " << site.activeResource->uri
<< " took too long";
else
JLOG(j_.error()) << "Request took too long, but a response has "
"already been processed";
if (!site.lastRefreshStatus)
site.lastRefreshStatus.emplace(Site::Status{
clock_type::now(), ListDisposition::invalid, "timeout"});
}
std::lock_guard lock_state{state_mutex_};

View File

@@ -592,24 +592,6 @@ Transactor::ticketDelete(
return tesSUCCESS;
}
bool
Transactor::useOldNumberRules(TxType txType)
{
constexpr auto skipTransactions = std::to_array<TxType>(
{ttAMM_BID,
ttAMM_CLAWBACK,
ttAMM_CREATE,
ttAMM_DELETE,
ttAMM_DEPOSIT,
ttAMM_VOTE,
ttAMM_WITHDRAW});
return std::find(
std::begin(skipTransactions),
std::end(skipTransactions),
txType) != std::end(skipTransactions);
}
// check stuff before you bother to lock the ledger
void
Transactor::preCompute()
@@ -1124,16 +1106,10 @@ Transactor::operator()()
{
JLOG(j_.trace()) << "apply: " << ctx_.tx.getTransactionID();
// These global updates really should have been for every Transaction
// step: preflight, preclaim, and doApply. And even calculateBaseFee. See
// with_txn_type().
//
// raii classes for the current ledger rules.
// fixUniversalNumber predate the rulesGuard and should be replaced.
NumberSO stNumberSO{view().rules().enabled(fixUniversalNumber)};
CurrentTransactionRulesGuard currentTransctionRulesGuard(view().rules());
if (Transactor::useOldNumberRules(ctx_.tx.getTxnType()))
Number::setMantissaScale(MantissaRange::small);
#ifdef DEBUG
{
@@ -1146,7 +1122,7 @@ Transactor::operator()()
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Transaction serdes mismatch";
JLOG(j_.fatal()) << ctx_.tx.getJson(JsonOptions::none);
JLOG(j_.info()) << to_string(ctx_.tx.getJson(JsonOptions::none));
JLOG(j_.fatal()) << s2.getJson(JsonOptions::none);
UNREACHABLE(
"ripple::Transactor::operator() : transaction serdes mismatch");

View File

@@ -230,9 +230,6 @@ public:
uint256 const& ticketIndex,
beast::Journal j);
static bool
useOldNumberRules(TxType txType);
protected:
TER
apply();

View File

@@ -34,33 +34,8 @@ struct UnknownTxnType : std::exception
// throw an "UnknownTxnType" exception on error
template <class F>
auto
with_txn_type(Rules const& rules, TxType txnType, F&& f)
with_txn_type(TxType txnType, F&& f)
{
// These global updates really should have been for every Transaction
// step: preflight, preclaim, calculateBaseFee, and doApply. Unfortunately,
// they were only included in doApply (via Transactor::operator()). That may
// have been sufficient when the changes were only related to operations
// that mutated data, but some features will now change how they read data,
// so these need to be more global.
//
// To prevent unintentional side effects on existing checks, they will be
// set for every operation only once SingleAssetVault (or later
// LendingProtocol) are enabled.
//
// See also Transactor::operator().
//
std::optional<NumberSO> stNumberSO;
std::optional<CurrentTransactionRulesGuard> rulesGuard;
if (rules.enabled(featureSingleAssetVault) /*|| rules.enabled(featureLendingProtocol)*/)
{
// raii classes for the current ledger rules.
// fixUniversalNumber predate the rulesGuard and should be replaced.
stNumberSO.emplace(rules.enabled(fixUniversalNumber));
rulesGuard.emplace(rules);
}
if (Transactor::useOldNumberRules(txnType))
Number::setMantissaScale(MantissaRange::small);
switch (txnType)
{
#pragma push_macro("TRANSACTION")
@@ -124,7 +99,7 @@ invoke_preflight(PreflightContext const& ctx)
{
try
{
return with_txn_type(ctx.rules, ctx.tx.getTxnType(), [&]<typename T>() {
return with_txn_type(ctx.tx.getTxnType(), [&]<typename T>() {
auto const tec = Transactor::invokePreflight<T>(ctx);
return std::make_pair(
tec,
@@ -151,51 +126,50 @@ invoke_preclaim(PreclaimContext const& ctx)
{
// use name hiding to accomplish compile-time polymorphism of static
// class functions for Transactor and derived classes.
return with_txn_type(
ctx.view.rules(), ctx.tx.getTxnType(), [&]<typename T>() -> TER {
// preclaim functionality is divided into two sections:
// 1. Up to and including the signature check: returns NotTEC.
// All transaction checks before and including checkSign
// MUST return NotTEC, or something more restrictive.
// Allowing tec results in these steps risks theft or
// destruction of funds, as a fee will be charged before the
// signature is checked.
// 2. After the signature check: returns TER.
return with_txn_type(ctx.tx.getTxnType(), [&]<typename T>() -> TER {
// preclaim functionality is divided into two sections:
// 1. Up to and including the signature check: returns NotTEC.
// All transaction checks before and including checkSign
// MUST return NotTEC, or something more restrictive.
// Allowing tec results in these steps risks theft or
// destruction of funds, as a fee will be charged before the
// signature is checked.
// 2. After the signature check: returns TER.
// If the transactor requires a valid account and the
// transaction doesn't list one, preflight will have already
// a flagged a failure.
auto const id = ctx.tx.getAccountID(sfAccount);
// If the transactor requires a valid account and the
// transaction doesn't list one, preflight will have already
// a flagged a failure.
auto const id = ctx.tx.getAccountID(sfAccount);
if (id != beast::zero)
{
if (NotTEC const preSigResult = [&]() -> NotTEC {
if (NotTEC const result =
T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
return result;
if (id != beast::zero)
{
if (NotTEC const preSigResult = [&]() -> NotTEC {
if (NotTEC const result =
T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
return result;
if (NotTEC const result =
T::checkPriorTxAndLastLedger(ctx))
return result;
if (NotTEC const result =
T::checkPriorTxAndLastLedger(ctx))
return result;
if (NotTEC const result =
T::checkPermission(ctx.view, ctx.tx))
return result;
if (NotTEC const result =
T::checkPermission(ctx.view, ctx.tx))
return result;
if (NotTEC const result = T::checkSign(ctx))
return result;
if (NotTEC const result = T::checkSign(ctx))
return result;
return tesSUCCESS;
}())
return preSigResult;
return tesSUCCESS;
}())
return preSigResult;
if (TER const result = T::checkFee(
ctx, calculateBaseFee(ctx.view, ctx.tx)))
return result;
}
if (TER const result =
T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)))
return result;
}
return T::preclaim(ctx);
});
return T::preclaim(ctx);
});
}
catch (UnknownTxnType const& e)
{
@@ -230,7 +204,7 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
{
try
{
return with_txn_type(view.rules(), tx.getTxnType(), [&]<typename T>() {
return with_txn_type(tx.getTxnType(), [&]<typename T>() {
return T::calculateBaseFee(view, tx);
});
}
@@ -290,11 +264,10 @@ invoke_apply(ApplyContext& ctx)
{
try
{
return with_txn_type(
ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() {
T p(ctx);
return p();
});
return with_txn_type(ctx.tx.getTxnType(), [&]<typename T>() {
T p(ctx);
return p();
});
}
catch (UnknownTxnType const& e)
{