1#include <test/jtx/Account.h>
3#include <xrpl/basics/Number.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/unit_test/suite.h>
6#include <xrpl/ledger/helpers/VaultHelpers.h>
7#include <xrpl/protocol/Asset.h>
8#include <xrpl/protocol/Indexes.h>
9#include <xrpl/protocol/Issue.h>
10#include <xrpl/protocol/MPTIssue.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/STAmount.h>
13#include <xrpl/protocol/STIssue.h>
14#include <xrpl/protocol/STLedgerEntry.h>
15#include <xrpl/protocol/STNumber.h>
16#include <xrpl/protocol/STTakesAsset.h>
17#include <xrpl/protocol/TER.h>
18#include <xrpl/protocol/UintTypes.h>
55 char const*
name =
nullptr;
70 vault->setFieldIssue(sfAsset,
STIssue{sfAsset, asset});
71 vault->at(sfAssetsTotal) = assetsTotal;
79 template <std::
size_t N>
89 for (
auto const& c : cases)
93 auto const vault =
makeVault(asset, c.assetsTotal);
95 Number(vault->at(sfAssetsTotal)) == c.assetsTotal,
97 ": assetsTotal is not a genuine on-grid STAmount value (associateAsset "
100 STAmount const delta{asset, c.delta};
103 std::string(c.name) +
": delta is not a genuine on-grid STAmount value");
106 for (
auto const mode : modes)
121 result.has_value() == reference->has_value(),
122 std::string(c.name) +
": result depends on ambient rounding mode");
123 if (result.has_value() && reference->has_value())
126 *result == **reference,
127 std::string(c.name) +
": value depends on ambient rounding mode");
129 else if (!result.has_value() && !reference->has_value())
132 result.error() == reference->error(),
133 std::string(c.name) +
": error depends on ambient rounding mode");
141 std::string(c.name) +
": expected tecPRECISION_LOSS, got success value " +
142 (result.has_value() ? result->getText() :
std::string()));
143 if (!result.has_value())
147 std::string(c.name) +
": expected tecPRECISION_LOSS, got " +
153 STAmount const expected{asset, *c.expected};
191 bool const isDebit = c.delta.mantissa() < 0;
193 isDebit ? c.assetsTotal -
Number(*result) : c.assetsTotal +
Number(*result);
194 int const postScale =
scale(posterior, asset);
195 int const checkScale =
196 isDebit ? postScale :
std::min(postScale,
scale(c.assetsTotal, asset));
200 reFloored == *result,
201 std::string(c.name) +
": result " + result->getText() +
202 " is not exact on the posterior grid (scale " +
216 testcase(
"probe: default Number regime is Large330 (19-digit mantissa)");
224 Number const probe{9'223'372'036'854'775'807LL, 0};
225 BEAST_EXPECT(probe.
mantissa() == 9'223'372'036'854'775'807LL);
226 BEAST_EXPECT(probe.
exponent() == 0);
243 .name =
"IOU debit: on-grid, same decade",
244 .assetsTotal =
Number{1'000'000'000'000'005LL, -9},
246 .expected =
Number{1, -9},
262 .name =
"IOU debit: sub-ULP at the naive scale, but not at the true postScale",
263 .assetsTotal =
Number{1'000'000, 0},
264 .delta =
Number{-73, -11},
265 .expected =
Number{7, -10},
276 .name =
"IOU debit: exact at the true (finer) postScale",
277 .assetsTotal =
Number{1'000'000, 0},
278 .delta =
Number{-53, -10},
279 .expected =
Number{53, -10},
289 .name =
"IOU debit: decade-crossing debit, floored (not exact) at finer grid",
290 .assetsTotal =
Number{1, 0},
291 .delta =
Number{-73, -17},
292 .expected =
Number{7, -16},
301 .name =
"IOU debit: near-total debit, unchanged (finer postScale than magnitude)",
302 .assetsTotal =
Number{1'000'000, 0},
303 .delta =
Number{-9'999'999'999'999'999LL, -10},
304 .expected =
Number{9'999'999'999'999'999LL, -10},
325 .name =
"IOU credit: on-grid",
326 .assetsTotal =
Number{1'000'000, 0},
328 .expected =
Number{2, -9},
351 .name =
"IOU credit: overcredit fix across a scale boundary",
352 .assetsTotal =
Number{9'999'999'999'999'999LL, -15},
354 .expected =
Number{4'999'999'999'999'991LL, -15},
378 .name =
"IOU credit: Finding-1 regression, ToNearest sum would overcredit",
379 .assetsTotal =
Number{1'000'000, 0},
380 .delta =
Number{9'999'999'999'999'999LL, -25},
381 .expected = std::nullopt,
391 .name =
"IOU credit: Finding-1 regression, 19-digit half-even tie",
392 .assetsTotal =
Number{1'000'000, 0},
393 .delta =
Number{9'995, -13},
394 .expected = std::nullopt,
400 .name =
"IOU credit: zero-total vault",
403 .expected =
Number{37, -6},
413 .name =
"IOU credit: sub-ULP credit",
414 .assetsTotal =
Number{1'000'000, 0},
416 .expected = std::nullopt,
434 .name =
"MPT debit: magnitude is positive, not the signed delta",
435 .assetsTotal =
Number{1'000'000},
440 .name =
"MPT credit: unchanged",
441 .assetsTotal =
Number{1'000'000},
450 .name =
"XRP debit: magnitude is positive, not the signed delta",
451 .assetsTotal =
Number{100'000},
456 .name =
"XRP credit: unchanged",
457 .assetsTotal =
Number{100'000},
TestcaseT testcase
Memberspace for declaring test cases.
A currency issued by an account.
Number is a floating point type that can represent a wide range of values.
constexpr rep mantissa() const noexcept
Returns the mantissa of the external view of the Number.
static MantissaRange::MantissaScale getMantissaScale()
Returns which mantissa scale is currently in use for normalization.
constexpr int exponent() const noexcept
Returns the exponent of the external view of the Number.
std::string getText() const override
void testIntegralAssets(Asset const &mpt, Asset const &xrp)
static std::shared_ptr< SLE > makeVault(Asset const &asset, Number const &assetsTotal)
void testIouCredits(Asset const &iou)
void run() override
Runs the suite.
void testIouDebits(Asset const &iou)
void testProbeLarge330Regime()
void runCases(Asset const &asset, std::array< Case, N > const &cases)
Immutable cryptographic account descriptor.
AccountID id() const
Returns the Account ID.
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
int scale(Number const &number, Asset const &asset)
Get the scale of a Number for a given asset.
std::expected< STAmount, TER > clampToAssetsTotalScale(SLE::ConstRef vault, STAmount const &delta)
Adjusts a requested asset change (delta) to match the decimal scale of the updated total vault assets...
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
std::string transToken(TER code)
STAmount roundToScale(STAmount const &value, std::int32_t scale, Number::RoundingMode rounding=Number::getround())
Round an arbitrary precision Amount to the precision of an STAmount that has a given exponent.
MPTID makeMptID(std::uint32_t const sequence, AccountID const &account)
constexpr Number abs(Number x) noexcept
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
std::optional< Number > expected