mirror of
https://github.com/XRPLF/rippled.git
synced 2026-01-21 15:15:26 +00:00
Compare commits
28 Commits
ximinez/nu
...
ximinez/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6e4620349 | ||
|
|
db0ef6a370 | ||
|
|
11a45a0ac2 | ||
|
|
aa035f4cfd | ||
|
|
8988f9117f | ||
|
|
ae4f379845 | ||
|
|
671aa11649 | ||
|
|
53d35fd8ea | ||
|
|
0c7ea2e333 | ||
|
|
5f54be25e9 | ||
|
|
d82756519c | ||
|
|
1f23832659 | ||
|
|
4c50969bde | ||
|
|
aabdf372dd | ||
|
|
c6d63a4b90 | ||
|
|
1e6c3208db | ||
|
|
a74f223efb | ||
|
|
1eb3a3ea5a | ||
|
|
630e428929 | ||
|
|
3f93edc5e0 | ||
|
|
baf62689ff | ||
|
|
ddf7d6cac4 | ||
|
|
fcd2ea2d6e | ||
|
|
a16aa5b12f | ||
|
|
ef2de81870 | ||
|
|
fce6757260 | ||
|
|
d759a0a2b0 | ||
|
|
d2dda416e8 |
@@ -109,10 +109,6 @@ template <class T>
|
||||
concept Integral64 =
|
||||
std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t>;
|
||||
|
||||
template <class STAmount, class Asset>
|
||||
concept CanUseAsScale = requires(Asset a, Number n) { STAmount(a, n); } &&
|
||||
requires(STAmount s) { s.exponent(); };
|
||||
|
||||
/** Number is a floating point type that can represent a wide range of values.
|
||||
*
|
||||
* It can represent all values that can be represented by an STAmount -
|
||||
@@ -272,26 +268,6 @@ public:
|
||||
constexpr int
|
||||
exponent() const noexcept;
|
||||
|
||||
/** Get the scale of this Number for the given asset.
|
||||
*
|
||||
* "scale" is similar to "exponent", but from the perspective of STAmount,
|
||||
* which has different rules for determining the exponent than Number.
|
||||
*
|
||||
* Because Number does not have access to STAmount or Asset, this function
|
||||
* is implemented as a template, with the expectation that it will only be
|
||||
* used by those types. Any types that fit the requirements will work,
|
||||
* though, if there's a need.
|
||||
*
|
||||
* @tparam STAmount The STAmount type.
|
||||
* @tparam Asset The Asset type.
|
||||
* @param asset The asset to use for determining the scale.
|
||||
* @return The scale of this Number for the given asset.
|
||||
*/
|
||||
template <class STAmount, class Asset>
|
||||
int
|
||||
scale(Asset const& asset) const
|
||||
requires CanUseAsScale<STAmount, Asset>;
|
||||
|
||||
constexpr Number
|
||||
operator+() const noexcept;
|
||||
constexpr Number
|
||||
@@ -626,14 +602,6 @@ Number::exponent() const noexcept
|
||||
return e;
|
||||
}
|
||||
|
||||
template <class STAmount, class Asset>
|
||||
int
|
||||
Number::scale(Asset const& asset) const
|
||||
requires CanUseAsScale<STAmount, Asset>
|
||||
{
|
||||
return STAmount{asset, *this}.exponent();
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
Number::operator+() const noexcept
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <xrpld/app/tx/apply.h>
|
||||
#include <xrpld/app/tx/detail/ApplyContext.h>
|
||||
#include <xrpld/app/tx/detail/InvariantCheck.h>
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
@@ -21,9 +20,6 @@
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
|
||||
@@ -3892,122 +3888,6 @@ class Invariants_test : public beast::unit_test::suite
|
||||
precloseMpt);
|
||||
}
|
||||
|
||||
void
|
||||
testVaultComputeMinScale()
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
Account const issuer{"issuer"};
|
||||
PrettyAsset const vaultAsset = issuer["IOU"];
|
||||
|
||||
struct TestCase
|
||||
{
|
||||
std::string name;
|
||||
std::int32_t expectedMinScale;
|
||||
std::initializer_list<Number const> values;
|
||||
};
|
||||
|
||||
NumberMantissaScaleGuard g{MantissaRange::large};
|
||||
|
||||
auto const testCases = std::vector<TestCase>{
|
||||
{
|
||||
.name = "No values",
|
||||
.expectedMinScale = 0,
|
||||
.values = {},
|
||||
},
|
||||
{
|
||||
.name = "Mixed integer and Number values",
|
||||
.expectedMinScale = -15,
|
||||
.values = {1, -1, Number{10, -1}},
|
||||
},
|
||||
{
|
||||
.name = "Mixed scales",
|
||||
.expectedMinScale = -17,
|
||||
.values = {Number{1, -2}, Number{5, -3}, Number{3, -2}},
|
||||
},
|
||||
{
|
||||
.name = "Mixed mantissa sizes",
|
||||
.expectedMinScale = -12,
|
||||
.values =
|
||||
{Number{1},
|
||||
Number{1234, -3},
|
||||
Number{12345, -6},
|
||||
Number{123, 1}},
|
||||
},
|
||||
};
|
||||
|
||||
for (auto const& tc : testCases)
|
||||
{
|
||||
testcase("vault computeMinScale: " + tc.name);
|
||||
|
||||
auto const actualScale =
|
||||
ValidVault::computeMinScale(vaultAsset, tc.values);
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualScale == tc.expectedMinScale,
|
||||
"expected: " + std::to_string(tc.expectedMinScale) +
|
||||
", actual: " + std::to_string(actualScale));
|
||||
for (auto const& num : tc.values)
|
||||
{
|
||||
// None of these scales are far enough apart that rounding the
|
||||
// values would lose information, so check that the rounded
|
||||
// value matches the original.
|
||||
auto const actualRounded =
|
||||
roundToAsset(vaultAsset, num, actualScale);
|
||||
BEAST_EXPECTS(
|
||||
actualRounded == num,
|
||||
"number " + to_string(num) + " rounded to scale " +
|
||||
std::to_string(actualScale) + " is " +
|
||||
to_string(actualRounded));
|
||||
}
|
||||
}
|
||||
|
||||
auto const testCases2 = std::vector<TestCase>{
|
||||
{
|
||||
.name = "False equivalence",
|
||||
.expectedMinScale = -15,
|
||||
.values =
|
||||
{
|
||||
Number{1234567890123456789, -18},
|
||||
Number{12345, -4},
|
||||
Number{1},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Unlike the first set of test cases, the values in these test could
|
||||
// look equivalent if using the wrong scale.
|
||||
for (auto const& tc : testCases2)
|
||||
{
|
||||
testcase("vault computeMinScale: " + tc.name);
|
||||
|
||||
auto const actualScale =
|
||||
ValidVault::computeMinScale(vaultAsset, tc.values);
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualScale == tc.expectedMinScale,
|
||||
"expected: " + std::to_string(tc.expectedMinScale) +
|
||||
", actual: " + std::to_string(actualScale));
|
||||
std::optional<Number> first;
|
||||
Number firstRounded;
|
||||
for (auto const& num : tc.values)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
first = num;
|
||||
firstRounded = roundToAsset(vaultAsset, num, actualScale);
|
||||
continue;
|
||||
}
|
||||
auto const numRounded =
|
||||
roundToAsset(vaultAsset, num, actualScale);
|
||||
BEAST_EXPECTS(
|
||||
numRounded != firstRounded,
|
||||
"at a scale of " + std::to_string(actualScale) + " " +
|
||||
to_string(num) + " == " + to_string(*first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -4031,7 +3911,6 @@ public:
|
||||
testValidPseudoAccounts();
|
||||
testValidLoanBroker();
|
||||
testVault();
|
||||
testVaultComputeMinScale();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -176,7 +176,8 @@ getAssetsTotalScale(SLE::const_ref vaultSle)
|
||||
{
|
||||
if (!vaultSle)
|
||||
return Number::minExponent - 1; // LCOV_EXCL_LINE
|
||||
return vaultSle->at(sfAssetsTotal).scale<STAmount>(vaultSle->at(sfAsset));
|
||||
return STAmount{vaultSle->at(sfAsset), vaultSle->at(sfAssetsTotal)}
|
||||
.exponent();
|
||||
}
|
||||
|
||||
TER
|
||||
|
||||
@@ -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_};
|
||||
|
||||
@@ -22,12 +22,7 @@
|
||||
#include <xrpl/protocol/Units.h>
|
||||
#include <xrpl/protocol/nftPageMask.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -3201,29 +3196,16 @@ ValidVault::finalize(
|
||||
"xrpl::ValidVault::finalize : deposit updated a vault");
|
||||
auto const& beforeVault = beforeVault_[0];
|
||||
|
||||
auto const maybeVaultDeltaAssets =
|
||||
deltaAssets(afterVault.pseudoId);
|
||||
if (!maybeVaultDeltaAssets)
|
||||
auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
|
||||
|
||||
if (!vaultDeltaAssets)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must change vault balance";
|
||||
return false; // That's all we can do
|
||||
}
|
||||
|
||||
// Get the coarsest scale to round calculations to
|
||||
auto const minScale = computeMinScale(
|
||||
vaultAsset,
|
||||
{
|
||||
*maybeVaultDeltaAssets,
|
||||
afterVault.assetsTotal - beforeVault.assetsTotal,
|
||||
afterVault.assetsAvailable -
|
||||
beforeVault.assetsAvailable,
|
||||
});
|
||||
|
||||
auto const vaultDeltaAssets =
|
||||
roundToAsset(vaultAsset, *maybeVaultDeltaAssets, minScale);
|
||||
|
||||
if (vaultDeltaAssets > tx[sfAmount])
|
||||
if (*vaultDeltaAssets > tx[sfAmount])
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must not change vault "
|
||||
@@ -3231,7 +3213,7 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (vaultDeltaAssets <= zero)
|
||||
if (*vaultDeltaAssets <= zero)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must increase vault balance";
|
||||
@@ -3248,24 +3230,16 @@ ValidVault::finalize(
|
||||
|
||||
if (!issuerDeposit)
|
||||
{
|
||||
auto const maybeAccDeltaAssets = deltaAssetsTxAccount();
|
||||
if (!maybeAccDeltaAssets)
|
||||
auto const accountDeltaAssets = deltaAssetsTxAccount();
|
||||
if (!accountDeltaAssets)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must change depositor "
|
||||
"balance";
|
||||
return false;
|
||||
}
|
||||
auto const localMinScale = std::min(
|
||||
minScale,
|
||||
computeMinScale(vaultAsset, {*maybeAccDeltaAssets}));
|
||||
|
||||
auto const accountDeltaAssets = roundToAsset(
|
||||
vaultAsset, *maybeAccDeltaAssets, localMinScale);
|
||||
auto const localVaultDeltaAssets = roundToAsset(
|
||||
vaultAsset, vaultDeltaAssets, localMinScale);
|
||||
|
||||
if (accountDeltaAssets >= zero)
|
||||
if (*accountDeltaAssets >= zero)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must decrease depositor "
|
||||
@@ -3273,7 +3247,7 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (localVaultDeltaAssets * -1 != accountDeltaAssets)
|
||||
if (*accountDeltaAssets * -1 != *vaultDeltaAssets)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must change vault and "
|
||||
@@ -3291,17 +3265,16 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const maybeAccDeltaShares = deltaShares(tx[sfAccount]);
|
||||
if (!maybeAccDeltaShares)
|
||||
auto const accountDeltaShares = deltaShares(tx[sfAccount]);
|
||||
if (!accountDeltaShares)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must change depositor "
|
||||
"shares";
|
||||
return false; // That's all we can do
|
||||
}
|
||||
// We don't need to round shares, they are integral MPT
|
||||
auto const accountDeltaShares = *maybeAccDeltaShares;
|
||||
if (accountDeltaShares <= zero)
|
||||
|
||||
if (*accountDeltaShares <= zero)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must increase depositor "
|
||||
@@ -3309,18 +3282,15 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const maybeVaultDeltaShares =
|
||||
deltaShares(afterVault.pseudoId);
|
||||
if (!maybeVaultDeltaShares || *maybeVaultDeltaShares == zero)
|
||||
auto const vaultDeltaShares = deltaShares(afterVault.pseudoId);
|
||||
if (!vaultDeltaShares || *vaultDeltaShares == zero)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must change vault shares";
|
||||
return false; // That's all we can do
|
||||
}
|
||||
|
||||
// We don't need to round shares, they are integral MPT
|
||||
auto const vaultDeltaShares = *maybeVaultDeltaShares;
|
||||
if (vaultDeltaShares * -1 != accountDeltaShares)
|
||||
if (*vaultDeltaShares * -1 != *accountDeltaShares)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: deposit must change depositor and "
|
||||
@@ -3328,22 +3298,15 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const assetTotalDelta = roundToAsset(
|
||||
vaultAsset,
|
||||
afterVault.assetsTotal - beforeVault.assetsTotal,
|
||||
minScale);
|
||||
if (assetTotalDelta != vaultDeltaAssets)
|
||||
if (beforeVault.assetsTotal + *vaultDeltaAssets !=
|
||||
afterVault.assetsTotal)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: deposit and assets "
|
||||
"outstanding must add up";
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const assetAvailableDelta = roundToAsset(
|
||||
vaultAsset,
|
||||
afterVault.assetsAvailable - beforeVault.assetsAvailable,
|
||||
minScale);
|
||||
if (assetAvailableDelta != vaultDeltaAssets)
|
||||
if (beforeVault.assetsAvailable + *vaultDeltaAssets !=
|
||||
afterVault.assetsAvailable)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: deposit and assets "
|
||||
"available must add up";
|
||||
@@ -3361,32 +3324,22 @@ ValidVault::finalize(
|
||||
"vault");
|
||||
auto const& beforeVault = beforeVault_[0];
|
||||
|
||||
auto const maybeVaultDeltaAssets =
|
||||
deltaAssets(afterVault.pseudoId);
|
||||
auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
|
||||
|
||||
if (!maybeVaultDeltaAssets)
|
||||
if (!vaultDeltaAssets)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: withdrawal must "
|
||||
"change vault balance";
|
||||
return false; // That's all we can do
|
||||
}
|
||||
|
||||
// Get the most coarse scale to round calculations to
|
||||
auto const minScale = computeMinScale(
|
||||
vaultAsset,
|
||||
{*maybeVaultDeltaAssets,
|
||||
afterVault.assetsTotal - beforeVault.assetsTotal,
|
||||
afterVault.assetsAvailable - beforeVault.assetsAvailable});
|
||||
|
||||
auto const vaultPseudoDeltaAssets =
|
||||
roundToAsset(vaultAsset, *maybeVaultDeltaAssets, minScale);
|
||||
|
||||
if (vaultPseudoDeltaAssets >= zero)
|
||||
if (*vaultDeltaAssets >= zero)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: withdrawal must "
|
||||
"decrease vault balance";
|
||||
result = false;
|
||||
}
|
||||
|
||||
// Any payments (including withdrawal) going to the issuer
|
||||
// do not change their balance, but destroy funds instead.
|
||||
bool const issuerWithdrawal = [&]() -> bool {
|
||||
@@ -3399,8 +3352,8 @@ ValidVault::finalize(
|
||||
|
||||
if (!issuerWithdrawal)
|
||||
{
|
||||
auto const maybeAccDelta = deltaAssetsTxAccount();
|
||||
auto const maybeOtherAccDelta =
|
||||
auto const accountDeltaAssets = deltaAssetsTxAccount();
|
||||
auto const otherAccountDelta =
|
||||
[&]() -> std::optional<Number> {
|
||||
if (auto const destination = tx[~sfDestination];
|
||||
destination && *destination != tx[sfAccount])
|
||||
@@ -3408,8 +3361,8 @@ ValidVault::finalize(
|
||||
return std::nullopt;
|
||||
}();
|
||||
|
||||
if (maybeAccDelta.has_value() ==
|
||||
maybeOtherAccDelta.has_value())
|
||||
if (accountDeltaAssets.has_value() ==
|
||||
otherAccountDelta.has_value())
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: withdrawal must change one "
|
||||
@@ -3418,18 +3371,10 @@ ValidVault::finalize(
|
||||
}
|
||||
|
||||
auto const destinationDelta = //
|
||||
maybeAccDelta ? *maybeAccDelta : *maybeOtherAccDelta;
|
||||
accountDeltaAssets ? *accountDeltaAssets
|
||||
: *otherAccountDelta;
|
||||
|
||||
// the scale of destinationDelta can be coarser than
|
||||
// minScale, so we take that into account when rounding
|
||||
auto const localMinScale = std::min(
|
||||
minScale,
|
||||
computeMinScale(vaultAsset, {destinationDelta}));
|
||||
|
||||
auto const roundedDestinationDelta = roundToAsset(
|
||||
vaultAsset, destinationDelta, localMinScale);
|
||||
|
||||
if (roundedDestinationDelta <= zero)
|
||||
if (destinationDelta <= zero)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: withdrawal must increase "
|
||||
@@ -3437,9 +3382,7 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const localPseudoDeltaAssets = roundToAsset(
|
||||
vaultAsset, vaultPseudoDeltaAssets, localMinScale);
|
||||
if (localPseudoDeltaAssets * -1 != roundedDestinationDelta)
|
||||
if (*vaultDeltaAssets * -1 != destinationDelta)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: withdrawal must change vault "
|
||||
@@ -3447,7 +3390,7 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
// We don't need to round shares, they are integral MPT
|
||||
|
||||
auto const accountDeltaShares = deltaShares(tx[sfAccount]);
|
||||
if (!accountDeltaShares)
|
||||
{
|
||||
@@ -3464,7 +3407,7 @@ ValidVault::finalize(
|
||||
"shares";
|
||||
result = false;
|
||||
}
|
||||
// We don't need to round shares, they are integral MPT
|
||||
|
||||
auto const vaultDeltaShares = deltaShares(afterVault.pseudoId);
|
||||
if (!vaultDeltaShares || *vaultDeltaShares == zero)
|
||||
{
|
||||
@@ -3481,24 +3424,17 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const assetTotalDelta = roundToAsset(
|
||||
vaultAsset,
|
||||
afterVault.assetsTotal - beforeVault.assetsTotal,
|
||||
minScale);
|
||||
// Note, vaultBalance is negative (see check above)
|
||||
if (assetTotalDelta != vaultPseudoDeltaAssets)
|
||||
if (beforeVault.assetsTotal + *vaultDeltaAssets !=
|
||||
afterVault.assetsTotal)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: withdrawal and "
|
||||
"assets outstanding must add up";
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const assetAvailableDelta = roundToAsset(
|
||||
vaultAsset,
|
||||
afterVault.assetsAvailable - beforeVault.assetsAvailable,
|
||||
minScale);
|
||||
|
||||
if (assetAvailableDelta != vaultPseudoDeltaAssets)
|
||||
if (beforeVault.assetsAvailable + *vaultDeltaAssets !=
|
||||
afterVault.assetsAvailable)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: withdrawal and "
|
||||
"assets available must add up";
|
||||
@@ -3532,19 +3468,10 @@ ValidVault::finalize(
|
||||
}
|
||||
}
|
||||
|
||||
auto const maybeVaultDeltaAssets =
|
||||
deltaAssets(afterVault.pseudoId);
|
||||
if (maybeVaultDeltaAssets)
|
||||
auto const vaultDeltaAssets = deltaAssets(afterVault.pseudoId);
|
||||
if (vaultDeltaAssets)
|
||||
{
|
||||
auto const minScale = computeMinScale(
|
||||
vaultAsset,
|
||||
{*maybeVaultDeltaAssets,
|
||||
afterVault.assetsTotal - beforeVault.assetsTotal,
|
||||
afterVault.assetsAvailable -
|
||||
beforeVault.assetsAvailable});
|
||||
auto const vaultDeltaAssets = roundToAsset(
|
||||
vaultAsset, *maybeVaultDeltaAssets, minScale);
|
||||
if (vaultDeltaAssets >= zero)
|
||||
if (*vaultDeltaAssets >= zero)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: clawback must decrease vault "
|
||||
@@ -3552,11 +3479,8 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const assetsTotalDelta = roundToAsset(
|
||||
vaultAsset,
|
||||
afterVault.assetsTotal - beforeVault.assetsTotal,
|
||||
minScale);
|
||||
if (assetsTotalDelta != vaultDeltaAssets)
|
||||
if (beforeVault.assetsTotal + *vaultDeltaAssets !=
|
||||
afterVault.assetsTotal)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: clawback and assets outstanding "
|
||||
@@ -3564,12 +3488,8 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const assetAvailableDelta = roundToAsset(
|
||||
vaultAsset,
|
||||
afterVault.assetsAvailable -
|
||||
beforeVault.assetsAvailable,
|
||||
minScale);
|
||||
if (assetAvailableDelta != vaultDeltaAssets)
|
||||
if (beforeVault.assetsAvailable + *vaultDeltaAssets !=
|
||||
afterVault.assetsAvailable)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: clawback and assets available "
|
||||
@@ -3584,15 +3504,15 @@ ValidVault::finalize(
|
||||
return false; // That's all we can do
|
||||
}
|
||||
|
||||
// We don't need to round shares, they are integral MPT
|
||||
auto const maybeAccountDeltaShares = deltaShares(tx[sfHolder]);
|
||||
if (!maybeAccountDeltaShares)
|
||||
auto const accountDeltaShares = deltaShares(tx[sfHolder]);
|
||||
if (!accountDeltaShares)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: clawback must change holder shares";
|
||||
return false; // That's all we can do
|
||||
}
|
||||
if (*maybeAccountDeltaShares >= zero)
|
||||
|
||||
if (*accountDeltaShares >= zero)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: clawback must decrease holder "
|
||||
@@ -3600,7 +3520,6 @@ ValidVault::finalize(
|
||||
result = false;
|
||||
}
|
||||
|
||||
// We don't need to round shares, they are integral MPT
|
||||
auto const vaultDeltaShares = deltaShares(afterVault.pseudoId);
|
||||
if (!vaultDeltaShares || *vaultDeltaShares == zero)
|
||||
{
|
||||
@@ -3609,7 +3528,7 @@ ValidVault::finalize(
|
||||
return false; // That's all we can do
|
||||
}
|
||||
|
||||
if (*vaultDeltaShares * -1 != *maybeAccountDeltaShares)
|
||||
if (*vaultDeltaShares * -1 != *accountDeltaShares)
|
||||
{
|
||||
JLOG(j.fatal()) << //
|
||||
"Invariant failed: clawback must change holder and "
|
||||
@@ -3647,32 +3566,4 @@ ValidVault::finalize(
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
ValidVault::computeMinScale(
|
||||
Asset const& asset,
|
||||
std::initializer_list<Number const> numbers)
|
||||
{
|
||||
if (numbers.size() == 0)
|
||||
return 0;
|
||||
// Helper to get the minimal scale of an STAmount by removing trailing
|
||||
// zeros from its mantissa
|
||||
auto const getNatScale = [](Asset const& asset,
|
||||
Number const& value) -> std::int32_t {
|
||||
if (asset.integral())
|
||||
return 0;
|
||||
if (value == beast::zero)
|
||||
return value.exponent();
|
||||
|
||||
return value.scale<STAmount>(asset);
|
||||
};
|
||||
|
||||
std::vector<std::int32_t> natScales;
|
||||
std::transform(
|
||||
numbers.begin(),
|
||||
numbers.end(),
|
||||
std::back_inserter(natScales),
|
||||
[&](Number const& n) { return getNatScale(asset, n); });
|
||||
|
||||
return *std::max_element(natScales.begin(), natScales.end());
|
||||
}
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -900,12 +900,6 @@ public:
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
beast::Journal const&);
|
||||
|
||||
// Compute the coarsest scale required to represent all numbers
|
||||
[[nodiscard]] static std::int32_t
|
||||
computeMinScale(
|
||||
Asset const& asset,
|
||||
std::initializer_list<Number const> numbers);
|
||||
};
|
||||
|
||||
// additional invariant checks can be declared above and then added to this
|
||||
|
||||
@@ -125,7 +125,7 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
tenthBipsOfValue(
|
||||
currentDebtTotal,
|
||||
TenthBips32(sleBroker->at(sfCoverRateMinimum))),
|
||||
currentDebtTotal.scale<STAmount>(vaultAsset));
|
||||
currentDebtTotal.exponent());
|
||||
}();
|
||||
if (coverAvail < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
Reference in New Issue
Block a user