diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index 2b126bc5f..a585770d5 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -478,7 +478,6 @@ target_sources (rippled PRIVATE src/ripple/ledger/impl/BookDirs.cpp src/ripple/ledger/impl/CachedSLEs.cpp src/ripple/ledger/impl/CachedView.cpp - src/ripple/ledger/impl/CashDiff.cpp src/ripple/ledger/impl/Directory.cpp src/ripple/ledger/impl/OpenView.cpp src/ripple/ledger/impl/PaymentSandbox.cpp @@ -833,7 +832,6 @@ target_sources (rippled PRIVATE subdir: ledger #]===============================] src/test/ledger/BookDirs_test.cpp - src/test/ledger/CashDiff_test.cpp src/test/ledger/Directory_test.cpp src/test/ledger/Invariants_test.cpp src/test/ledger/PaymentSandbox_test.cpp diff --git a/src/ripple/app/ledger/OpenLedger.h b/src/ripple/app/ledger/OpenLedger.h index e9aba34fb..42120cf63 100644 --- a/src/ripple/app/ledger/OpenLedger.h +++ b/src/ripple/app/ledger/OpenLedger.h @@ -222,8 +222,7 @@ OpenLedger::apply( { try { - // Dereferencing the iterator can - // throw since it may be transformed. + // Dereferencing the iterator can throw since it may be transformed. auto const tx = *iter; auto const txId = tx->getTransactionID(); if (check.txExists(txId)) @@ -233,9 +232,10 @@ OpenLedger::apply( if (result == Result::retry) retries.insert(tx); } - catch (std::exception const&) + catch (std::exception const& e) { - JLOG(j.error()) << "Caught exception"; + JLOG(j.error()) + << "OpenLedger::apply: Caught exception: " << e.what(); } } bool retry = true; diff --git a/src/ripple/app/ledger/impl/LedgerMaster.cpp b/src/ripple/app/ledger/impl/LedgerMaster.cpp index 0bf69dde0..7bc1bf243 100644 --- a/src/ripple/app/ledger/impl/LedgerMaster.cpp +++ b/src/ripple/app/ledger/impl/LedgerMaster.cpp @@ -638,11 +638,11 @@ LedgerMaster::getValidatedRange(std::uint32_t& minVal, std::uint32_t& maxVal) } return true; } - catch (std::exception&) + catch (std::exception const& e) { - JLOG(m_journal.error()) - << __func__ << " : " - << "Error parsing result of getCompleteLedgers()"; + JLOG(m_journal.error()) << "LedgerMaster::getValidatedRange: " + "exception parsing complete ledgers: " + << e.what(); return false; } } diff --git a/src/ripple/app/tx/impl/CreateOffer.cpp b/src/ripple/app/tx/impl/CreateOffer.cpp index 85a9c4c8a..624d93b7a 100644 --- a/src/ripple/app/tx/impl/CreateOffer.cpp +++ b/src/ripple/app/tx/impl/CreateOffer.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -120,7 +119,7 @@ CreateOffer::preflight(PreflightContext const& ctx) if (saTakerPays.native() != !uPaysIssuerID || saTakerGets.native() != !uGetsIssuerID) { - JLOG(j.warn()) << "Malformed offer: bad issuer"; + JLOG(j.debug()) << "Malformed offer: bad issuer"; return temBAD_ISSUER; } @@ -153,26 +152,24 @@ CreateOffer::preclaim(PreclaimContext const& ctx) if (isGlobalFrozen(ctx.view, uPaysIssuerID) || isGlobalFrozen(ctx.view, uGetsIssuerID)) { - JLOG(ctx.j.info()) << "Offer involves frozen asset"; - + JLOG(ctx.j.debug()) << "Offer involves frozen asset"; return tecFROZEN; } - else if ( - accountFunds(ctx.view, id, saTakerGets, fhZERO_IF_FROZEN, viewJ) <= + + if (accountFunds(ctx.view, id, saTakerGets, fhZERO_IF_FROZEN, viewJ) <= beast::zero) { JLOG(ctx.j.debug()) << "delay: Offers must be at least partially funded."; - return tecUNFUNDED_OFFER; } + // This can probably be simplified to make sure that you cancel sequences // before the transaction sequence number. - else if (cancelSequence && (uAccountSequence <= *cancelSequence)) + if (cancelSequence && (uAccountSequence <= *cancelSequence)) { JLOG(ctx.j.debug()) << "uAccountSequenceNext=" << uAccountSequence << " uOfferSequence=" << *cancelSequence; - return temBAD_SEQUENCE; } @@ -188,8 +185,9 @@ CreateOffer::preclaim(PreclaimContext const& ctx) // Note that this will get checked again in applyGuts, but it saves // us a call to checkAcceptAsset and possible false negative. // - // The return code change is attached to featureChecks as a convenience. - // The change is not big enough to deserve its own amendment. + // The return code change is attached to featureDepositPreauth as a + // convenience, as the change is not big enough to deserve its own + // amendment. return ctx.view.rules().enabled(featureDepositPreauth) ? TER{tecEXPIRED} : TER{tesSUCCESS}; @@ -226,8 +224,9 @@ CreateOffer::checkAcceptAsset( if (!issuerAccount) { - JLOG(j.warn()) << "delay: can't receive IOUs from non-existent issuer: " - << to_string(issue.account); + JLOG(j.debug()) + << "delay: can't receive IOUs from non-existent issuer: " + << to_string(issue.account); return (flags & tapRETRY) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER}; } @@ -861,248 +860,25 @@ CreateOffer::flowCross( return {tecINTERNAL, takerAmount}; } -enum class SBoxCmp { same, dustDiff, offerDelDiff, xrpRound, diff }; - -static std::string -to_string(SBoxCmp c) -{ - switch (c) - { - case SBoxCmp::same: - return "same"; - case SBoxCmp::dustDiff: - return "dust diffs"; - case SBoxCmp::offerDelDiff: - return "offer del diffs"; - case SBoxCmp::xrpRound: - return "XRP round to zero"; - case SBoxCmp::diff: - return "different"; - } - return {}; -} - -static SBoxCmp -compareSandboxes( - char const* name, - ApplyContext const& ctx, - detail::ApplyViewBase const& viewTaker, - detail::ApplyViewBase const& viewFlow, - beast::Journal j) -{ - SBoxCmp c = SBoxCmp::same; - CashDiff diff = cashFlowDiff( - CashFilter::treatZeroOfferAsDeletion, - viewTaker, - CashFilter::none, - viewFlow); - - if (diff.hasDiff()) - { - using namespace beast::severities; - // There is a special case of an offer with XRP on one side where - // the XRP gets rounded to zero. It mostly looks like dust-level - // differences. It is easier to detect if we look for it before - // removing the dust differences. - if (int const side = diff.xrpRoundToZero()) - { - char const* const whichSide = side > 0 ? "; Flow" : "; Taker"; - j.stream(kWarning) - << "FlowCross: " << name << " different" << whichSide - << " XRP rounded to zero. tx: " << ctx.tx.getTransactionID(); - return SBoxCmp::xrpRound; - } - - c = SBoxCmp::dustDiff; - Severity s = kInfo; - std::string diffDesc = ", but only dust."; - diff.rmDust(); - if (diff.hasDiff()) - { - // From here on we want to note the transaction ID of differences. - std::stringstream txIdSs; - txIdSs << ". tx: " << ctx.tx.getTransactionID(); - auto txID = txIdSs.str(); - - // Sometimes one version deletes offers that the other doesn't - // delete. That's okay, but keep track of it. - c = SBoxCmp::offerDelDiff; - s = kWarning; - int sides = diff.rmLhsDeletedOffers() ? 1 : 0; - sides |= diff.rmRhsDeletedOffers() ? 2 : 0; - if (!diff.hasDiff()) - { - char const* t = ""; - switch (sides) - { - case 1: - t = "; Taker deleted more offers"; - break; - case 2: - t = "; Flow deleted more offers"; - break; - case 3: - t = "; Taker and Flow deleted different offers"; - break; - default: - break; - } - diffDesc = std::string(t) + txID; - } - else - { - // A difference without a broad classification... - c = SBoxCmp::diff; - std::stringstream ss; - ss << "; common entries: " << diff.commonCount() - << "; Taker unique: " << diff.lhsOnlyCount() - << "; Flow unique: " << diff.rhsOnlyCount() << txID; - diffDesc = ss.str(); - } - } - j.stream(s) << "FlowCross: " << name << " different" << diffDesc; - } - return c; -} - std::pair CreateOffer::cross(Sandbox& sb, Sandbox& sbCancel, Amounts const& takerAmount) { - using beast::zero; - - // There are features for Flow offer crossing and for comparing results - // between Taker and Flow offer crossing. Turn those into bools. - bool const useFlowCross{sb.rules().enabled(featureFlowCross)}; - bool const doCompare{sb.rules().enabled(featureCompareTakerFlowCross)}; + if (sb.rules().enabled(featureFlowCross)) + { + PaymentSandbox psbFlow{&sb}; + PaymentSandbox psbCancelFlow{&sbCancel}; + auto const ret = flowCross(psbFlow, psbCancelFlow, takerAmount); + psbFlow.apply(sb); + psbCancelFlow.apply(sbCancel); + return ret; + } Sandbox sbTaker{&sb}; Sandbox sbCancelTaker{&sbCancel}; - auto const takerR = (!useFlowCross || doCompare) - ? takerCross(sbTaker, sbCancelTaker, takerAmount) - : std::make_pair(tecINTERNAL, takerAmount); - - PaymentSandbox psbFlow{&sb}; - PaymentSandbox psbCancelFlow{&sbCancel}; - auto const flowR = (useFlowCross || doCompare) - ? flowCross(psbFlow, psbCancelFlow, takerAmount) - : std::make_pair(tecINTERNAL, takerAmount); - - if (doCompare) - { - SBoxCmp c = SBoxCmp::same; - if (takerR.first != flowR.first) - { - c = SBoxCmp::diff; - j_.warn() << "FlowCross: Offer cross tec codes different. tx: " - << ctx_.tx.getTransactionID(); - } - else if ( - (takerR.second.in == zero && flowR.second.in == zero) || - (takerR.second.out == zero && flowR.second.out == zero)) - { - c = compareSandboxes( - "Both Taker and Flow fully crossed", - ctx_, - sbTaker, - psbFlow, - j_); - } - else if (takerR.second.in == zero && takerR.second.out == zero) - { - char const* crossType = - "Taker fully crossed, Flow partially crossed"; - if (flowR.second.in == takerAmount.in && - flowR.second.out == takerAmount.out) - crossType = "Taker fully crossed, Flow not crossed"; - - c = compareSandboxes(crossType, ctx_, sbTaker, psbFlow, j_); - } - else if (flowR.second.in == zero && flowR.second.out == zero) - { - char const* crossType = - "Taker partially crossed, Flow fully crossed"; - if (takerR.second.in == takerAmount.in && - takerR.second.out == takerAmount.out) - crossType = "Taker not crossed, Flow fully crossed"; - - c = compareSandboxes(crossType, ctx_, sbTaker, psbFlow, j_); - } - else if (ctx_.tx.getFlags() & tfFillOrKill) - { - c = compareSandboxes( - "FillOrKill offer", ctx_, sbCancelTaker, psbCancelFlow, j_); - } - else if ( - takerR.second.in == takerAmount.in && - flowR.second.in == takerAmount.in && - takerR.second.out == takerAmount.out && - flowR.second.out == takerAmount.out) - { - char const* crossType = "Neither Taker nor Flow crossed"; - c = compareSandboxes(crossType, ctx_, sbTaker, psbFlow, j_); - } - else if ( - takerR.second.in == takerAmount.in && - takerR.second.out == takerAmount.out) - { - char const* crossType = "Taker not crossed, Flow partially crossed"; - c = compareSandboxes(crossType, ctx_, sbTaker, psbFlow, j_); - } - else if ( - flowR.second.in == takerAmount.in && - flowR.second.out == takerAmount.out) - { - char const* crossType = "Taker partially crossed, Flow not crossed"; - c = compareSandboxes(crossType, ctx_, sbTaker, psbFlow, j_); - } - else - { - c = compareSandboxes( - "Partial cross offer", ctx_, sbTaker, psbFlow, j_); - - // If we've gotten this far then the returned amounts matter. - if (c <= SBoxCmp::dustDiff && takerR.second != flowR.second) - { - c = SBoxCmp::dustDiff; - using namespace beast::severities; - Severity s = kInfo; - std::string onlyDust = ", but only dust."; - if (!diffIsDust(takerR.second.in, flowR.second.in) || - (!diffIsDust(takerR.second.out, flowR.second.out))) - { - char const* outSame = ""; - if (takerR.second.out == flowR.second.out) - outSame = " but outs same"; - - c = SBoxCmp::diff; - s = kWarning; - std::stringstream ss; - ss << outSame - << ". Taker in: " << takerR.second.in.getText() - << "; Taker out: " << takerR.second.out.getText() - << "; Flow in: " << flowR.second.in.getText() - << "; Flow out: " << flowR.second.out.getText() - << ". tx: " << ctx_.tx.getTransactionID(); - onlyDust = ss.str(); - } - j_.stream(s) - << "FlowCross: Partial cross amounts different" << onlyDust; - } - } - j_.error() << "FlowCross cmp result: " << to_string(c); - } - - // Return one result or the other based on amendment. - if (useFlowCross) - { - psbFlow.apply(sb); - psbCancelFlow.apply(sbCancel); - return flowR; - } - + auto const ret = takerCross(sbTaker, sbCancelTaker, takerAmount); sbTaker.apply(sb); sbCancelTaker.apply(sbCancel); - return takerR; + return ret; } std::string diff --git a/src/ripple/ledger/CashDiff.h b/src/ripple/ledger/CashDiff.h deleted file mode 100644 index 3b1dea364..000000000 --- a/src/ripple/ledger/CashDiff.h +++ /dev/null @@ -1,172 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2016 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 RIPPLE_LEDGER_CASHDIFF_H_INCLUDED -#define RIPPLE_LEDGER_CASHDIFF_H_INCLUDED - -#include -#include -#include // std::unique_ptr - -namespace ripple { - -class ReadView; - -namespace detail { - -class ApplyStateTable; - -} - -// Used by CashDiff to specify filters applied while processing differences. -// Entries are bit flags that can be ANDed and ORed. -enum class CashFilter : std::uint8_t { - none = 0x0, - treatZeroOfferAsDeletion = 0x1 -}; -inline CashFilter -operator|(CashFilter lhs, CashFilter rhs) -{ - using ul_t = std::underlying_type::type; - return static_cast(safe_cast(lhs) | safe_cast(rhs)); -} -inline CashFilter -operator&(CashFilter lhs, CashFilter rhs) -{ - using ul_t = std::underlying_type::type; - return static_cast(safe_cast(lhs) & safe_cast(rhs)); -} - -//------------------------------------------------------------------------------ - -// A class to identify differences between two ApplyStateTable instances -// for debugging. -class CashDiff -{ -public: - CashDiff() = delete; - CashDiff(CashDiff const&) = delete; - CashDiff(CashDiff&& other) noexcept; - CashDiff& - operator=(CashDiff const&) = delete; - ~CashDiff(); - - CashDiff( - ReadView const& view, - CashFilter lhsFilter, - detail::ApplyStateTable const& lhs, - CashFilter rhsFilter, - detail::ApplyStateTable const& rhs); - - // Returns the number of cases where lhs and rhs had the same entries - // (but not necessarily the same amounts) - std::size_t - commonCount() const; - - // Returns the number of entries that were present in rhs but not in lhs. - std::size_t - rhsOnlyCount() const; - - // Returns the number of entries that were present in lhs but not in rhs. - std::size_t - lhsOnlyCount() const; - - // Returns true is there are any differences to report. - bool - hasDiff() const; - - // Checks for the XRP round-to-zero case. Returns zero if not detected. - // Otherwise returns -1 if seen on lhs, +1 if seen on rhs. - // - // For tiny offers of TakerPays IOU and TakerGets XRP, cases have been - // observed where XRP rounding allows a tiny amount of IOU to be - // removed from an Offer while returning no XRP to the offer owner. - // That's because the XRP amount was rounded down to zero drops. - // - // The person submitting the tiny offer does not, however, get something - // for nothing. The transaction's fee is significantly larger than the - // value of the received IOU. - // - // This check should be made before calling rmDust(). - int - xrpRoundToZero() const; - - // Remove dust-sized differences. Returns true is dust was removed. - bool - rmDust(); - - // Remove offer deletion differences from a given side. Returns true - // if any deleted offers were removed from the differences. - bool - rmLhsDeletedOffers(); - bool - rmRhsDeletedOffers(); - - struct OfferAmounts - { - static std::size_t constexpr count_ = 2; - static std::size_t constexpr count() - { - return count_; - } - STAmount amounts[count_]; - STAmount const& - takerPays() const - { - return amounts[0]; - } - STAmount const& - takerGets() const - { - return amounts[1]; - } - STAmount const& - operator[](std::size_t i) const - { - assert(i < count()); - return amounts[i]; - } - friend bool - operator<(OfferAmounts const& lhs, OfferAmounts const& rhs) - { - if (lhs[0] < rhs[0]) - return true; - if (lhs[0] > rhs[0]) - return false; - return lhs[1] < rhs[1]; - } - }; - -private: - class Impl; - std::unique_ptr impl_; -}; - -// Return true if the difference between two STAmounts is "small". -// -// If v1 and v2 have different issues, then their difference is never dust. -// If v1 < v2, smallness is computed as v1 / (v2 - v1). -// The e10 argument says at least how big that ratio must be. Default is 10^6. -// If both v1 and v2 are XRP, consider any diff of 2 drops or less to be dust. -bool -diffIsDust(STAmount const& v1, STAmount const& v2, std::uint8_t e10 = 6); - -} // namespace ripple - -#endif diff --git a/src/ripple/ledger/detail/ApplyViewBase.h b/src/ripple/ledger/detail/ApplyViewBase.h index 7fa34b5fb..68c210c7b 100644 --- a/src/ripple/ledger/detail/ApplyViewBase.h +++ b/src/ripple/ledger/detail/ApplyViewBase.h @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -120,13 +119,6 @@ public: void rawDestroyXRP(XRPAmount const& feeDrops) override; - friend CashDiff - cashFlowDiff( - CashFilter lhsFilter, - ApplyViewBase const& lhs, - CashFilter rhsFilter, - ApplyViewBase const& rhs); - protected: ApplyFlags flags_; ReadView const* base_; diff --git a/src/ripple/ledger/impl/ApplyViewBase.cpp b/src/ripple/ledger/impl/ApplyViewBase.cpp index 4f92b9721..ac87aabb4 100644 --- a/src/ripple/ledger/impl/ApplyViewBase.cpp +++ b/src/ripple/ledger/impl/ApplyViewBase.cpp @@ -18,7 +18,6 @@ //============================================================================== #include -#include #include namespace ripple { @@ -175,18 +174,5 @@ ApplyViewBase::rawDestroyXRP(XRPAmount const& fee) items_.destroyXRP(fee); } -//--- - -CashDiff -cashFlowDiff( - CashFilter lhsFilter, - ApplyViewBase const& lhs, - CashFilter rhsFilter, - ApplyViewBase const& rhs) -{ - assert(lhs.base_ == rhs.base_); - return CashDiff{*lhs.base_, lhsFilter, lhs.items_, rhsFilter, rhs.items_}; -} - } // namespace detail } // namespace ripple diff --git a/src/ripple/ledger/impl/CashDiff.cpp b/src/ripple/ledger/impl/CashDiff.cpp deleted file mode 100644 index d2c915b32..000000000 --- a/src/ripple/ledger/impl/CashDiff.cpp +++ /dev/null @@ -1,846 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2016 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. -*/ -//============================================================================== - -#include -#include -#include -#include -#include -#include // std::abs() -#include - -namespace ripple { -namespace detail { - -// Data structure that summarize cash changes in a single ApplyStateTable. -struct CashSummary -{ - explicit CashSummary() = default; - - // Sorted vectors. All of the vectors fill in for std::maps. - std::vector> xrpChanges; - - std::vector, STAmount>> - trustChanges; - - std::vector, bool>> - trustDeletions; - - std::vector< - std::pair, CashDiff::OfferAmounts>> - offerChanges; - - // Note that the OfferAmounts hold the amount *prior* to deletion. - std::vector< - std::pair, CashDiff::OfferAmounts>> - offerDeletions; - - bool - hasDiff() const - { - return !xrpChanges.empty() || !trustChanges.empty() || - !trustDeletions.empty() || !offerChanges.empty() || - !offerDeletions.empty(); - } - - void - reserve(size_t newCap) - { - xrpChanges.reserve(newCap); - trustChanges.reserve(newCap); - trustDeletions.reserve(newCap); - offerChanges.reserve(newCap); - offerDeletions.reserve(newCap); - } - - void - shrink_to_fit() - { - xrpChanges.shrink_to_fit(); - trustChanges.shrink_to_fit(); - trustDeletions.shrink_to_fit(); - offerChanges.shrink_to_fit(); - offerDeletions.shrink_to_fit(); - } - - void - sort() - { - std::sort(xrpChanges.begin(), xrpChanges.end()); - std::sort(trustChanges.begin(), trustChanges.end()); - std::sort(trustDeletions.begin(), trustDeletions.end()); - std::sort(offerChanges.begin(), offerChanges.end()); - std::sort(offerDeletions.begin(), offerDeletions.end()); - } -}; - -// treatZeroOfferAsDeletion() -// -// Older payment code might set an Offer's TakerPays and TakerGets to -// zero and let the offer be cleaned up later. A more recent version -// may be more proactive about removing offers. We attempt to paper -// over that difference here. -// -// Two conditions are checked: -// -// o A modified Offer with both TakerPays and TakerGets set to zero is -// added to offerDeletions (not offerChanges). -// -// o Any deleted offer that was zero before deletion is ignored. It will -// have been treated as deleted when the offer was first set to zero. -// -// The returned bool indicates whether the passed in data was handled. -// This allows the caller to avoid further handling. -static bool -treatZeroOfferAsDeletion( - CashSummary& result, - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) -{ - using beast::zero; - - if (!before) - return false; - - auto const& prev = *before; - - if (isDelete) - { - if (prev.getType() == ltOFFER && prev[sfTakerPays] == zero && - prev[sfTakerGets] == zero) - { - // Offer was previously treated as deleted when it was zeroed. - return true; - } - } - else - { - // modify - if (!after) - return false; - - auto const& cur = *after; - if (cur.getType() == ltOFFER && cur[sfTakerPays] == zero && - cur[sfTakerGets] == zero) - { - // Either ignore or treat as delete. - auto const oldTakerPays = prev[sfTakerPays]; - auto const oldTakerGets = prev[sfTakerGets]; - if (oldTakerPays != zero && oldTakerGets != zero) - { - result.offerDeletions.push_back(std::make_pair( - std::make_tuple(prev[sfAccount], prev[sfSequence]), - CashDiff::OfferAmounts{{oldTakerPays, oldTakerGets}})); - return true; - } - } - } - return false; -} - -static bool -getBasicCashFlow( - CashSummary& result, - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) -{ - if (isDelete) - { - if (!before) - return false; - - auto const& prev = *before; - switch (prev.getType()) - { - case ltACCOUNT_ROOT: - result.xrpChanges.push_back( - std::make_pair(prev[sfAccount], XRPAmount{0})); - return true; - - case ltRIPPLE_STATE: - result.trustDeletions.push_back(std::make_pair( - std::make_tuple( - prev[sfLowLimit].getIssuer(), - prev[sfHighLimit].getIssuer(), - prev[sfBalance].getCurrency()), - false)); - return true; - - case ltOFFER: - result.offerDeletions.push_back(std::make_pair( - std::make_tuple(prev[sfAccount], prev[sfSequence]), - CashDiff::OfferAmounts{ - {prev[sfTakerPays], prev[sfTakerGets]}})); - return true; - - default: - return false; - } - } - else - { - // insert or modify - if (!after) - { - assert(after); - return false; - } - - auto const& cur = *after; - switch (cur.getType()) - { - case ltACCOUNT_ROOT: { - auto const curXrp = cur[sfBalance].xrp(); - if (!before || (*before)[sfBalance].xrp() != curXrp) - result.xrpChanges.push_back( - std::make_pair(cur[sfAccount], curXrp)); - return true; - } - case ltRIPPLE_STATE: { - auto const curBalance = cur[sfBalance]; - if (!before || (*before)[sfBalance] != curBalance) - result.trustChanges.push_back(std::make_pair( - std::make_tuple( - cur[sfLowLimit].getIssuer(), - cur[sfHighLimit].getIssuer(), - curBalance.getCurrency()), - curBalance)); - return true; - } - case ltOFFER: { - auto const curTakerPays = cur[sfTakerPays]; - auto const curTakerGets = cur[sfTakerGets]; - if (!before || (*before)[sfTakerGets] != curTakerGets || - (*before)[sfTakerPays] != curTakerPays) - { - result.offerChanges.push_back(std::make_pair( - std::make_tuple(cur[sfAccount], cur[sfSequence]), - CashDiff::OfferAmounts{{curTakerPays, curTakerGets}})); - } - return true; - } - default: - break; - } - } - return false; -} - -// Extract the final cash state from an ApplyStateTable. -static CashSummary -getCashFlow(ReadView const& view, CashFilter f, ApplyStateTable const& table) -{ - CashSummary result; - result.reserve(table.size()); - - // Make a container of filters based on the passed in filter flags. - using FuncType = decltype(&getBasicCashFlow); - boost::container::static_vector filters; - - if ((f & CashFilter::treatZeroOfferAsDeletion) != CashFilter::none) - filters.push_back(treatZeroOfferAsDeletion); - - filters.push_back(&getBasicCashFlow); - - auto each = [&result, &filters]( - uint256 const& key, - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) { - auto discarded = std::find_if( - filters.begin(), - filters.end(), - [&result, isDelete, &before, &after](FuncType func) { - return func(result, isDelete, before, after); - }); - (void)discarded; - }; - - table.visit(view, each); - result.sort(); - result.shrink_to_fit(); - return result; -} - -} // namespace detail - -//------------------------------------------------------------------------------ - -// Holds all of the CashDiff-related data. -class CashDiff::Impl -{ -private: - // Note differences in destroyed XRP between two ApplyStateTables. - struct DropsGone - { - XRPAmount lhs; - XRPAmount rhs; - }; - - ReadView const& view_; - - std::size_t commonKeys_ = 0; // Number of keys common to both rhs and lhs. - std::size_t lhsKeys_ = 0; // Number of keys in lhs but not rhs. - std::size_t rhsKeys_ = 0; // Number of keys in rhs but not lhs. - std::optional dropsGone_; - detail::CashSummary lhsDiffs_; - detail::CashSummary rhsDiffs_; - -public: - // Constructor. - Impl( - ReadView const& view, - CashFilter lhsFilter, - detail::ApplyStateTable const& lhs, - CashFilter rhsFilter, - detail::ApplyStateTable const& rhs) - : view_(view) - { - findDiffs(lhsFilter, lhs, rhsFilter, rhs); - } - - std::size_t - commonCount() const - { - return commonKeys_; - } - - std::size_t - lhsOnlyCount() const - { - return lhsKeys_; - } - - std::size_t - rhsOnlyCount() const - { - return rhsKeys_; - } - - bool - hasDiff() const - { - return dropsGone_ != std::nullopt || lhsDiffs_.hasDiff() || - rhsDiffs_.hasDiff(); - } - - int - xrpRoundToZero() const; - - // Filter out differences that are small enough to be in the floating - // point noise. - bool - rmDust(); - - // Remove offer deletion differences from a given side - bool - rmLhsDeletedOffers(); - bool - rmRhsDeletedOffers(); - -private: - void - findDiffs( - CashFilter lhsFilter, - detail::ApplyStateTable const& lhs, - CashFilter rhsFilter, - detail::ApplyStateTable const& rhs); -}; - -// Template function to count difference types in individual CashDiff vectors. -// Assumes those vectors are sorted. -// -// Returned array: -// [0] count of keys present in both vectors. -// [1] count of keys present in lhs only. -// [2] count of keys present in rhs only. -template -static std::array -countKeys( - std::vector> const& lhs, - std::vector> const& rhs) -{ - std::array ret{}; // Zero initialize; - - auto lhsItr = lhs.cbegin(); - auto rhsItr = rhs.cbegin(); - - while (lhsItr != lhs.cend() || rhsItr != rhs.cend()) - { - if (lhsItr == lhs.cend()) - { - // rhs has an entry that is not in lhs. - ret[2] += 1; - ++rhsItr; - } - else if (rhsItr == rhs.cend()) - { - // lhs has an entry that is not in rhs. - ret[1] += 1; - ++lhsItr; - } - else if (lhsItr->first < rhsItr->first) - { - // This key is only in lhs. - ret[1] += 1; - ++lhsItr; - } - else if (rhsItr->first < lhsItr->first) - { - // This key is only in rhs. - ret[2] += 1; - ++rhsItr; - } - else - { - // The equivalent key is present in both vectors. - ret[0] += 1; - ++lhsItr; - ++rhsItr; - } - } - return ret; -} - -// Given two CashSummary instances, count the keys. Assumes both -// CashSummaries have sorted entries. -// -// Returned array: -// [0] count of keys present in both vectors. -// [1] count of keys present in lhs only. -// [2] count of keys present in rhs only. -static std::array -countKeys(detail::CashSummary const& lhs, detail::CashSummary const& rhs) -{ - std::array ret{}; // Zero initialize; - - // Lambda to add in new results. - auto addIn = [&ret](std::array const& a) { - std::transform( - a.cbegin(), - a.cend(), - ret.cbegin(), - ret.begin(), - std::plus()); - }; - addIn(countKeys(lhs.xrpChanges, rhs.xrpChanges)); - addIn(countKeys(lhs.trustChanges, rhs.trustChanges)); - addIn(countKeys(lhs.trustDeletions, rhs.trustDeletions)); - addIn(countKeys(lhs.offerChanges, rhs.offerChanges)); - addIn(countKeys(lhs.offerDeletions, rhs.offerDeletions)); - return ret; -} - -int -CashDiff::Impl::xrpRoundToZero() const -{ - // The case has one OfferChange that is present on both lhs_ and rhs_. - // That OfferChange should have XRP for TakerGets. There should be a 1 - // drop difference between the TakerGets of lhsDiffs_ and rhsDiffs_. - if (lhsDiffs_.offerChanges.size() != 1 || - rhsDiffs_.offerChanges.size() != 1) - return 0; - - if (!lhsDiffs_.offerChanges[0].second.takerGets().native() || - !rhsDiffs_.offerChanges[0].second.takerGets().native()) - return 0; - - bool const lhsBigger = - lhsDiffs_.offerChanges[0].second.takerGets().mantissa() > - rhsDiffs_.offerChanges[0].second.takerGets().mantissa(); - - detail::CashSummary const& bigger = lhsBigger ? lhsDiffs_ : rhsDiffs_; - detail::CashSummary const& smaller = lhsBigger ? rhsDiffs_ : lhsDiffs_; - if (bigger.offerChanges[0].second.takerGets().mantissa() - - smaller.offerChanges[0].second.takerGets().mantissa() != - 1) - return 0; - - // The side with the smaller XRP balance in the OfferChange should have - // two XRP differences. The other side should have no XRP differences. - if (smaller.xrpChanges.size() != 2) - return 0; - if (!bigger.xrpChanges.empty()) - return 0; - - // There should be no other differences. - if (!smaller.trustChanges.empty() || !bigger.trustChanges.empty() || - !smaller.trustDeletions.empty() || !bigger.trustDeletions.empty() || - !smaller.offerDeletions.empty() || !bigger.offerDeletions.empty()) - return 0; - - // Return which side exhibited the problem. - return lhsBigger ? -1 : 1; -} - -// Function that compares two CashDiff::OfferAmounts and returns true if -// the difference is dust-sized. -static bool -diffIsDust(CashDiff::OfferAmounts const& lhs, CashDiff::OfferAmounts const& rhs) -{ - for (auto i = 0; i < lhs.count(); ++i) - { - if (!diffIsDust(lhs[i], rhs[i])) - return false; - } - return true; -} - -// Template function to remove dust from individual CashDiff vectors. -template -static bool -rmVecDust( - std::vector>& lhs, - std::vector>& rhs, - L&& justDust) -{ - static_assert( - std::is_same:: - value, - "Invalid lambda passed to rmVecDust"); - - bool dustWasRemoved = false; - auto lhsItr = lhs.begin(); - while (lhsItr != lhs.end()) - { - using value_t = std::pair; - auto const found = std::equal_range( - rhs.begin(), - rhs.end(), - *lhsItr, - [](value_t const& a, value_t const& b) { - return a.first < b.first; - }); - - if (found.first != found.second) - { - // The same entry changed for both lhs and rhs. Check whether - // the differences are small enough to be removed. - if (justDust(lhsItr->second, found.first->second)) - { - dustWasRemoved = true; - rhs.erase(found.first); - // Dodge an invalid iterator by using erase's return value. - lhsItr = lhs.erase(lhsItr); - continue; - } - } - ++lhsItr; - } - return dustWasRemoved; -} - -bool -CashDiff::Impl::rmDust() -{ - bool removedDust = false; - - // Four of the containers can have small (floating point style) - // amount differences: xrpChanges, trustChanges, offerChanges, and - // offerDeletions. Rifle through those containers and remove any - // entries that are _almost_ the same between lhs and rhs. - - // xrpChanges. We call a difference of 2 drops or less dust. - removedDust |= rmVecDust( - lhsDiffs_.xrpChanges, - rhsDiffs_.xrpChanges, - [](XRPAmount const& lhs, XRPAmount const& rhs) { - return diffIsDust(lhs, rhs); - }); - - // trustChanges. - removedDust |= rmVecDust( - lhsDiffs_.trustChanges, - rhsDiffs_.trustChanges, - [](STAmount const& lhs, STAmount const& rhs) { - return diffIsDust(lhs, rhs); - }); - - // offerChanges. - removedDust |= rmVecDust( - lhsDiffs_.offerChanges, - rhsDiffs_.offerChanges, - [](CashDiff::OfferAmounts const& lhs, - CashDiff::OfferAmounts const& rhs) { return diffIsDust(lhs, rhs); }); - - // offerDeletions. - removedDust |= rmVecDust( - lhsDiffs_.offerDeletions, - rhsDiffs_.offerDeletions, - [](CashDiff::OfferAmounts const& lhs, - CashDiff::OfferAmounts const& rhs) { return diffIsDust(lhs, rhs); }); - - return removedDust; -} - -bool -CashDiff::Impl::rmLhsDeletedOffers() -{ - bool const ret = !lhsDiffs_.offerDeletions.empty(); - if (ret) - lhsDiffs_.offerDeletions.clear(); - return ret; -} - -bool -CashDiff::Impl::rmRhsDeletedOffers() -{ - bool const ret = !rhsDiffs_.offerDeletions.empty(); - if (ret) - rhsDiffs_.offerDeletions.clear(); - return ret; -} - -// Deposits differences between two sorted vectors into a destination. -template -static void -setDiff(std::vector const& a, std::vector const& b, std::vector& dest) -{ - dest.clear(); - std::set_difference( - a.cbegin(), - a.cend(), - b.cbegin(), - b.cend(), - std::inserter(dest, dest.end())); -} - -void -CashDiff::Impl::findDiffs( - CashFilter lhsFilter, - detail::ApplyStateTable const& lhs, - CashFilter rhsFilter, - detail::ApplyStateTable const& rhs) -{ - // If dropsDestroyed_ is different, note that. - if (lhs.dropsDestroyed() != rhs.dropsDestroyed()) - { - dropsGone_ = DropsGone{lhs.dropsDestroyed(), rhs.dropsDestroyed()}; - } - - // Extract cash flow changes from the state tables - auto lhsDiffs = getCashFlow(view_, lhsFilter, lhs); - auto rhsDiffs = getCashFlow(view_, rhsFilter, rhs); - - // Get statistics on keys. - auto const counts = countKeys(lhsDiffs, rhsDiffs); - commonKeys_ = counts[0]; - lhsKeys_ = counts[1]; - rhsKeys_ = counts[2]; - - // Save only the differences between the results. - // xrpChanges: - setDiff(lhsDiffs.xrpChanges, rhsDiffs.xrpChanges, lhsDiffs_.xrpChanges); - setDiff(rhsDiffs.xrpChanges, lhsDiffs.xrpChanges, rhsDiffs_.xrpChanges); - - // trustChanges: - setDiff( - lhsDiffs.trustChanges, rhsDiffs.trustChanges, lhsDiffs_.trustChanges); - setDiff( - rhsDiffs.trustChanges, lhsDiffs.trustChanges, rhsDiffs_.trustChanges); - - // trustDeletions: - setDiff( - lhsDiffs.trustDeletions, - rhsDiffs.trustDeletions, - lhsDiffs_.trustDeletions); - setDiff( - rhsDiffs.trustDeletions, - lhsDiffs.trustDeletions, - rhsDiffs_.trustDeletions); - - // offerChanges: - setDiff( - lhsDiffs.offerChanges, rhsDiffs.offerChanges, lhsDiffs_.offerChanges); - setDiff( - rhsDiffs.offerChanges, lhsDiffs.offerChanges, rhsDiffs_.offerChanges); - - // offerDeletions: - setDiff( - lhsDiffs.offerDeletions, - rhsDiffs.offerDeletions, - lhsDiffs_.offerDeletions); - setDiff( - rhsDiffs.offerDeletions, - lhsDiffs.offerDeletions, - rhsDiffs_.offerDeletions); -} - -//------------------------------------------------------------------------------ - -// Locates differences between two ApplyStateTables. -CashDiff::CashDiff(CashDiff&& other) noexcept : impl_(std::move(other.impl_)) -{ -} - -CashDiff::~CashDiff() = default; - -CashDiff::CashDiff( - ReadView const& view, - CashFilter lhsFilter, - detail::ApplyStateTable const& lhs, - CashFilter rhsFilter, - detail::ApplyStateTable const& rhs) - : impl_(new Impl(view, lhsFilter, lhs, rhsFilter, rhs)) -{ -} - -std::size_t -CashDiff::commonCount() const -{ - return impl_->commonCount(); -} - -std::size_t -CashDiff::rhsOnlyCount() const -{ - return impl_->rhsOnlyCount(); -} - -std::size_t -CashDiff::lhsOnlyCount() const -{ - return impl_->lhsOnlyCount(); -} - -bool -CashDiff::hasDiff() const -{ - return impl_->hasDiff(); -} - -int -CashDiff::xrpRoundToZero() const -{ - return impl_->xrpRoundToZero(); -} - -bool -CashDiff::rmDust() -{ - return impl_->rmDust(); -} - -bool -CashDiff::rmLhsDeletedOffers() -{ - return impl_->rmLhsDeletedOffers(); -} - -bool -CashDiff::rmRhsDeletedOffers() -{ - return impl_->rmRhsDeletedOffers(); -} - -//------------------------------------------------------------------------------ - -// Function that compares two STAmounts and returns true if the difference -// is dust-sized. -bool -diffIsDust(STAmount const& v1, STAmount const& v2, std::uint8_t e10) -{ - // If one value is positive and the other negative then there's something - // odd afoot. - if (v1 != beast::zero && v2 != beast::zero && - (v1.negative() != v2.negative())) - return false; - - // v1 and v2 must be the same Issue for their difference to make sense. - if (v1.native() != v2.native()) - return false; - - if (!v1.native() && (v1.issue() != v2.issue())) - return false; - - // If v1 == v2 then the dust is vanishingly small. - if (v1 == v2) - return true; - - STAmount const& small = v1 < v2 ? v1 : v2; - STAmount const& large = v1 < v2 ? v2 : v1; - - // Handling XRP is different from IOU. - if (v1.native()) - { - std::uint64_t const s = small.mantissa(); - std::uint64_t const l = large.mantissa(); - - // Always allow a couple of drops of noise. - if (l - s <= 2) - return true; - - static_assert(sizeof(1ULL) == sizeof(std::uint64_t), ""); - std::uint64_t const ratio = s / (l - s); - static constexpr std::uint64_t e10Lookup[]{ - 1ULL, - 10ULL, - 100ULL, - 1'000ULL, - 10'000ULL, - 100'000ULL, - 1'000'000ULL, - 10'000'000ULL, - 100'000'000ULL, - 1'000'000'000ULL, - 10'000'000'000ULL, - 100'000'000'000ULL, - 1'000'000'000'000ULL, - 10'000'000'000'000ULL, - 100'000'000'000'000ULL, - 1'000'000'000'000'000ULL, - 10'000'000'000'000'000ULL, - 100'000'000'000'000'000ULL, - 1'000'000'000'000'000'000ULL, - 10'000'000'000'000'000'000ULL, - }; - static std::size_t constexpr maxIndex = - sizeof(e10Lookup) / sizeof e10Lookup[0]; - - // Make sure the table is big enough. - static_assert( - std::numeric_limits::max() / - e10Lookup[maxIndex - 1] < - 10, - "Table too small"); - - if (e10 >= maxIndex) - return false; - - return ratio >= e10Lookup[e10]; - } - - // Non-native. Note that even though large and small may not be equal, - // their difference may be zero. One way that can happen is if two - // values are different, but their difference results in an STAmount - // with an exponent less than -96. - STAmount const diff = large - small; - if (diff == beast::zero) - return true; - - STAmount const ratio = divide(small, diff, v1.issue()); - STAmount const one(v1.issue(), 1); - int const ratioExp = ratio.exponent() - one.exponent(); - - return ratioExp >= e10; -}; - -} // namespace ripple diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 37a787ab9..3fb67fa08 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -351,7 +351,6 @@ foreachFeature(FeatureBitset bs, F&& f) extern uint256 const featureOwnerPaysFee; extern uint256 const featureFlow; -extern uint256 const featureCompareTakerFlowCross; extern uint256 const featureFlowCross; extern uint256 const featureCryptoConditionsSuite; extern uint256 const fix1513; diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index 805a48462..5c7910fe9 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -166,7 +166,6 @@ bitsetIndexToFeature(size_t i) uint256 const featureOwnerPaysFee = *getRegisteredFeature("OwnerPaysFee"), featureFlow = *getRegisteredFeature("Flow"), - featureCompareTakerFlowCross = *getRegisteredFeature("CompareTakerFlowCross"), featureFlowCross = *getRegisteredFeature("FlowCross"), featureCryptoConditionsSuite = *getRegisteredFeature("CryptoConditionsSuite"), fix1513 = *getRegisteredFeature("fix1513"), diff --git a/src/test/ledger/CashDiff_test.cpp b/src/test/ledger/CashDiff_test.cpp deleted file mode 100644 index c3250c7a3..000000000 --- a/src/test/ledger/CashDiff_test.cpp +++ /dev/null @@ -1,112 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2016 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. -*/ -//============================================================================== - -#include -#include -#include -#include - -namespace ripple { -namespace test { - -class CashDiff_test : public beast::unit_test::suite -{ - static_assert(!std::is_default_constructible{}, ""); - static_assert(!std::is_copy_constructible{}, ""); - static_assert(!std::is_copy_assignable{}, ""); - static_assert(std::is_nothrow_move_constructible{}, ""); - static_assert(!std::is_move_assignable{}, ""); - - // Exercise diffIsDust (STAmount, STAmount) - void - testDust() - { - testcase("diffIsDust (STAmount, STAmount)"); - - Issue const usd(Currency(0x5553440000000000), AccountID(0x4985601)); - Issue const usf(Currency(0x5553460000000000), AccountID(0x4985601)); - - // Positive and negative are never dust. - expect(!diffIsDust(STAmount{usd, 1}, STAmount{usd, -1})); - - // Different issues are never dust. - expect(!diffIsDust(STAmount{usd, 1}, STAmount{usf, 1})); - - // Native and non-native are never dust. - expect(!diffIsDust(STAmount{usd, 1}, STAmount{1})); - - // Equal values are always dust. - expect(diffIsDust(STAmount{0}, STAmount{0})); - { - // Test IOU. - std::uint64_t oldProbe = 0; - std::uint64_t newProbe = 10; - std::uint8_t e10 = 1; - do - { - STAmount large(usd, newProbe + 1); - STAmount small(usd, newProbe); - - expect(diffIsDust(large, small, e10)); - expect(diffIsDust(large, small, e10 + 1) == (e10 > 13)); - - oldProbe = newProbe; - newProbe = oldProbe * 10; - e10 += 1; - } while (newProbe > oldProbe && - newProbe < std::numeric_limits::max()); - } - { - // Test XRP. - // A delta of 2 or less is always dust. - expect(diffIsDust(STAmount{2}, STAmount{0})); - - std::uint64_t oldProbe = 0; - std::uint64_t newProbe = 10; - std::uint8_t e10 = 0; - do - { - // Differences of 2 of fewer drops are always treated as dust, - // so use a delta of 3. - STAmount large(newProbe + 3); - STAmount small(newProbe); - - expect(diffIsDust(large, small, e10)); - expect(diffIsDust(large, small, e10 + 1) == (e10 >= 20)); - - oldProbe = newProbe; - newProbe = oldProbe * 10; - e10 += 1; - } while (newProbe > oldProbe && - newProbe < std::numeric_limits::max()); - } - } - -public: - void - run() override - { - testDust(); - } -}; - -BEAST_DEFINE_TESTSUITE(CashDiff, ledger, ripple); - -} // namespace test -} // namespace ripple