From ab9735cb71e7ac84277dd665c6f0780c63b48769 Mon Sep 17 00:00:00 2001 From: Edward Hennis Date: Thu, 14 Feb 2019 18:59:07 -0500 Subject: [PATCH] Add units to all fee calculations: * Uses existing XRPAmount with units for drops, and a new TaggedFee for fee units (LoadFeeTrack), and fee levels (TxQ). * Resolves #2451 --- CMakeLists.txt | 4 +- src/ripple/app/ledger/Ledger.cpp | 2 +- src/ripple/app/misc/FeeVote.h | 8 +- src/ripple/app/misc/FeeVoteImpl.cpp | 30 +- src/ripple/app/misc/LoadFeeTrack.h | 4 +- src/ripple/app/misc/NetworkOPs.cpp | 120 ++-- src/ripple/app/misc/TxQ.h | 44 +- src/ripple/app/misc/impl/LoadFeeTrack.cpp | 145 +++-- src/ripple/app/misc/impl/TxQ.cpp | 79 ++- src/ripple/app/paths/Flow.cpp | 2 +- src/ripple/app/paths/impl/AmountSpec.h | 2 +- src/ripple/app/paths/impl/BookStep.cpp | 2 +- src/ripple/app/paths/impl/FlowDebugInfo.h | 2 +- src/ripple/app/paths/impl/PaySteps.cpp | 2 +- src/ripple/app/paths/impl/StrandFlow.h | 2 +- src/ripple/app/paths/impl/XRPEndpointStep.cpp | 2 +- src/ripple/app/tx/applySteps.h | 2 +- src/ripple/app/tx/impl/ApplyContext.cpp | 2 +- src/ripple/app/tx/impl/ApplyContext.h | 6 +- src/ripple/app/tx/impl/Change.h | 2 +- src/ripple/app/tx/impl/Escrow.cpp | 10 +- src/ripple/app/tx/impl/Escrow.h | 2 +- src/ripple/app/tx/impl/InvariantCheck.cpp | 12 +- src/ripple/app/tx/impl/InvariantCheck.h | 4 +- src/ripple/app/tx/impl/PayChan.cpp | 2 +- src/ripple/app/tx/impl/SetRegularKey.cpp | 2 +- src/ripple/app/tx/impl/SetRegularKey.h | 2 +- src/ripple/app/tx/impl/Transactor.cpp | 14 +- src/ripple/app/tx/impl/Transactor.h | 8 +- src/ripple/app/tx/impl/applySteps.cpp | 4 +- src/ripple/basics/XRPAmount.h | 300 ++++++++++ src/ripple/basics/feeunits.h | 542 ++++++++++++++++++ src/ripple/basics/impl/mulDiv.cpp | 2 +- src/ripple/basics/safe_cast.h | 50 ++ src/ripple/basics/tagged_integer.h | 6 +- src/ripple/beast/cxx17/type_traits.h | 63 ++ src/ripple/core/Config.h | 16 +- src/ripple/core/ConfigSections.h | 1 - src/ripple/core/impl/Config.cpp | 11 +- src/ripple/json/impl/json_assert.h | 2 +- src/ripple/ledger/OpenView.h | 2 +- src/ripple/ledger/ReadView.h | 19 +- src/ripple/ledger/detail/ApplyStateTable.h | 2 +- src/ripple/ledger/detail/ApplyViewBase.h | 2 +- src/ripple/protocol/AmountConversions.h | 2 +- src/ripple/protocol/PayChan.h | 2 +- src/ripple/protocol/Quality.h | 2 +- src/ripple/protocol/STAmount.h | 2 +- src/ripple/protocol/STObject.h | 41 ++ src/ripple/protocol/STValidation.h | 7 +- src/ripple/protocol/SystemParameters.h | 25 +- src/ripple/protocol/XRPAmount.h | 175 ------ src/ripple/protocol/impl/STAmount.cpp | 15 +- src/ripple/protocol/impl/STObject.cpp | 13 + src/ripple/rpc/handlers/NoRippleCheck.cpp | 4 +- src/ripple/rpc/impl/TransactionSign.cpp | 17 +- src/test/app/Check_test.cpp | 12 +- src/test/app/Flow_test.cpp | 6 +- src/test/app/LoadFeeTrack_test.cpp | 62 +- src/test/app/MultiSign_test.cpp | 11 +- src/test/app/Offer_test.cpp | 75 ++- src/test/app/Regression_test.cpp | 4 +- src/test/app/TxQ_test.cpp | 50 +- src/test/basics/feeunits_test.cpp | 110 ++++ src/test/jtx/Env_test.cpp | 10 +- src/test/jtx/amount.h | 56 +- src/test/jtx/impl/amount.cpp | 4 +- src/test/jtx/impl/utility.cpp | 2 +- src/test/ledger/Invariants_test.cpp | 18 +- src/test/protocol/XRPAmount_test.cpp | 2 +- src/test/rpc/AmendmentBlocked_test.cpp | 2 +- src/test/rpc/NoRippleCheck_test.cpp | 13 +- src/test/unity/basics_test_unity.cpp | 1 + 73 files changed, 1694 insertions(+), 589 deletions(-) create mode 100644 src/ripple/basics/XRPAmount.h create mode 100644 src/ripple/basics/feeunits.h delete mode 100644 src/ripple/protocol/XRPAmount.h create mode 100644 src/test/basics/feeunits_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f2ff105541..fa261673d35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1737,11 +1737,13 @@ install ( src/ripple/basics/StringUtilities.h src/ripple/basics/ToString.h src/ripple/basics/UnorderedContainers.h + src/ripple/basics/XRPAmount.h src/ripple/basics/algorithm.h src/ripple/basics/base_uint.h src/ripple/basics/chrono.h src/ripple/basics/contract.h src/ripple/basics/date.h + src/ripple/basics/feeunits.h src/ripple/basics/hardened_hash.h src/ripple/basics/strHex.h DESTINATION include/ripple/basics) @@ -1819,7 +1821,6 @@ install ( src/ripple/protocol/TxFlags.h src/ripple/protocol/TxFormats.h src/ripple/protocol/UintTypes.h - src/ripple/protocol/XRPAmount.h src/ripple/protocol/digest.h src/ripple/protocol/jss.h src/ripple/protocol/tokens.h @@ -2346,6 +2347,7 @@ else () src/test/basics/base64_test.cpp src/test/basics/base_uint_test.cpp src/test/basics/contract_test.cpp + src/test/basics/feeunits_test.cpp src/test/basics/hardened_hash_test.cpp src/test/basics/mulDiv_test.cpp src/test/basics/qalloc_test.cpp diff --git a/src/ripple/app/ledger/Ledger.cpp b/src/ripple/app/ledger/Ledger.cpp index cf764f4aeff..3ff08f1a979 100644 --- a/src/ripple/app/ledger/Ledger.cpp +++ b/src/ripple/app/ledger/Ledger.cpp @@ -190,7 +190,7 @@ Ledger::Ledger ( , rules_{config.features} { info_.seq = 1; - info_.drops = SYSTEM_CURRENCY_START; + info_.drops = INITIAL_XRP; info_.closeTimeResolution = ledgerDefaultTimeResolution; static auto const id = calcAccountID( diff --git a/src/ripple/app/misc/FeeVote.h b/src/ripple/app/misc/FeeVote.h index 808fd0581c6..77d57e95324 100644 --- a/src/ripple/app/misc/FeeVote.h +++ b/src/ripple/app/misc/FeeVote.h @@ -40,16 +40,16 @@ class FeeVote struct Setup { /** The cost of a reference transaction in drops. */ - std::uint64_t reference_fee = 10; + XRPAmount reference_fee{ 10 }; /** The cost of a reference transaction in fee units. */ - std::uint32_t const reference_fee_units = 10; + static constexpr FeeUnit32 reference_fee_units{ 10 }; /** The account reserve requirement in drops. */ - std::uint64_t account_reserve = 20 * SYSTEM_CURRENCY_PARTS; + XRPAmount account_reserve{ 20 * DROPS_PER_XRP }; /** The per-owned item reserve requirement in drops. */ - std::uint64_t owner_reserve = 5 * SYSTEM_CURRENCY_PARTS; + XRPAmount owner_reserve{ 5 * DROPS_PER_XRP }; }; virtual ~FeeVote () = default; diff --git a/src/ripple/app/misc/FeeVoteImpl.cpp b/src/ripple/app/misc/FeeVoteImpl.cpp index 407234e6ecd..da4bd9bb3f8 100644 --- a/src/ripple/app/misc/FeeVoteImpl.cpp +++ b/src/ripple/app/misc/FeeVoteImpl.cpp @@ -28,14 +28,13 @@ namespace ripple { namespace detail { -template class VotableInteger { private: - using map_type = std::map ; + using Integer = XRPAmount; Integer mCurrent; // The current setting Integer mTarget; // The setting we want - map_type mVoteMap; + std::map mVoteMap; public: VotableInteger (Integer current, Integer target) @@ -62,9 +61,9 @@ class VotableInteger getVotes() const; }; -template -Integer -VotableInteger ::getVotes() const +auto +VotableInteger::getVotes() const + -> Integer { Integer ourVote = mCurrent; int weight = 0; @@ -153,13 +152,14 @@ FeeVoteImpl::doVoting( // LCL must be flag ledger assert ((lastClosedLedger->info().seq % 256) == 0); - detail::VotableInteger baseFeeVote ( + detail::VotableInteger baseFeeVote ( lastClosedLedger->fees().base, target_.reference_fee); - detail::VotableInteger baseReserveVote ( - lastClosedLedger->fees().accountReserve(0).drops(), target_.account_reserve); + detail::VotableInteger baseReserveVote( + lastClosedLedger->fees().accountReserve(0), + target_.account_reserve); - detail::VotableInteger incReserveVote ( + detail::VotableInteger incReserveVote ( lastClosedLedger->fees().increment, target_.owner_reserve); for (auto const& val : set) @@ -196,15 +196,15 @@ FeeVoteImpl::doVoting( } // choose our positions - std::uint64_t const baseFee = baseFeeVote.getVotes (); - std::uint32_t const baseReserve = baseReserveVote.getVotes (); - std::uint32_t const incReserve = incReserveVote.getVotes (); - std::uint32_t const feeUnits = target_.reference_fee_units; + XRPAmount const baseFee = baseFeeVote.getVotes (); + XRPAmount const baseReserve = baseReserveVote.getVotes (); + XRPAmount const incReserve = incReserveVote.getVotes (); + constexpr FeeUnit32 feeUnits = Setup::reference_fee_units; auto const seq = lastClosedLedger->info().seq + 1; // add transactions to our position if ((baseFee != lastClosedLedger->fees().base) || - (baseReserve != lastClosedLedger->fees().accountReserve(0)) || + (baseReserve != lastClosedLedger->fees().accountReserve(0)) || (incReserve != lastClosedLedger->fees().increment)) { JLOG(journal_.warn()) << diff --git a/src/ripple/app/misc/LoadFeeTrack.h b/src/ripple/app/misc/LoadFeeTrack.h index 321830d590c..36cc75c3c15 100644 --- a/src/ripple/app/misc/LoadFeeTrack.h +++ b/src/ripple/app/misc/LoadFeeTrack.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_CORE_LOADFEETRACK_H_INCLUDED #define RIPPLE_CORE_LOADFEETRACK_H_INCLUDED +#include #include #include #include @@ -140,7 +141,8 @@ class LoadFeeTrack final //------------------------------------------------------------------------------ // Scale using load as well as base rate -std::uint64_t scaleFeeLoad(std::uint64_t fee, LoadFeeTrack const& feeTrack, +XRPAmount +scaleFeeLoad(FeeUnit64 fee, LoadFeeTrack const& feeTrack, Fees const& fees, bool bUnlimited); } // ripple diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index fc974089735..b482af3d999 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -174,7 +174,7 @@ class NetworkOPsImp final { ServerFeeSummary() = default; - ServerFeeSummary(std::uint64_t fee, + ServerFeeSummary(XRPAmount fee, TxQ::Metrics&& escalationMetrics, LoadFeeTrack const & loadFeeTrack); bool @@ -188,7 +188,7 @@ class NetworkOPsImp final std::uint32_t loadFactorServer = 256; std::uint32_t loadBaseServer = 256; - std::uint64_t baseFee = 10; + XRPAmount baseFee{ 10 }; boost::optional em = boost::none; }; @@ -1605,7 +1605,7 @@ void NetworkOPsImp::pubManifest (Manifest const& mo) } NetworkOPsImp::ServerFeeSummary::ServerFeeSummary( - std::uint64_t fee, + XRPAmount fee, TxQ::Metrics&& escalationMetrics, LoadFeeTrack const & loadFeeTrack) : loadFactorServer{loadFeeTrack.getLoadFactor()} @@ -1636,6 +1636,15 @@ NetworkOPsImp::ServerFeeSummary::operator !=(NetworkOPsImp::ServerFeeSummary con return false; } +// Need to cap to uint64 to uint32 due to JSON limitations +static std::uint32_t trunc32(std::uint64_t v) +{ + constexpr std::uint64_t max32 = + std::numeric_limits::max(); + + return std::min(max32, v); +}; + void NetworkOPsImp::pubServer () { // VFALCO TODO Don't hold the lock across calls to send...make a copy of the @@ -1652,21 +1661,11 @@ void NetworkOPsImp::pubServer () app_.getTxQ().getMetrics(*app_.openLedger().current()), app_.getFeeTrack()}; - // Need to cap to uint64 to uint32 due to JSON limitations - auto clamp = [](std::uint64_t v) - { - constexpr std::uint64_t max32 = - std::numeric_limits::max(); - - return static_cast(std::min(max32, v)); - }; - - jvObj [jss::type] = "serverStatus"; jvObj [jss::server_status] = strOperatingMode (); jvObj [jss::load_base] = f.loadBaseServer; jvObj [jss::load_factor_server] = f.loadFactorServer; - jvObj [jss::base_fee] = clamp(f.baseFee); + jvObj [jss::base_fee] = f.baseFee.json(); if(f.em) { @@ -1675,11 +1674,13 @@ void NetworkOPsImp::pubServer () mulDiv(f.em->openLedgerFeeLevel, f.loadBaseServer, f.em->referenceFeeLevel).second); - jvObj [jss::load_factor] = clamp(loadFactor); - jvObj [jss::load_factor_fee_escalation] = clamp(f.em->openLedgerFeeLevel); - jvObj [jss::load_factor_fee_queue] = clamp(f.em->minProcessingFeeLevel); - jvObj [jss::load_factor_fee_reference] - = clamp(f.em->referenceFeeLevel); + jvObj [jss::load_factor] = trunc32(loadFactor); + jvObj [jss::load_factor_fee_escalation] = + f.em->openLedgerFeeLevel.json(); + jvObj [jss::load_factor_fee_queue] = + f.em->minProcessingFeeLevel.json(); + jvObj [jss::load_factor_fee_reference] = + f.em->referenceFeeLevel.json(); } else @@ -2236,16 +2237,13 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin, bool counters) escalationMetrics.referenceFeeLevel; auto const loadFactor = std::max(safe_cast(loadFactorServer), - mulDiv(loadFactorFeeEscalation, loadBaseServer, loadBaseFeeEscalation).second); + mulDiv(loadFactorFeeEscalation, loadBaseServer, + loadBaseFeeEscalation).second); if (!human) { - constexpr std::uint64_t max32 = - std::numeric_limits::max(); - info[jss::load_base] = loadBaseServer; - info[jss::load_factor] = static_cast( - std::min(max32, loadFactor)); + info[jss::load_factor] = trunc32(loadFactor); info[jss::load_factor_server] = loadFactorServer; /* Json::Value doesn't support uint64, so clamp to max @@ -2254,14 +2252,11 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin, bool counters) that high. */ info[jss::load_factor_fee_escalation] = - static_cast (std::min( - max32, loadFactorFeeEscalation)); + loadFactorFeeEscalation.json(); info[jss::load_factor_fee_queue] = - static_cast (std::min( - max32, escalationMetrics.minProcessingFeeLevel)); + escalationMetrics.minProcessingFeeLevel.json(); info[jss::load_factor_fee_reference] = - static_cast (std::min( - max32, loadBaseFeeEscalation)); + loadBaseFeeEscalation.json(); } else { @@ -2288,14 +2283,15 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin, bool counters) } if (loadFactorFeeEscalation != escalationMetrics.referenceFeeLevel && - (admin || loadFactorFeeEscalation != loadFactor)) + (admin || + loadFactorFeeEscalation != FeeLevel64{ loadFactor })) info[jss::load_factor_fee_escalation] = - static_cast (loadFactorFeeEscalation) / + static_cast(loadFactorFeeEscalation) / escalationMetrics.referenceFeeLevel; if (escalationMetrics.minProcessingFeeLevel != escalationMetrics.referenceFeeLevel) info[jss::load_factor_fee_queue] = - static_cast ( + static_cast( escalationMetrics.minProcessingFeeLevel) / escalationMetrics.referenceFeeLevel; } @@ -2310,33 +2306,35 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin, bool counters) if (lpClosed) { - std::uint64_t baseFee = lpClosed->fees().base; - std::uint64_t baseRef = lpClosed->fees().units; + XRPAmount const baseFee = lpClosed->fees().base; Json::Value l (Json::objectValue); l[jss::seq] = Json::UInt (lpClosed->info().seq); l[jss::hash] = to_string (lpClosed->info().hash); if (!human) { - l[jss::base_fee] = Json::Value::UInt (baseFee); - l[jss::reserve_base] = Json::Value::UInt (lpClosed->fees().accountReserve(0).drops()); + l[jss::base_fee] = baseFee.json(); + l[jss::reserve_base] = lpClosed->fees().accountReserve(0).json(); l[jss::reserve_inc] = - Json::Value::UInt (lpClosed->fees().increment); - l[jss::close_time] = - Json::Value::UInt (lpClosed->info().closeTime.time_since_epoch().count()); + lpClosed->fees().increment.json(); + l[jss::close_time] = Json::Value::UInt ( + lpClosed->info().closeTime.time_since_epoch().count()); } else { - l[jss::base_fee_xrp] = static_cast (baseFee) / - SYSTEM_CURRENCY_PARTS; + // Make a local specialization of the TaggedFee class, using + // XRPAmount as the "unit" to make some of the math here easier. + using DropsDouble = units::TaggedFee; + + l[jss::base_fee_xrp] = + static_cast(baseFee) / + DROPS_PER_XRP; l[jss::reserve_base_xrp] = - static_cast (Json::UInt ( - lpClosed->fees().accountReserve(0).drops() * baseFee / baseRef)) - / SYSTEM_CURRENCY_PARTS; + static_cast( + lpClosed->fees().accountReserve(0)) / DROPS_PER_XRP; l[jss::reserve_inc_xrp] = - static_cast (Json::UInt ( - lpClosed->fees().increment * baseFee / baseRef)) - / SYSTEM_CURRENCY_PARTS; + static_cast( + lpClosed->fees().increment) / DROPS_PER_XRP; auto const nowOffset = app_.timeKeeper().nowOffset(); if (std::abs (nowOffset.count()) >= 60) @@ -2454,11 +2452,11 @@ void NetworkOPsImp::pubLedger ( jvObj[jss::ledger_time] = Json::Value::UInt (lpAccepted->info().closeTime.time_since_epoch().count()); - jvObj[jss::fee_ref] - = Json::UInt (lpAccepted->fees().units); - jvObj[jss::fee_base] = Json::UInt (lpAccepted->fees().base); - jvObj[jss::reserve_base] = Json::UInt (lpAccepted->fees().accountReserve(0).drops()); - jvObj[jss::reserve_inc] = Json::UInt (lpAccepted->fees().increment); + jvObj[jss::fee_ref] = lpAccepted->fees().units.json(); + jvObj[jss::fee_base] = lpAccepted->fees().base.json(); + jvObj[jss::reserve_base] = + lpAccepted->fees().accountReserve(0).json(); + jvObj[jss::reserve_inc] = lpAccepted->fees().increment.json(); jvObj[jss::txn_count] = Json::UInt (alpAccepted->getTxnCount ()); @@ -2823,13 +2821,13 @@ bool NetworkOPsImp::subLedger (InfoSub::ref isrListener, Json::Value& jvResult) { jvResult[jss::ledger_index] = lpClosed->info().seq; jvResult[jss::ledger_hash] = to_string (lpClosed->info().hash); - jvResult[jss::ledger_time] - = Json::Value::UInt(lpClosed->info().closeTime.time_since_epoch().count()); - jvResult[jss::fee_ref] - = Json::UInt (lpClosed->fees().units); - jvResult[jss::fee_base] = Json::UInt (lpClosed->fees().base); - jvResult[jss::reserve_base] = Json::UInt (lpClosed->fees().accountReserve(0).drops()); - jvResult[jss::reserve_inc] = Json::UInt (lpClosed->fees().increment); + jvResult[jss::ledger_time] = Json::Value::UInt( + lpClosed->info().closeTime.time_since_epoch().count()); + jvResult[jss::fee_ref] = lpClosed->fees().units.json(); + jvResult[jss::fee_base] = lpClosed->fees().base.json(); + jvResult[jss::reserve_base] = + lpClosed->fees().accountReserve(0).json(); + jvResult[jss::reserve_inc] = lpClosed->fees().increment.json(); } if ((mMode >= omSYNCING) && !isNeedNetworkLedger ()) diff --git a/src/ripple/app/misc/TxQ.h b/src/ripple/app/misc/TxQ.h index 71bf41a0a10..91bee7ccb7e 100644 --- a/src/ripple/app/misc/TxQ.h +++ b/src/ripple/app/misc/TxQ.h @@ -55,7 +55,7 @@ class TxQ { public: /// Fee level for single-signed reference transaction. - static constexpr std::uint64_t baseLevel = 256; + static constexpr FeeLevel64 baseLevel{ 256 }; /** Structure used to customize @ref TxQ behavior. @@ -104,7 +104,7 @@ class TxQ std::int32_t multiTxnPercent = -90; /// Minimum value of the escalation multiplier, regardless /// of the prior ledger's median fee level. - std::uint64_t minimumEscalationMultiplier = baseLevel * 500; + FeeLevel64 minimumEscalationMultiplier = baseLevel * 500; /// Minimum number of transactions to allow into the ledger /// before escalation, regardless of the prior ledger's size. std::uint32_t minimumTxnInLedger = 5; @@ -168,7 +168,7 @@ class TxQ we can make this more complicated. But avoid bikeshedding for now. */ - std::uint64_t zeroBaseFeeTransactionFeeLevel = 256000; + FeeLevel64 zeroBaseFeeTransactionFeeLevel{ 256000 }; /// Use standalone mode behavior. bool standAlone = false; }; @@ -191,15 +191,15 @@ class TxQ /// Number of transactions expected per ledger std::size_t txPerLedger; /// Reference transaction fee level - std::uint64_t referenceFeeLevel; + FeeLevel64 referenceFeeLevel; /// Minimum fee level for a transaction to be considered for /// the open ledger or the queue - std::uint64_t minProcessingFeeLevel; + FeeLevel64 minProcessingFeeLevel; /// Median fee level of the last ledger - std::uint64_t medFeeLevel; + FeeLevel64 medFeeLevel; /// Minimum fee level to get into the current open ledger, /// bypassing the queue - std::uint64_t openLedgerFeeLevel; + FeeLevel64 openLedgerFeeLevel; }; /** @@ -212,7 +212,7 @@ class TxQ explicit AccountTxDetails() = default; /// Fee level of the queued transaction - uint64_t feeLevel; + FeeLevel64 feeLevel; /// LastValidLedger field of the queued transaction, if any boost::optional lastValid; /** Potential @ref TxConsequences of applying the queued transaction @@ -369,7 +369,7 @@ class TxQ boost::circular_buffer recentTxnCounts_; /// Based on the median fee of the LCL. Used /// when fee escalation kicks in. - std::uint64_t escalationMultiplier_; + FeeLevel64 escalationMultiplier_; /// Journal beast::Journal j_; @@ -417,7 +417,7 @@ class TxQ std::size_t const txnsExpected; // Based on the median fee of the LCL. Used // when fee escalation kicks in. - std::uint64_t const escalationMultiplier; + FeeLevel64 const escalationMultiplier; }; /// Get the current @ref Snapshot @@ -439,7 +439,7 @@ class TxQ @return A fee level value. */ static - std::uint64_t + FeeLevel64 scaleFeeLevel(Snapshot const& snapshot, OpenView const& view); /** @@ -473,7 +473,7 @@ class TxQ whether the calculation result overflows. */ static - std::pair + std::pair escalatedSeriesFeeLevel(Snapshot const& snapshot, OpenView const& view, std::size_t extraCount, std::size_t seriesSize); }; @@ -498,7 +498,7 @@ class TxQ boost::optional consequences; /// Computed fee level that the transaction will pay. - uint64_t const feeLevel; + FeeLevel64 const feeLevel; /// Transaction ID. TxID const txID; /// Prior transaction ID (`sfAccountTxnID` field). @@ -559,7 +559,7 @@ class TxQ public: /// Constructor MaybeTx(std::shared_ptr const&, - TxID const& txID, std::uint64_t feeLevel, + TxID const& txID, FeeLevel64 feeLevel, ApplyFlags const flags, PreflightResult const& pfresult); @@ -720,7 +720,7 @@ class TxQ std::pair tryClearAccountQueue(Application& app, OpenView& view, STTx const& tx, AccountMap::iterator const& accountIter, - TxQAccount::TxMap::iterator, std::uint64_t feeLevelPaid, + TxQAccount::TxMap::iterator, FeeLevel64 feeLevelPaid, PreflightResult const& pfresult, std::size_t const txExtraCount, ApplyFlags flags, FeeMetrics::Snapshot const& metricsSnapshot, @@ -740,6 +740,20 @@ setup_TxQ(Config const&); std::unique_ptr make_TxQ(TxQ::Setup const&, beast::Journal); +template +std::pair +toDrops(FeeLevel const& level, XRPAmount const& baseFee) +{ + return mulDiv(level, baseFee, TxQ::baseLevel); +} + +inline +std::pair +toFeeLevel(XRPAmount const& drops, XRPAmount const& baseFee) +{ + return mulDiv(drops, TxQ::baseLevel, baseFee); +} + } // ripple #endif diff --git a/src/ripple/app/misc/impl/LoadFeeTrack.cpp b/src/ripple/app/misc/impl/LoadFeeTrack.cpp index b4f3631a2d7..ea75b0f113c 100644 --- a/src/ripple/app/misc/impl/LoadFeeTrack.cpp +++ b/src/ripple/app/misc/impl/LoadFeeTrack.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -81,56 +82,110 @@ LoadFeeTrack::lowerLocalFee () //------------------------------------------------------------------------------ -// NIKB TODO: Once we get C++17, we can replace lowestTerms -// with this: -// -// template && -// std::is_integral_v> -// > -// void lowestTerms(T1& a, T2& b) -// { -// if (auto const gcd = std::gcd(a, b)) -// { -// a /= gcd; -// b /= gcd; -// } -// } +// EHENNIS TODO: Once we get C++17, we can replace gcd with std::gcd + +template +std::enable_if_t, T> +abs(T t) +{ + return t; +} + +template +std::enable_if_t, T> +abs(T t) +{ + return std::abs(t); +} template ::value && - std::is_unsigned::value && + std::is_integral_v && sizeof(T1) <= sizeof(std::uint64_t) >, class = std::enable_if_t < - std::is_integral::value && - std::is_unsigned::value && + std::is_integral_v && sizeof(T2) <= sizeof(std::uint64_t) > > -void lowestTerms(T1& a, T2& b) +std::common_type_t gcd(T1 const a, T2 const b) { if (a == 0 && b == 0) - return; + return 0; - std::uint64_t x = a, y = b; + std::uint64_t x = abs(a), y = abs(b); while (y != 0) { auto t = x % y; x = y; y = t; } - a /= x; - b /= x; + return x; } +namespace detail +{ + +struct xrp_unit_product_tag; + +using xrp_unit_product = + units::TaggedFee; + +} // detail + +detail::xrp_unit_product +operator* (FeeUnit64 lhs, XRPAmount rhs) +{ + return detail::xrp_unit_product{ lhs.fee() * rhs.drops() }; +} + +XRPAmount +operator/ (detail::xrp_unit_product lhs, FeeUnit64 rhs) +{ + return{ lhs.fee() / rhs.fee() }; +} + + // Scale using load as well as base rate -std::uint64_t -scaleFeeLoad(std::uint64_t fee, LoadFeeTrack const& feeTrack, +XRPAmount +scaleFeeLoad(FeeUnit64 fee, LoadFeeTrack const& feeTrack, Fees const& fees, bool bUnlimited) { if (fee == 0) - return fee; + return 0; + + auto lowestTerms1 = [](auto& a, auto& b) + { + if (auto const g = gcd(a, b.value())) + { + a /= g; + b /= g; + } + }; + + // Normally, types with different units wouldn't be mathematically + // compatible. This function is an exception. + auto lowestTerms2 = [](auto& a, auto& b) + { + if (auto const g = gcd(a.value(), b.value())) + { + a /= g; + b /= g; + } + }; + + // Normally, these types wouldn't be swappable. This function is an + // exception + auto maybe_swap = [](auto& lhs, auto& rhs) + { + if (lhs.value() < rhs.value()) + { + auto tmp = lhs.value(); + lhs = rhs.value(); + rhs = tmp; + } + // double check + assert(lhs.value() >= rhs.value()); + }; + std::uint32_t feeFactor; std::uint32_t uRemFee; { @@ -150,43 +205,43 @@ scaleFeeLoad(std::uint64_t fee, LoadFeeTrack const& feeTrack, // The denominator of the fraction we're trying to compute. // fees.units and lftNormalFee are both 32 bit, // so the multiplication can't overflow. - auto den = safe_cast(fees.units) + auto den = FeeUnit64{ fees.units } * safe_cast(feeTrack.getLoadBase()); // Reduce fee * baseFee * feeFactor / (fees.units * lftNormalFee) // to lowest terms. - lowestTerms(fee, den); - lowestTerms(baseFee, den); - lowestTerms(feeFactor, den); + lowestTerms2(fee, den); + lowestTerms2(baseFee, den); + lowestTerms1(feeFactor, den); // fee and baseFee are 64 bit, feeFactor is 32 bit // Order fee and baseFee largest first - if (fee < baseFee) - std::swap(fee, baseFee); + maybe_swap(fee, baseFee); // If baseFee * feeFactor overflows, the final result will overflow - const auto max = std::numeric_limits::max(); + constexpr XRPAmount max = + std::numeric_limits::max(); if (baseFee > max / feeFactor) Throw ("scaleFeeLoad"); baseFee *= feeFactor; // Reorder fee and baseFee - if (fee < baseFee) - std::swap(fee, baseFee); - // If fee * baseFee / den might overflow... - if (fee > max / baseFee) + maybe_swap(fee, baseFee); + // If fee * baseFee overflows, do the division first + if (fee > FeeUnit64{ max / baseFee }) { // Do the division first, on the larger of fee and baseFee - fee /= den; - if (fee > max / baseFee) + auto const factor = fee / den; + // If factor * basefee ( == fee / den * baseFee ) will overflow, + // throw + if (factor > max / baseFee) Throw ("scaleFeeLoad"); - fee *= baseFee; + return factor * baseFee; } else { // Otherwise fee * baseFee won't overflow, // so do it prior to the division. - fee *= baseFee; - fee /= den; + auto const product = fee * baseFee; + return product / den; } - return fee; } } // ripple diff --git a/src/ripple/app/misc/impl/TxQ.cpp b/src/ripple/app/misc/impl/TxQ.cpp index 778980204a6..bae77a2552f 100644 --- a/src/ripple/app/misc/impl/TxQ.cpp +++ b/src/ripple/app/misc/impl/TxQ.cpp @@ -36,11 +36,11 @@ namespace ripple { ////////////////////////////////////////////////////////////////////////// static -std::uint64_t +FeeLevel64 getFeeLevelPaid( STTx const& tx, - std::uint64_t baseRefLevel, - std::uint64_t refTxnCostDrops, + FeeLevel64 baseRefLevel, + XRPAmount refTxnCostDrops, TxQ::Setup const& setup) { if (refTxnCostDrops == 0) @@ -51,7 +51,7 @@ getFeeLevelPaid( // If the math overflows, return the clipped // result blindly. This is very unlikely to ever // happen. - return mulDiv(tx[sfFee].xrp().drops(), + return mulDiv(tx[sfFee].xrp(), baseRefLevel, refTxnCostDrops).second; } @@ -66,23 +66,24 @@ getLastLedgerSequence(STTx const& tx) } static -std::uint64_t -increase(std::uint64_t level, +FeeLevel64 +increase(FeeLevel64 level, std::uint32_t increasePercent) { - return mulDiv( - level, 100 + increasePercent, 100).second; + return mulDiv(level, 100 + increasePercent, 100).second; } ////////////////////////////////////////////////////////////////////////// +constexpr FeeLevel64 TxQ::baseLevel; + std::size_t TxQ::FeeMetrics::update(Application& app, ReadView const& view, bool timeLeap, TxQ::Setup const& setup) { - std::vector feeLevels; + std::vector feeLevels; auto const txBegin = view.txs.begin(); auto const txEnd = view.txs.end(); auto const size = std::distance(txBegin, txEnd); @@ -90,7 +91,8 @@ TxQ::FeeMetrics::update(Application& app, std::for_each(txBegin, txEnd, [&](auto const& tx) { - auto const baseFee = calculateBaseFee(view, *tx.first); + auto const baseFee = view.fees().toDrops( + calculateBaseFee(view, *tx.first)).second; feeLevels.push_back(getFeeLevelPaid(*tx.first, baseLevel, baseFee, setup)); } @@ -158,7 +160,7 @@ TxQ::FeeMetrics::update(Application& app, // number of elements, it will add the two elements // on either side of the "middle" and average them. escalationMultiplier_ = (feeLevels[size / 2] + - feeLevels[(size - 1) / 2] + 1) / 2; + feeLevels[(size - 1) / 2] + FeeLevel64{ 1 }) / 2; escalationMultiplier_ = std::max(escalationMultiplier_, setup.minimumEscalationMultiplier); } @@ -169,7 +171,7 @@ TxQ::FeeMetrics::update(Application& app, return size; } -std::uint64_t +FeeLevel64 TxQ::FeeMetrics::scaleFeeLevel(Snapshot const& snapshot, OpenView const& view) { @@ -213,7 +215,7 @@ sumOfFirstSquares(std::size_t x) } -std::pair +std::pair TxQ::FeeMetrics::escalatedSeriesFeeLevel(Snapshot const& snapshot, OpenView const& view, std::size_t extraCount, std::size_t seriesSize) @@ -245,7 +247,7 @@ TxQ::FeeMetrics::escalatedSeriesFeeLevel(Snapshot const& snapshot, // `sumNlast` definitely overflowed. Also the odds of this // are nearly nil. if (!sumNlast.first) - return sumNlast; + return { sumNlast.first, FeeLevel64{ sumNlast.second } }; auto const totalFeeLevel = mulDiv(multiplier, sumNlast.second - sumNcurrent.second, target * target); @@ -255,7 +257,7 @@ TxQ::FeeMetrics::escalatedSeriesFeeLevel(Snapshot const& snapshot, TxQ::MaybeTx::MaybeTx( std::shared_ptr const& txn_, - TxID const& txID_, std::uint64_t feeLevel_, + TxID const& txID_, FeeLevel64 feeLevel_, ApplyFlags const flags_, PreflightResult const& pfresult_) : txn(txn_) @@ -483,7 +485,7 @@ TxQ::erase(TxQ::TxQAccount& txQAccount, std::pair TxQ::tryClearAccountQueue(Application& app, OpenView& view, STTx const& tx, TxQ::AccountMap::iterator const& accountIter, - TxQAccount::TxMap::iterator beginTxIter, std::uint64_t feeLevelPaid, + TxQAccount::TxMap::iterator beginTxIter, FeeLevel64 feeLevelPaid, PreflightResult const& pfresult, std::size_t const txExtraCount, ApplyFlags flags, FeeMetrics::Snapshot const& metricsSnapshot, beast::Journal j) @@ -668,7 +670,8 @@ TxQ::apply(Application& app, OpenView& view, // or transaction replacement, so just pull it up now. // TODO: Do we want to avoid doing it again during // preclaim? - auto const baseFee = calculateBaseFee(view, *tx); + auto const baseFee = view.fees().toDrops( + calculateBaseFee(view, *tx)).second; auto const feeLevelPaid = getFeeLevelPaid(*tx, baseLevel, baseFee, setup_); auto const requiredFeeLevel = [&]() @@ -1003,7 +1006,8 @@ TxQ::apply(Application& app, OpenView& view, multiTxn->nextTxIter->second.retriesRemaining == MaybeTx::retriesAllowed && feeLevelPaid > requiredFeeLevel && - requiredFeeLevel > baseLevel && baseFee != 0) + requiredFeeLevel > baseLevel && + baseFee != 0) { OpenView sandbox(open_ledger, &view, view.rules()); @@ -1079,11 +1083,11 @@ TxQ::apply(Application& app, OpenView& view, || endAccount.transactions.size() == 1) return lastRIter->feeLevel; - constexpr std::uint64_t max = - std::numeric_limits::max(); + constexpr FeeLevel64 max{ + std::numeric_limits::max() }; auto endTotal = std::accumulate(endAccount.transactions.begin(), endAccount.transactions.end(), - std::pair(0, 0), + std::pair(0, 0), [&](auto const& total, auto const& tx) { // Check for overflow. @@ -1093,7 +1097,7 @@ TxQ::apply(Application& app, OpenView& view, endAccount.transactions.size(); if (total.first >= max - next || total.second >= max - mod) - return std::make_pair(max, std::uint64_t(0)); + return std::make_pair(max, FeeLevel64 { 0 }); return std::make_pair(total.first + next, total.second + mod); }); return endTotal.first + endTotal.second / @@ -1389,7 +1393,8 @@ TxQ::getMetrics(OpenView const& view) const result.txInLedger = view.txCount(); result.txPerLedger = snapshot.txnsExpected; result.referenceFeeLevel = baseLevel; - result.minProcessingFeeLevel = isFull() ? byFee_.rbegin()->feeLevel + 1 : + result.minProcessingFeeLevel = isFull() ? + byFee_.rbegin()->feeLevel + FeeLevel64{ 1 } : baseLevel; result.medFeeLevel = snapshot.escalationMultiplier; result.openLedgerFeeLevel = FeeMetrics::scaleFeeLevel(snapshot, view); @@ -1492,27 +1497,19 @@ TxQ::doRPC(Application& app) const levels[jss::median_level] = to_string(metrics.medFeeLevel); levels[jss::open_ledger_level] = to_string(metrics.openLedgerFeeLevel); - auto const baseFee = view->fees().base; + auto const& baseFee = view->fees().base; auto& drops = ret[jss::drops] = Json::Value(); // Don't care about the overflow flags - drops[jss::base_fee] = to_string(mulDiv( - metrics.referenceFeeLevel, baseFee, - metrics.referenceFeeLevel).second); - drops[jss::minimum_fee] = to_string(mulDiv( - metrics.minProcessingFeeLevel, baseFee, - metrics.referenceFeeLevel).second); - drops[jss::median_fee] = to_string(mulDiv( - metrics.medFeeLevel, baseFee, - metrics.referenceFeeLevel).second); - auto escalatedFee = mulDiv( - metrics.openLedgerFeeLevel, baseFee, - metrics.referenceFeeLevel).second; - if (mulDiv(escalatedFee, metrics.referenceFeeLevel, - baseFee).second < metrics.openLedgerFeeLevel) - ++escalatedFee; - - drops[jss::open_ledger_fee] = to_string(escalatedFee); + drops[jss::base_fee] = to_string(toDrops( + metrics.referenceFeeLevel, baseFee).second); + drops[jss::minimum_fee] = to_string(toDrops( + metrics.minProcessingFeeLevel, baseFee).second); + drops[jss::median_fee] = to_string(toDrops( + metrics.medFeeLevel, baseFee).second); + drops[jss::open_ledger_fee] = to_string(toDrops( + metrics.openLedgerFeeLevel - FeeLevel64{ 1 }, baseFee).second + + 1); return ret; } diff --git a/src/ripple/app/paths/Flow.cpp b/src/ripple/app/paths/Flow.cpp index 93739e98fd5..0393cd9eb57 100644 --- a/src/ripple/app/paths/Flow.cpp +++ b/src/ripple/app/paths/Flow.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include diff --git a/src/ripple/app/paths/impl/AmountSpec.h b/src/ripple/app/paths/impl/AmountSpec.h index 4d32fad476c..0192ea36c97 100644 --- a/src/ripple/app/paths/impl/AmountSpec.h +++ b/src/ripple/app/paths/impl/AmountSpec.h @@ -21,7 +21,7 @@ #define RIPPLE_PATH_IMPL_AMOUNTSPEC_H_INCLUDED #include -#include +#include #include namespace ripple { diff --git a/src/ripple/app/paths/impl/BookStep.cpp b/src/ripple/app/paths/impl/BookStep.cpp index 498cc3e574b..350e8ecb621 100644 --- a/src/ripple/app/paths/impl/BookStep.cpp +++ b/src/ripple/app/paths/impl/BookStep.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include diff --git a/src/ripple/app/paths/impl/FlowDebugInfo.h b/src/ripple/app/paths/impl/FlowDebugInfo.h index f0a2b0d3ba3..3ccdfac843e 100644 --- a/src/ripple/app/paths/impl/FlowDebugInfo.h +++ b/src/ripple/app/paths/impl/FlowDebugInfo.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/ripple/app/paths/impl/PaySteps.cpp b/src/ripple/app/paths/impl/PaySteps.cpp index edaac72ed72..0bf72bf2a8f 100644 --- a/src/ripple/app/paths/impl/PaySteps.cpp +++ b/src/ripple/app/paths/impl/PaySteps.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/ripple/app/paths/impl/StrandFlow.h b/src/ripple/app/paths/impl/StrandFlow.h index 247697e477d..3d2b33c7f39 100644 --- a/src/ripple/app/paths/impl/StrandFlow.h +++ b/src/ripple/app/paths/impl/StrandFlow.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include diff --git a/src/ripple/app/paths/impl/XRPEndpointStep.cpp b/src/ripple/app/paths/impl/XRPEndpointStep.cpp index 8e3ad4d57dd..b2b0eb8bf84 100644 --- a/src/ripple/app/paths/impl/XRPEndpointStep.cpp +++ b/src/ripple/app/paths/impl/XRPEndpointStep.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include diff --git a/src/ripple/app/tx/applySteps.h b/src/ripple/app/tx/applySteps.h index 0f4768fe798..d49de7bba3b 100644 --- a/src/ripple/app/tx/applySteps.h +++ b/src/ripple/app/tx/applySteps.h @@ -234,7 +234,7 @@ preclaim(PreflightResult const& preflightResult, @return The base fee. */ -std::uint64_t +FeeUnit64 calculateBaseFee(ReadView const& view, STTx const& tx); diff --git a/src/ripple/app/tx/impl/ApplyContext.cpp b/src/ripple/app/tx/impl/ApplyContext.cpp index 6d479c8bb88..7ca578f900c 100644 --- a/src/ripple/app/tx/impl/ApplyContext.cpp +++ b/src/ripple/app/tx/impl/ApplyContext.cpp @@ -30,7 +30,7 @@ namespace ripple { ApplyContext::ApplyContext(Application& app_, OpenView& base, STTx const& tx_, TER preclaimResult_, - std::uint64_t baseFee_, ApplyFlags flags, + FeeUnit64 baseFee_, ApplyFlags flags, beast::Journal journal_) : app(app_) , tx(tx_) diff --git a/src/ripple/app/tx/impl/ApplyContext.h b/src/ripple/app/tx/impl/ApplyContext.h index cb7890d7d10..0fb8a69dd88 100644 --- a/src/ripple/app/tx/impl/ApplyContext.h +++ b/src/ripple/app/tx/impl/ApplyContext.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -40,13 +40,13 @@ class ApplyContext explicit ApplyContext (Application& app, OpenView& base, STTx const& tx, TER preclaimResult, - std::uint64_t baseFee, ApplyFlags flags, + FeeUnit64 baseFee, ApplyFlags flags, beast::Journal = beast::Journal{beast::Journal::getNullSink()}); Application& app; STTx const& tx; TER const preclaimResult; - std::uint64_t const baseFee; + FeeUnit64 const baseFee; beast::Journal const journal; ApplyView& diff --git a/src/ripple/app/tx/impl/Change.h b/src/ripple/app/tx/impl/Change.h index e5d84cf1a2e..ade6684ece0 100644 --- a/src/ripple/app/tx/impl/Change.h +++ b/src/ripple/app/tx/impl/Change.h @@ -46,7 +46,7 @@ class Change void preCompute() override; static - std::uint64_t + FeeUnit64 calculateBaseFee ( ReadView const& view, STTx const& tx) diff --git a/src/ripple/app/tx/impl/Escrow.cpp b/src/ripple/app/tx/impl/Escrow.cpp index b1b36e77876..d986e0119c2 100644 --- a/src/ripple/app/tx/impl/Escrow.cpp +++ b/src/ripple/app/tx/impl/Escrow.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include // During an EscrowFinish, the transaction must specify both // a condition and a fulfillment. We track whether that @@ -349,17 +349,17 @@ EscrowFinish::preflight (PreflightContext const& ctx) return tesSUCCESS; } -std::uint64_t +FeeUnit64 EscrowFinish::calculateBaseFee ( ReadView const& view, STTx const& tx) { - std::uint64_t extraFee = 0; + FeeUnit64 extraFee{ 0 }; if (auto const fb = tx[~sfFulfillment]) { - extraFee += view.fees().units * - (32 + safe_cast (fb->size() / 16)); + extraFee += safe_cast(view.fees().units) * + (32 + (fb->size() / 16)); } return Transactor::calculateBaseFee (view, tx) + extraFee; diff --git a/src/ripple/app/tx/impl/Escrow.h b/src/ripple/app/tx/impl/Escrow.h index 3c600868ae6..939a72749b4 100644 --- a/src/ripple/app/tx/impl/Escrow.h +++ b/src/ripple/app/tx/impl/Escrow.h @@ -63,7 +63,7 @@ class EscrowFinish preflight (PreflightContext const& ctx); static - std::uint64_t + FeeUnit64 calculateBaseFee ( ReadView const& view, STTx const& tx); diff --git a/src/ripple/app/tx/impl/InvariantCheck.cpp b/src/ripple/app/tx/impl/InvariantCheck.cpp index 135b6e2e32e..5c32bf56cb1 100644 --- a/src/ripple/app/tx/impl/InvariantCheck.cpp +++ b/src/ripple/app/tx/impl/InvariantCheck.cpp @@ -18,7 +18,9 @@ //============================================================================== #include +#include #include +#include namespace ripple { @@ -48,7 +50,7 @@ TransactionFeeCheck::finalize( // We should never charge a fee that's greater than or equal to the // entire XRP supply. - if (fee.drops() >= SYSTEM_CURRENCY_START) + if (fee >= INITIAL_XRP) { JLOG(j.fatal()) << "Invariant failed: fee paid exceeds system limit: " << fee.drops(); return false; @@ -163,11 +165,11 @@ XRPBalanceChecks::visitEntry( if (!balance.native()) return true; - auto const drops = balance.xrp().drops(); + auto const drops = balance.xrp(); // Can't have more than the number of drops instantiated // in the genesis ledger. - if (drops > SYSTEM_CURRENCY_START) + if (drops > INITIAL_XRP) return true; // Can't have a negative balance (0 is OK) @@ -251,10 +253,10 @@ NoZeroEscrow::visitEntry( if (!amount.native()) return true; - if (amount.xrp().drops() <= 0) + if (amount.xrp() <= 0) return true; - if (amount.xrp().drops() >= SYSTEM_CURRENCY_START) + if (amount.xrp() >= INITIAL_XRP) return true; return false; diff --git a/src/ripple/app/tx/impl/InvariantCheck.h b/src/ripple/app/tx/impl/InvariantCheck.h index b1a2fbca32c..77ab1696f37 100644 --- a/src/ripple/app/tx/impl/InvariantCheck.h +++ b/src/ripple/app/tx/impl/InvariantCheck.h @@ -150,7 +150,7 @@ class AccountRootsNotDeleted /** * @brief Invariant: An account XRP balance must be in XRP and take a value - * between 0 and SYSTEM_CURRENCY_START drops, inclusive. + * between 0 and INITIAL_XRP drops, inclusive. * * We iterate all account roots modified by the transaction and ensure that * their XRP balances are reasonable. @@ -240,7 +240,7 @@ class NoBadOffers /** * @brief Invariant: an escrow entry must take a value between 0 and - * SYSTEM_CURRENCY_START drops exclusive. + * INITIAL_XRP drops exclusive. */ class NoZeroEscrow { diff --git a/src/ripple/app/tx/impl/PayChan.cpp b/src/ripple/app/tx/impl/PayChan.cpp index 5be551db34e..aae29f8d0ff 100644 --- a/src/ripple/app/tx/impl/PayChan.cpp +++ b/src/ripple/app/tx/impl/PayChan.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include namespace ripple { diff --git a/src/ripple/app/tx/impl/SetRegularKey.cpp b/src/ripple/app/tx/impl/SetRegularKey.cpp index ae46397b521..f592aea0bdb 100644 --- a/src/ripple/app/tx/impl/SetRegularKey.cpp +++ b/src/ripple/app/tx/impl/SetRegularKey.cpp @@ -24,7 +24,7 @@ namespace ripple { -std::uint64_t +FeeUnit64 SetRegularKey::calculateBaseFee ( ReadView const& view, STTx const& tx) diff --git a/src/ripple/app/tx/impl/SetRegularKey.h b/src/ripple/app/tx/impl/SetRegularKey.h index 0b51f5841c5..b8001e83ec2 100644 --- a/src/ripple/app/tx/impl/SetRegularKey.h +++ b/src/ripple/app/tx/impl/SetRegularKey.h @@ -48,7 +48,7 @@ class SetRegularKey preflight (PreflightContext const& ctx); static - std::uint64_t + FeeUnit64 calculateBaseFee ( ReadView const& view, STTx const& tx); diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index fd7df0cbd48..30da2fae3de 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -122,7 +122,8 @@ Transactor::Transactor( { } -std::uint64_t Transactor::calculateBaseFee ( +FeeUnit64 +Transactor::calculateBaseFee ( ReadView const& view, STTx const& tx) { @@ -131,13 +132,12 @@ std::uint64_t Transactor::calculateBaseFee ( // The computation has two parts: // * The base fee, which is the same for most transactions. // * The additional cost of each multisignature on the transaction. - std::uint64_t baseFee = view.fees().units; + FeeUnit64 const baseFee = view.fees().units; // Each signer adds one more baseFee to the minimum required fee // for the transaction. - std::uint32_t signerCount = 0; - if (tx.isFieldPresent (sfSigners)) - signerCount = tx.getFieldArray (sfSigners).size(); + const std::size_t signerCount = tx.isFieldPresent (sfSigners) ? + tx.getFieldArray (sfSigners).size() : 0; return baseFee + (signerCount * baseFee); } @@ -149,7 +149,7 @@ Transactor::calculateFeePaid(STTx const& tx) } XRPAmount -Transactor::minimumFee (Application& app, std::uint64_t baseFee, +Transactor::minimumFee (Application& app, FeeUnit64 baseFee, Fees const& fees, ApplyFlags flags) { return scaleFeeLoad (baseFee, app.getFeeTrack (), @@ -164,7 +164,7 @@ Transactor::calculateMaxSpend(STTx const& tx) TER Transactor::checkFee (PreclaimContext const& ctx, - std::uint64_t baseFee) + FeeUnit64 baseFee) { auto const feePaid = calculateFeePaid(ctx.tx); if (!isLegalAmount (feePaid) || feePaid < beast::zero) diff --git a/src/ripple/app/tx/impl/Transactor.h b/src/ripple/app/tx/impl/Transactor.h index 5ea9388030b..f9ed83320a6 100644 --- a/src/ripple/app/tx/impl/Transactor.h +++ b/src/ripple/app/tx/impl/Transactor.h @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include @@ -122,7 +122,7 @@ class Transactor static TER - checkFee (PreclaimContext const& ctx, std::uint64_t baseFee); + checkFee (PreclaimContext const& ctx, FeeUnit64 baseFee); static NotTEC @@ -130,7 +130,7 @@ class Transactor // Returns the fee in fee units, not scaled for load. static - std::uint64_t + FeeUnit64 calculateBaseFee ( ReadView const& view, STTx const& tx); @@ -182,7 +182,7 @@ class Transactor */ static XRPAmount - minimumFee (Application& app, std::uint64_t baseFee, + minimumFee (Application& app, FeeUnit64 baseFee, Fees const& fees, ApplyFlags flags); private: diff --git a/src/ripple/app/tx/impl/applySteps.cpp b/src/ripple/app/tx/impl/applySteps.cpp index 9ed43f24a85..28985a5b241 100644 --- a/src/ripple/app/tx/impl/applySteps.cpp +++ b/src/ripple/app/tx/impl/applySteps.cpp @@ -141,7 +141,7 @@ invoke_preclaim (PreclaimContext const& ctx) } static -std::uint64_t +FeeUnit64 invoke_calculateBaseFee( ReadView const& view, STTx const& tx) @@ -308,7 +308,7 @@ preclaim (PreflightResult const& preflightResult, } } -std::uint64_t +FeeUnit64 calculateBaseFee(ReadView const& view, STTx const& tx) { diff --git a/src/ripple/basics/XRPAmount.h b/src/ripple/basics/XRPAmount.h new file mode 100644 index 00000000000..4f3cf8e5a7f --- /dev/null +++ b/src/ripple/basics/XRPAmount.h @@ -0,0 +1,300 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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_BASICS_XRPAMOUNT_H_INCLUDED +#define RIPPLE_BASICS_XRPAMOUNT_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +class XRPAmount + : private boost::totally_ordered + , private boost::additive + , private boost::dividable + , private boost::modable + , private boost::unit_steppable +{ +public: + using unit_type = XRPAmount; + using value_type = std::int64_t; +private: + value_type drops_; + +protected: + template + static constexpr bool is_compatible_v = std::is_integral_v; + + template + using enable_if_compatible_t = + typename std::enable_if_t>; + +public: + XRPAmount () = default; + constexpr XRPAmount (XRPAmount const& other) = default; + constexpr XRPAmount& operator= (XRPAmount const& other) = default; + + constexpr + XRPAmount (beast::Zero) + : drops_ (0) + { + static_assert(std::is_convertible_v, + "conversion"); + } + + constexpr + XRPAmount& + operator= (beast::Zero) + { + drops_ = 0; + return *this; + } + + constexpr + XRPAmount (value_type drops) + : drops_ (drops) + { + } + + template > + constexpr + XRPAmount (Other drops) + : drops_ (static_cast (drops)) + { + } + + template > + XRPAmount& + operator= (Other drops) + { + drops_ = static_cast (drops); + return *this; + } + + constexpr + XRPAmount + operator*(value_type const& rhs) const + { + return { drops_ * rhs }; + } + + constexpr + value_type + operator/(XRPAmount const& rhs) const + { + return drops_ / rhs.drops_; + } + + XRPAmount& + operator+= (XRPAmount const& other) + { + drops_ += other.drops(); + return *this; + } + + XRPAmount& + operator-= (XRPAmount const& other) + { + drops_ -= other.drops(); + return *this; + } + + XRPAmount& + operator++() + { + ++drops_; + return *this; + } + + XRPAmount& + operator--() + { + --drops_; + return *this; + } + + XRPAmount& + operator*= (value_type const& rhs) + { + drops_ *= rhs; + return *this; + } + + XRPAmount& + operator/= (value_type const& rhs) + { + drops_ /= rhs; + return *this; + } + + XRPAmount& + operator%= (value_type const& rhs) + { + drops_ %= rhs; + return *this; + } + + XRPAmount + operator- () const + { + return { -drops_ }; + } + + bool + operator==(XRPAmount const& other) const + { + return drops_ == other.drops_; + } + + bool + operator<(XRPAmount const& other) const + { + return drops_ < other.drops_; + } + + /** Returns true if the amount is not zero */ + explicit + constexpr + operator bool() const noexcept + { + return drops_ != 0; + } + + /** Return the sign of the amount */ + constexpr + int + signum() const noexcept + { + return (drops_ < 0) ? -1 : (drops_ ? 1 : 0); + } + + /** Returns the number of drops */ + constexpr + value_type + drops () const + { + return drops_; + } + + Json::Value + json () const + { + static_assert(std::is_signed_v, "Expected XRPAmount to be signed"); + + constexpr auto min = std::numeric_limits::min(); + constexpr auto max = std::numeric_limits::max(); + + if (drops_ < min) + return min; + if (drops_ > max) + return max; + return static_cast(drops_); + } + + /** Returns the underlying value. Code SHOULD NOT call this + function unless the type has been abstracted away, + e.g. in a templated function. + */ + constexpr + value_type + value () const + { + return drops_; + } + + friend + std::istream& + operator>> (std::istream& s, XRPAmount& val) + { + s >> val.drops_; + return s; + } + +}; + +// Output XRPAmount as just the drops value. +template +std::basic_ostream& +operator<<(std::basic_ostream& os, + const XRPAmount& q) +{ + os << q.drops(); + + return os; +} + +inline +std::string +to_string (XRPAmount const& amount) +{ + return std::to_string (amount.drops ()); +} + +inline +constexpr +XRPAmount +operator*(XRPAmount::value_type lhs, XRPAmount const& rhs) +{ + // multiplication is commutative + return rhs * lhs; +} + +inline +XRPAmount +mulRatio ( + XRPAmount const& amt, + std::uint32_t num, + std::uint32_t den, + bool roundUp) +{ + using namespace boost::multiprecision; + + if (!den) + Throw ("division by zero"); + + int128_t const amt128 (amt.drops ()); + auto const neg = amt.drops () < 0; + auto const m = amt128 * num; + auto r = m / den; + if (m % den) + { + if (!neg && roundUp) + r += 1; + if (neg && !roundUp) + r -= 1; + } + if (r > std::numeric_limits::max ()) + Throw ("XRP mulRatio overflow"); + if (r < std::numeric_limits::min ()) + Throw ("XRP mulRatio underflow"); + return XRPAmount (r.convert_to ()); +} + +} + +#endif // RIPPLE_BASICS_XRPAMOUNT_H_INCLUDED diff --git a/src/ripple/basics/feeunits.h b/src/ripple/basics/feeunits.h new file mode 100644 index 00000000000..f722c8d3e69 --- /dev/null +++ b/src/ripple/basics/feeunits.h @@ -0,0 +1,542 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2019 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef BASICS_FEES_H_INCLUDED +#define BASICS_FEES_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +namespace units { + +struct feeunit_tag; +struct feelevel_tag; +struct unitless_tag; + +template +using enable_if_unit_t = typename std::enable_if_t< + std::is_class::value && + std::is_object::value && + std::is_object::value +>; + +template > +constexpr bool is_usable_unit_v = + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v; + + +template +class TaggedFee + : private boost::totally_ordered > + , private boost::additive > + , private boost::dividable , T> + , private boost::modable , T> + , private boost::unit_steppable > +{ +public: + using unit_type = UnitTag; + using value_type = T; +private: + value_type fee_; + + typedef void (TaggedFee::*unspecified_null_pointer_constant_type)(int*******); + +protected: + template + static constexpr bool is_compatible_v = std::is_integral_v && + std::is_convertible_v; + + template > + static constexpr bool is_compatiblefee_v = + is_compatible_v && + std::is_same_v; + + template + using enable_if_compatible_t = + typename std::enable_if_t>; + + template + using enable_if_compatiblefee_t = + typename std::enable_if_t>; + +public: + TaggedFee () = default; + constexpr TaggedFee (TaggedFee const& other) = default; + constexpr TaggedFee& operator= (TaggedFee const& other) = default; + + // Little trick stolen from boost::units that allows a 0 in the + // implicit constructor, but no other value + constexpr TaggedFee(unspecified_null_pointer_constant_type) : fee_() + { + } + + constexpr + explicit + TaggedFee (beast::Zero) + : fee_ (0) + { + } + + constexpr + TaggedFee& + operator= (beast::Zero) + { + fee_ = 0; + return *this; + } + + constexpr + explicit + TaggedFee (value_type fee) + : fee_ (fee) + { + } + + template > + constexpr + explicit + TaggedFee (Other fee) + : fee_ (static_cast (fee)) + { + } + + template > + constexpr + TaggedFee (OtherFee const& fee) + : fee_ (static_cast (fee.value())) + { + } + + template > + TaggedFee& + operator= (Other fee) + { + fee_ = static_cast (fee); + return *this; + } + + template > + TaggedFee& + operator= (OtherFee const& fee) + { + fee_ = static_cast (fee.value()); + return *this; + } + + constexpr + TaggedFee + operator* (value_type const& rhs) const + { + return TaggedFee{ fee_ * rhs }; + } + + constexpr + value_type + operator/ (TaggedFee const& rhs) const + { + return fee_ / rhs.fee_; + } + + TaggedFee& + operator+= (TaggedFee const& other) + { + fee_ += other.fee(); + return *this; + } + + TaggedFee& + operator-= (TaggedFee const& other) + { + fee_ -= other.fee(); + return *this; + } + + TaggedFee& + operator++() + { + ++fee_; + return *this; + } + + TaggedFee& + operator--() + { + --fee_; + return *this; + } + + TaggedFee& + operator*= (value_type const& rhs) + { + fee_ *= rhs; + return *this; + } + + TaggedFee& + operator/= (value_type const& rhs) + { + fee_ /= rhs; + return *this; + } + + TaggedFee& + operator%= (value_type const& rhs) + { + fee_ %= rhs; + return *this; + } + + TaggedFee + operator- () const + { + static_assert( std::is_unsigned_v, + "- operator illegal on unsigned fee types"); + return { -fee_ }; + } + + bool + operator==(TaggedFee const& other) const + { + return fee_ == other.fee_; + } + + bool + operator<(TaggedFee const& other) const + { + return fee_ < other.fee_; + } + + /** Returns true if the amount is not zero */ + explicit + constexpr + operator bool() const noexcept + { + return fee_ != 0; + } + + /** Return the sign of the amount */ + constexpr + int + signum() const noexcept + { + return (fee_ < 0) ? -1 : (fee_ ? 1 : 0); + } + + /** Returns the number of drops */ + constexpr + value_type + fee () const + { + return fee_; + } + + Json::Value + json () const + { + static_assert(is_usable_unit_v, "Invalid unit type"); + using jsontype = std::conditional_t, + Json::Int, Json::UInt>; + + constexpr auto min = std::numeric_limits::min(); + constexpr auto max = std::numeric_limits::max(); + + if (fee_ < min) + return min; + if (fee_ > max) + return max; + return static_cast(fee_); + } + + + /** Returns the underlying value. Code SHOULD NOT call this + function unless the type has been abstracted away, + e.g. in a templated function. + */ + constexpr + value_type + value () const + { + return fee_; + } + + friend + std::istream& + operator>> (std::istream& s, TaggedFee& val) + { + s >> val.fee_; + return s; + } + +}; + +// Output Fees as just their numeric value. +template +std::basic_ostream& +operator<<(std::basic_ostream& os, + const TaggedFee& q) +{ + os << q.value(); + + return os; +} + +template +std::string +to_string (TaggedFee const& amount) +{ + return std::to_string (amount.fee ()); +} + +template +constexpr +TaggedFee +operator*(T lhs, TaggedFee const& rhs) +{ + // multiplication is commutative + return rhs * lhs; +} + +template > +constexpr bool can_muldiv_source_v = + std::is_convertible_v; + +template > +constexpr bool can_muldiv_dest_v = + can_muldiv_source_v && // Dest is also a source + std::is_convertible_v && + sizeof(typename Dest::value_type) >= sizeof(std::uint64_t); + +template, + class = enable_if_unit_t> +constexpr bool can_muldiv_sources_v = + can_muldiv_source_v && + can_muldiv_source_v && + std::is_same_v; + +template, + class = enable_if_unit_t, + class = enable_if_unit_t> +constexpr bool can_muldiv_v = + can_muldiv_sources_v && + can_muldiv_dest_v; + // Source and Dest can be the same by default + +template, + class = enable_if_unit_t, + class = enable_if_unit_t> +constexpr bool can_muldiv_commute_v = + can_muldiv_v && + ! std::is_same_v; + +template +using enable_muldiv_source_t = + typename std::enable_if_t< can_muldiv_source_v >; + +template +using enable_muldiv_dest_t = + typename std::enable_if_t< can_muldiv_dest_v >; + +template +using enable_muldiv_sources_t = + typename std::enable_if_t< can_muldiv_sources_v >; + +template +using enable_muldiv_t = + typename std::enable_if_t< can_muldiv_v >; + +template +using enable_muldiv_commute_t = + typename std::enable_if_t< can_muldiv_commute_v >; + +template +TaggedFee +scalar(T value) +{ + return TaggedFee{ value }; +} + +template > +std::pair +mulDivU(Source1 value, Dest mul, Source2 div) +{ + // Shortcuts, since these happen a lot in the real world + if (value == div) + return { true, mul }; + if (mul.value() == div.value()) + return { true, Dest{ value.value() } }; + + using namespace boost::multiprecision; + using desttype = typename Dest::value_type; + + constexpr auto max = + std::numeric_limits::max(); + + if(value.value() < 0 || mul.value() < 0 || div.value() < 0) + { + // split the asserts so if one hits, the user can tell which + // without a debugger. + assert(value.value() >= 0); + assert(mul.value() >= 0); + assert(div.value() >= 0); + return { false, Dest{ max } }; + } + + uint128_t product; + product = multiply(product, + static_cast(value.value()), + static_cast(mul.value())); + + auto quotient = product / div.value(); + + if (quotient > max) + return { false, Dest{ max } }; + + return { true, Dest{ static_cast(quotient) } }; +} + +} // units + +template +using FeeUnit = units::TaggedFee; +using FeeUnit32 = FeeUnit; +using FeeUnit64 = FeeUnit; + +template +using FeeLevel = units::TaggedFee; +using FeeLevel64 = FeeLevel; +using FeeLevelDouble = FeeLevel; + +template > +std::pair +mulDiv(Source1 value, Dest mul, Source2 div) +{ + return units::mulDivU(value, mul, div); +} + +template > +std::pair +mulDiv(Dest value, Source1 mul, Source2 div) +{ + // Multiplication is commutative + return units::mulDivU(mul, value, div); +} + +template > +std::pair +mulDiv(std::uint64_t value, + Dest mul, + std::uint64_t div) +{ + // Give the scalars a non-tag so the + // unit-handling version gets called. + return units::mulDivU(units::scalar(value), mul, units::scalar(div)); +} + +template > +std::pair +mulDiv(Dest value, std::uint64_t mul, std::uint64_t div) +{ + // Multiplication is commutative + return mulDiv(mul, value, div); +} + +template > +std::pair +mulDiv(Source1 value, + std::uint64_t mul, + Source2 div) +{ + // Give the scalars a dimensionless unit so the + // unit-handling version gets called. + auto unitresult = units::mulDivU(value, units::scalar(mul), div); + return { unitresult.first, unitresult.second.value() }; +} + +template > +std::pair +mulDiv(std::uint64_t value, Source1 mul, Source2 div) +{ + // Multiplication is commutative + return mulDiv(mul, value, div); +} + +template +constexpr +std::enable_if_t +< + std::is_same_v && + std::is_integral_v && + std::is_integral_v, + Dest +> +safe_cast(Src s) noexcept +{ + // Dest may not have an explicit value constructor + return Dest{ safe_cast(s.value()) }; +} + +template +constexpr +std::enable_if_t +< + std::is_same_v && + std::is_integral_v && + std::is_integral_v, + Dest +> +unsafe_cast(Src s) noexcept +{ + // Dest may not have an explicit value constructor + return Dest{ unsafe_cast(s.value()) }; +} + +} // ripple + +#endif // BASICS_FEES_H_INCLUDED diff --git a/src/ripple/basics/impl/mulDiv.cpp b/src/ripple/basics/impl/mulDiv.cpp index 69fc2483430..232817aaeab 100644 --- a/src/ripple/basics/impl/mulDiv.cpp +++ b/src/ripple/basics/impl/mulDiv.cpp @@ -36,7 +36,7 @@ mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div) result /= div; - auto const limit = std::numeric_limits::max(); + auto constexpr limit = std::numeric_limits::max(); if (result > limit) return { false, limit }; diff --git a/src/ripple/basics/safe_cast.h b/src/ripple/basics/safe_cast.h index 80422c02d7c..4894096b795 100644 --- a/src/ripple/basics/safe_cast.h +++ b/src/ripple/basics/safe_cast.h @@ -73,6 +73,56 @@ safe_cast(Src s) noexcept return safe_cast(static_cast>(s)); } +// unsafe_cast explicitly flags a static_cast as not necessarily able to hold +// all values of the source. It includes a compile-time check so that if +// underlying types become safe, it can be converted to a safe_cast. + +template +inline +constexpr +std::enable_if_t +< + std::is_integral::value && std::is_integral::value, + Dest +> +unsafe_cast(Src s) noexcept +{ + constexpr unsigned not_same = std::is_signed::value != + std::is_signed::value; + static_assert((std::is_unsigned::value && + std::is_signed::value) || + sizeof(Dest) < sizeof(Src) + not_same, + "Only unsafe if casting signed to unsigned or " + "destination is too small"); + return static_cast(s); +} + +template +inline +constexpr +std::enable_if_t +< + std::is_enum::value && std::is_integral::value, + Dest +> +unsafe_cast(Src s) noexcept +{ + return static_cast(unsafe_cast>(s)); +} + +template +inline +constexpr +std::enable_if_t +< + std::is_integral::value && std::is_enum::value, + Dest +> +unsafe_cast(Src s) noexcept +{ + return unsafe_cast(static_cast>(s)); +} + } // ripple #endif diff --git a/src/ripple/basics/tagged_integer.h b/src/ripple/basics/tagged_integer.h index 5267fa9359b..4afe0847a90 100644 --- a/src/ripple/basics/tagged_integer.h +++ b/src/ripple/basics/tagged_integer.h @@ -68,9 +68,9 @@ class tagged_integer std::is_integral::value && sizeof(OtherInt) <= sizeof(Int)>::type> explicit - /* constexpr */ - tagged_integer(OtherInt value) noexcept - : m_value(value) + constexpr + tagged_integer(OtherInt value) noexcept + : m_value(value) { static_assert( sizeof(tagged_integer) == sizeof(Int), diff --git a/src/ripple/beast/cxx17/type_traits.h b/src/ripple/beast/cxx17/type_traits.h index 95f42b1050b..a1af076f239 100644 --- a/src/ripple/beast/cxx17/type_traits.h +++ b/src/ripple/beast/cxx17/type_traits.h @@ -35,6 +35,13 @@ namespace std { #endif // ! __cpp_lib_void_t + #if ! __cpp_lib_common_type_t + + template + using common_type_t = typename std::common_type::type; + + #endif // ! __cpp_lib_void_t + #if ! __cpp_lib_bool_constant template @@ -42,6 +49,62 @@ namespace std { #endif // ! __cpp_lib_bool_constant + #if ! __cpp_lib_is_integral_v + + template + constexpr bool is_integral_v = + std::is_integral::value; + + #endif // ! __cpp_lib_is_integral_v + + #if ! __cpp_lib_is_floating_point_v + + template + constexpr bool is_floating_point_v = + std::is_floating_point::value; + + #endif // ! __cpp_lib_is_floating_point_v + + #if ! __cpp_lib_is_assignable_v + + template + constexpr bool is_assignable_v = + std::is_assignable::value; + + #endif // ! __cpp_lib_is_assignable_v + + #if ! __cpp_lib_is_convertible_v + + template + constexpr bool is_convertible_v = + std::is_convertible::value; + + #endif // ! __cpp_lib_is_convertible_v + + #if ! __cpp_lib_is_same_v + + template + constexpr bool is_same_v = + std::is_same::value; + + #endif // ! __cpp_lib_is_same_v + + #if ! __cpp_lib_is_signed_v + + template + constexpr bool is_signed_v = + std::is_signed::value; + + #endif // ! __cpp_lib_is_signed_v + + #if ! __cpp_lib_is_unsigned_v + + template + constexpr bool is_unsigned_v = + std::is_unsigned::value; + + #endif // ! __cpp_lib_is_unsigned_v + #endif // Ideas from Howard Hinnant diff --git a/src/ripple/core/Config.h b/src/ripple/core/Config.h index daf71dbcff5..e2f8419cff1 100644 --- a/src/ripple/core/Config.h +++ b/src/ripple/core/Config.h @@ -22,6 +22,7 @@ #include #include +#include #include // VFALCO Breaks levelization #include #include @@ -138,14 +139,18 @@ class Config : public BasicConfig std::string START_LEDGER; // Network parameters - int const TRANSACTION_FEE_BASE = 10; // The number of fee units a reference transaction costs + + // The number of fee units a reference transaction costs + static constexpr FeeUnit32 TRANSACTION_FEE_BASE{ 10 }; // Note: The following parameters do not relate to the UNL or trust at all // Minimum number of nodes to consider the network present std::size_t NETWORK_QUORUM = 1; // Peer networking parameters - bool PEER_PRIVATE = false; // True to ask peers not to relay current IP. + + // True to ask peers not to relay current IP. + bool PEER_PRIVATE = false; std::size_t PEERS_MAX = 0; std::chrono::seconds WEBSOCKET_PING_FREQ = std::chrono::minutes {5}; @@ -159,10 +164,9 @@ class Config : public BasicConfig // Validation boost::optional VALIDATION_QUORUM; // validations to consider ledger authoritative - std::uint64_t FEE_DEFAULT = 10; - std::uint64_t FEE_ACCOUNT_RESERVE = 200*SYSTEM_CURRENCY_PARTS; - std::uint64_t FEE_OWNER_RESERVE = 50*SYSTEM_CURRENCY_PARTS; - std::uint64_t FEE_OFFER = 10; + XRPAmount FEE_DEFAULT{ 10 }; + XRPAmount FEE_ACCOUNT_RESERVE{ 200 * DROPS_PER_XRP }; + XRPAmount FEE_OWNER_RESERVE{ 50 * DROPS_PER_XRP }; // Node storage configuration std::uint32_t LEDGER_HISTORY = 256; diff --git a/src/ripple/core/ConfigSections.h b/src/ripple/core/ConfigSections.h index bc4e36478f6..e5f1a3f490e 100644 --- a/src/ripple/core/ConfigSections.h +++ b/src/ripple/core/ConfigSections.h @@ -40,7 +40,6 @@ struct ConfigSection #define SECTION_DEBUG_LOGFILE "debug_logfile" #define SECTION_ELB_SUPPORT "elb_support" #define SECTION_FEE_DEFAULT "fee_default" -#define SECTION_FEE_OFFER "fee_offer" #define SECTION_FEE_ACCOUNT_RESERVE "fee_account_reserve" #define SECTION_FEE_OWNER_RESERVE "fee_owner_reserve" #define SECTION_FETCH_DEPTH "fetch_depth" diff --git a/src/ripple/core/impl/Config.cpp b/src/ripple/core/impl/Config.cpp index adb3039d5b8..8300f7cd06d 100644 --- a/src/ripple/core/impl/Config.cpp +++ b/src/ripple/core/impl/Config.cpp @@ -151,6 +151,8 @@ getEnvVar (char const* name) return value; } +constexpr FeeUnit32 Config::TRANSACTION_FEE_BASE; + void Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone) { @@ -363,16 +365,13 @@ void Config::loadFromString (std::string const& fileContents) NETWORK_QUORUM = beast::lexicalCastThrow(strTemp); if (getSingleSection (secConfig, SECTION_FEE_ACCOUNT_RESERVE, strTemp, j_)) - FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow (strTemp); + FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow (strTemp); if (getSingleSection (secConfig, SECTION_FEE_OWNER_RESERVE, strTemp, j_)) - FEE_OWNER_RESERVE = beast::lexicalCastThrow (strTemp); - - if (getSingleSection (secConfig, SECTION_FEE_OFFER, strTemp, j_)) - FEE_OFFER = beast::lexicalCastThrow (strTemp); + FEE_OWNER_RESERVE = beast::lexicalCastThrow (strTemp); if (getSingleSection (secConfig, SECTION_FEE_DEFAULT, strTemp, j_)) - FEE_DEFAULT = beast::lexicalCastThrow (strTemp); + FEE_DEFAULT = beast::lexicalCastThrow (strTemp); if (getSingleSection (secConfig, SECTION_LEDGER_HISTORY, strTemp, j_)) { diff --git a/src/ripple/json/impl/json_assert.h b/src/ripple/json/impl/json_assert.h index fc8ab335aa4..c4af5c04435 100644 --- a/src/ripple/json/impl/json_assert.h +++ b/src/ripple/json/impl/json_assert.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_JSON_JSON_ASSERT_H_INCLUDED #define RIPPLE_JSON_JSON_ASSERT_H_INCLUDED -#include "ripple/json/json_errors.h" +#include #define JSON_ASSERT_UNREACHABLE assert( false ) #define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw diff --git a/src/ripple/ledger/OpenView.h b/src/ripple/ledger/OpenView.h index bda42e0fe9b..d25bf77060f 100644 --- a/src/ripple/ledger/OpenView.h +++ b/src/ripple/ledger/OpenView.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/ripple/ledger/ReadView.h b/src/ripple/ledger/ReadView.h index 2548870d80e..f4328654b69 100644 --- a/src/ripple/ledger/ReadView.h +++ b/src/ripple/ledger/ReadView.h @@ -22,12 +22,13 @@ #include #include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -45,10 +46,10 @@ namespace ripple { */ struct Fees { - std::uint64_t base = 0; // Reference tx cost (drops) - std::uint32_t units = 0; // Reference fee units - std::uint32_t reserve = 0; // Reserve base (drops) - std::uint32_t increment = 0; // Reserve increment (drops) + XRPAmount base{ 0 }; // Reference tx cost (drops) + FeeUnit32 units{ 0 }; // Reference fee units + XRPAmount reserve{ 0 }; // Reserve base (drops), 32-bits by protocol + XRPAmount increment{ 0 }; // Reserve increment (drops), 32-bits explicit Fees() = default; Fees (Fees const&) = default; @@ -62,7 +63,13 @@ struct Fees XRPAmount accountReserve (std::size_t ownerCount) const { - return { reserve + ownerCount * increment }; + return reserve + ownerCount * increment; + } + + std::pair + toDrops(FeeUnit64 const& fee) const + { + return mulDiv(base, fee, units); } }; diff --git a/src/ripple/ledger/detail/ApplyStateTable.h b/src/ripple/ledger/detail/ApplyStateTable.h index 66c5dd224a9..ab9ff4b3ee5 100644 --- a/src/ripple/ledger/detail/ApplyStateTable.h +++ b/src/ripple/ledger/detail/ApplyStateTable.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/ripple/ledger/detail/ApplyViewBase.h b/src/ripple/ledger/detail/ApplyViewBase.h index cf7a52a74b7..91bf1e02dca 100644 --- a/src/ripple/ledger/detail/ApplyViewBase.h +++ b/src/ripple/ledger/detail/ApplyViewBase.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include namespace ripple { namespace detail { diff --git a/src/ripple/protocol/AmountConversions.h b/src/ripple/protocol/AmountConversions.h index 3525b848396..5de7bc92714 100644 --- a/src/ripple/protocol/AmountConversions.h +++ b/src/ripple/protocol/AmountConversions.h @@ -21,7 +21,7 @@ #define RIPPLE_PROTOCOL_AMOUNTCONVERSION_H_INCLUDED #include -#include +#include #include namespace ripple { diff --git a/src/ripple/protocol/PayChan.h b/src/ripple/protocol/PayChan.h index 90352b8a820..0525ee61a93 100644 --- a/src/ripple/protocol/PayChan.h +++ b/src/ripple/protocol/PayChan.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace ripple { diff --git a/src/ripple/protocol/Quality.h b/src/ripple/protocol/Quality.h index e8016e0c307..07fe336a6ff 100644 --- a/src/ripple/protocol/Quality.h +++ b/src/ripple/protocol/Quality.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/ripple/protocol/STAmount.h b/src/ripple/protocol/STAmount.h index e4d4cbd0343..80e6b2bcac5 100644 --- a/src/ripple/protocol/STAmount.h +++ b/src/ripple/protocol/STAmount.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include namespace ripple { diff --git a/src/ripple/protocol/STObject.h b/src/ripple/protocol/STObject.h index 37a36ac0dd0..0be43eb62bc 100644 --- a/src/ripple/protocol/STObject.h +++ b/src/ripple/protocol/STObject.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +86,13 @@ class STObject ValueProxy&> operator= (U&& u); + template + std::enable_if_t< + units::is_usable_unit_v && + std::is_assignable_v, + ValueProxy&> + operator= (Fee const& u); + operator value_type() const; private: @@ -213,6 +221,13 @@ class STObject OptionalProxy&> operator= (U&& u); + template + std::enable_if_t< + units::is_usable_unit_v && + std::is_assignable_v, + OptionalProxy&> + operator= (Fee const& u); + private: friend class STObject; @@ -472,7 +487,9 @@ class STObject void setFieldU8 (SField const& field, unsigned char); void setFieldU16 (SField const& field, std::uint16_t); void setFieldU32 (SField const& field, std::uint32_t); + void setFieldU32 (SField const& field, XRPAmount); void setFieldU64 (SField const& field, std::uint64_t); + void setFieldU64 (SField const& field, XRPAmount); void setFieldH128 (SField const& field, uint128 const&); void setFieldH256 (SField const& field, uint256 const& ); void setFieldVL (SField const& field, Blob const&); @@ -734,6 +751,18 @@ STObject::ValueProxy::operator= (U&& u) return *this; } +template +template +std::enable_if_t< + units::is_usable_unit_v && + std::is_assignable_v, + STObject::ValueProxy&> +STObject::ValueProxy::operator= (Fee const& u) +{ + this->assign(u.value()); + return *this; +} + template STObject::ValueProxy::operator value_type() const { @@ -821,6 +850,18 @@ STObject::OptionalProxy::operator=(U&& u) return *this; } +template +template +std::enable_if_t< + units::is_usable_unit_v && + std::is_assignable_v, + STObject::OptionalProxy&> +STObject::OptionalProxy::operator= (Fee const& u) +{ + this->assign(u.value()); + return *this; +} + template STObject::OptionalProxy::OptionalProxy( STObject* st, TypedField const* f) diff --git a/src/ripple/protocol/STValidation.h b/src/ripple/protocol/STValidation.h index f09c0219081..0d84b83e83a 100644 --- a/src/ripple/protocol/STValidation.h +++ b/src/ripple/protocol/STValidation.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_PROTOCOL_STVALIDATION_H_INCLUDED #define RIPPLE_PROTOCOL_STVALIDATION_H_INCLUDED +#include #include #include #include @@ -100,9 +101,9 @@ class STValidation final : public STObject, public CountedObject struct FeeSettings { boost::optional loadFee; - boost::optional baseFee; - boost::optional reserveBase; - boost::optional reserveIncrement; + boost::optional baseFee; + boost::optional reserveBase; + boost::optional reserveIncrement; }; /** Construct, sign and trust a new STValidation diff --git a/src/ripple/protocol/SystemParameters.h b/src/ripple/protocol/SystemParameters.h index ef95206f5c5..185645d6f08 100644 --- a/src/ripple/protocol/SystemParameters.h +++ b/src/ripple/protocol/SystemParameters.h @@ -20,6 +20,7 @@ #ifndef RIPPLE_PROTOCOL_SYSTEMPARAMETERS_H_INCLUDED #define RIPPLE_PROTOCOL_SYSTEMPARAMETERS_H_INCLUDED +#include #include #include @@ -37,23 +38,25 @@ systemName () } /** Configure the native currency. */ -static -std::uint64_t const -SYSTEM_CURRENCY_GIFT = 1000; - -static -std::uint64_t const -SYSTEM_CURRENCY_USERS = 100000000; /** Number of drops per 1 XRP */ static -std::uint64_t const -SYSTEM_CURRENCY_PARTS = 1000000; +constexpr +XRPAmount +DROPS_PER_XRP{ 1'000'000 }; /** Number of drops in the genesis account. */ static -std::uint64_t const -SYSTEM_CURRENCY_START = SYSTEM_CURRENCY_GIFT * SYSTEM_CURRENCY_USERS * SYSTEM_CURRENCY_PARTS; +constexpr +XRPAmount +INITIAL_XRP{ 100'000'000'000 * DROPS_PER_XRP }; + +/** Returns true if the amount does not exceed the initial XRP in existence. */ +inline +bool isLegalAmount (XRPAmount const& amount) +{ + return amount <= INITIAL_XRP; +} /* The currency code for the native currency. */ static inline diff --git a/src/ripple/protocol/XRPAmount.h b/src/ripple/protocol/XRPAmount.h deleted file mode 100644 index cd625cd6b9a..00000000000 --- a/src/ripple/protocol/XRPAmount.h +++ /dev/null @@ -1,175 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 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_PROTOCOL_XRPAMOUNT_H_INCLUDED -#define RIPPLE_PROTOCOL_XRPAMOUNT_H_INCLUDED - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { - -class XRPAmount - : private boost::totally_ordered - , private boost::additive -{ -private: - std::int64_t drops_; - -public: - XRPAmount () = default; - XRPAmount (XRPAmount const& other) = default; - XRPAmount& operator= (XRPAmount const& other) = default; - - XRPAmount (beast::Zero) - : drops_ (0) - { - } - - XRPAmount& - operator= (beast::Zero) - { - drops_ = 0; - return *this; - } - - template ::value>> - XRPAmount (Integer drops) - : drops_ (static_cast (drops)) - { - } - - template ::value>> - XRPAmount& - operator= (Integer drops) - { - drops_ = static_cast (drops); - return *this; - } - - XRPAmount& - operator+= (XRPAmount const& other) - { - drops_ += other.drops_; - return *this; - } - - XRPAmount& - operator-= (XRPAmount const& other) - { - drops_ -= other.drops_; - return *this; - } - - XRPAmount - operator- () const - { - return { -drops_ }; - } - - bool - operator==(XRPAmount const& other) const - { - return drops_ == other.drops_; - } - - bool - operator<(XRPAmount const& other) const - { - return drops_ < other.drops_; - } - - /** Returns true if the amount is not zero */ - explicit - operator bool() const noexcept - { - return drops_ != 0; - } - - /** Return the sign of the amount */ - int - signum() const noexcept - { - return (drops_ < 0) ? -1 : (drops_ ? 1 : 0); - } - - /** Returns the number of drops */ - std::int64_t - drops () const - { - return drops_; - } -}; - -inline -std::string -to_string (XRPAmount const& amount) -{ - return std::to_string (amount.drops ()); -} - -inline -XRPAmount -mulRatio ( - XRPAmount const& amt, - std::uint32_t num, - std::uint32_t den, - bool roundUp) -{ - using namespace boost::multiprecision; - - if (!den) - Throw ("division by zero"); - - int128_t const amt128 (amt.drops ()); - auto const neg = amt.drops () < 0; - auto const m = amt128 * num; - auto r = m / den; - if (m % den) - { - if (!neg && roundUp) - r += 1; - if (neg && !roundUp) - r -= 1; - } - if (r > std::numeric_limits::max ()) - Throw ("XRP mulRatio overflow"); - return XRPAmount (r.convert_to ()); -} - -/** Returns true if the amount does not exceed the initial XRP in existence. */ -inline -bool isLegalAmount (XRPAmount const& amount) -{ - return amount.drops () <= SYSTEM_CURRENCY_START; -} - -} - -#endif diff --git a/src/ripple/protocol/impl/STAmount.cpp b/src/ripple/protocol/impl/STAmount.cpp index 9bfb883367a..d5e06326d55 100644 --- a/src/ripple/protocol/impl/STAmount.cpp +++ b/src/ripple/protocol/impl/STAmount.cpp @@ -280,9 +280,9 @@ STAmount::STAmount (XRPAmount const& amount) , mIsNegative (amount < beast::zero) { if (mIsNegative) - mValue = static_cast (-amount.drops ()); + mValue = unsafe_cast (-amount.drops ()); else - mValue = static_cast (amount.drops ()); + mValue = unsafe_cast (amount.drops ()); canonicalize (); } @@ -298,12 +298,14 @@ STAmount::construct (SerialIter& sit, SField const& name) // Conversion // //------------------------------------------------------------------------------ -XRPAmount STAmount::xrp () const +XRPAmount +STAmount::xrp () const { if (!mIsNative) - Throw ("Cannot return non-native STAmount as XRPAmount"); + Throw ( + "Cannot return non-native STAmount as XRPAmount"); - auto drops = static_cast (mValue); + auto drops = static_cast (mValue); if (mIsNegative) drops = -drops; @@ -311,7 +313,8 @@ XRPAmount STAmount::xrp () const return { drops }; } -IOUAmount STAmount::iou () const +IOUAmount +STAmount::iou () const { if (mIsNative) Throw ("Cannot return native STAmount as IOUAmount"); diff --git a/src/ripple/protocol/impl/STObject.cpp b/src/ripple/protocol/impl/STObject.cpp index 752b2155f25..03f67afbe5a 100644 --- a/src/ripple/protocol/impl/STObject.cpp +++ b/src/ripple/protocol/impl/STObject.cpp @@ -603,11 +603,24 @@ void STObject::setFieldU32 (SField const& field, std::uint32_t v) setFieldUsingSetValue (field, v); } +void STObject::setFieldU32 (SField const& field, XRPAmount v) +{ + assert(v > beast::zero && + v <= std::numeric_limits::max()); + setFieldUsingSetValue (field, v.drops()); +} + void STObject::setFieldU64 (SField const& field, std::uint64_t v) { setFieldUsingSetValue (field, v); } +void STObject::setFieldU64 (SField const& field, XRPAmount v) +{ + assert(v > beast::zero); + setFieldUsingSetValue (field, v.drops()); +} + void STObject::setFieldH128 (SField const& field, uint128 const& v) { setFieldUsingSetValue (field, v); diff --git a/src/ripple/rpc/handlers/NoRippleCheck.cpp b/src/ripple/rpc/handlers/NoRippleCheck.cpp index fdd264e5878..4fd2e50fd02 100644 --- a/src/ripple/rpc/handlers/NoRippleCheck.cpp +++ b/src/ripple/rpc/handlers/NoRippleCheck.cpp @@ -43,8 +43,8 @@ static void fillTransaction ( auto& fees = ledger.fees(); // Convert the reference transaction cost in fee units to drops // scaled to represent the current fee load. - txArray["Fee"] = Json::UInt (scaleFeeLoad(fees.units, - context.app.getFeeTrack(), fees, false)); + txArray["Fee"] = scaleFeeLoad(fees.units, + context.app.getFeeTrack(), fees, false).json(); } // { diff --git a/src/ripple/rpc/impl/TransactionSign.cpp b/src/ripple/rpc/impl/TransactionSign.cpp index a9ba0bc674b..24e408f8bb6 100644 --- a/src/ripple/rpc/impl/TransactionSign.cpp +++ b/src/ripple/rpc/impl/TransactionSign.cpp @@ -711,22 +711,19 @@ Json::Value checkFee ( } // Default fee in fee units. - std::uint64_t const feeDefault = config.TRANSACTION_FEE_BASE; + FeeUnit32 const feeDefault = config.TRANSACTION_FEE_BASE; // Administrative and identified endpoints are exempt from local fees. - std::uint64_t const loadFee = + XRPAmount const loadFee = scaleFeeLoad (feeDefault, feeTrack, ledger->fees(), isUnlimited (role)); - std::uint64_t fee = loadFee; + XRPAmount fee = loadFee; { auto const metrics = txQ.getMetrics(*ledger); auto const baseFee = ledger->fees().base; - auto escalatedFee = mulDiv( - metrics.openLedgerFeeLevel, baseFee, - metrics.referenceFeeLevel).second; - if (mulDiv(escalatedFee, metrics.referenceFeeLevel, - baseFee).second < metrics.openLedgerFeeLevel) - ++escalatedFee; + auto escalatedFee = toDrops( + metrics.openLedgerFeeLevel - FeeLevel64{ 1 }, baseFee).second + + 1; fee = std::max(fee, escalatedFee); } @@ -751,7 +748,7 @@ Json::Value checkFee ( return RPC::make_error (rpcHIGH_FEE, ss.str()); } - tx [jss::Fee] = static_cast(fee); + tx [jss::Fee] = fee.json(); return Json::Value(); } diff --git a/src/test/app/Check_test.cpp b/src/test/app/Check_test.cpp index d96fcef8ce8..3ab379b1de5 100644 --- a/src/test/app/Check_test.cpp +++ b/src/test/app/Check_test.cpp @@ -374,7 +374,7 @@ class Check_test : public beast::unit_test::suite BEAST_EXPECT (checksOnAccount (env, bob ).size() == bobCount + 6); // alice uses multisigning to create a check. - std::uint64_t const baseFeeDrops {env.current()->fees().base}; + XRPAmount const baseFeeDrops {env.current()->fees().base}; env (check::create (alice, bob, USD(50)), msig (bogie, demon), fee (3 * baseFeeDrops)); env.close(); @@ -580,7 +580,7 @@ class Check_test : public beast::unit_test::suite fix1449Time() + 100 * env.closed()->info().closeTimeResolution; env.close (closeTime); - std::uint64_t const baseFeeDrops {env.current()->fees().base}; + XRPAmount const baseFeeDrops {env.current()->fees().base}; STAmount const startBalance {XRP(300).value()}; env.fund (startBalance, alice, bob); { @@ -670,7 +670,7 @@ class Check_test : public beast::unit_test::suite verifyDeliveredAmount (env, drops(checkAmount.mantissa() - 1)); env.require (balance (alice, reserve)); env.require (balance (bob, - startBalance + checkAmount - drops ((baseFeeDrops * 2) + 1))); + startBalance + checkAmount - drops (baseFeeDrops * 2 + 1))); BEAST_EXPECT (checksOnAccount (env, alice).size() == 0); BEAST_EXPECT (checksOnAccount (env, bob ).size() == 0); BEAST_EXPECT (ownerCount (env, alice) == 0); @@ -680,7 +680,7 @@ class Check_test : public beast::unit_test::suite env (pay (env.master, alice, checkAmount + drops (baseFeeDrops - 1))); env (pay (bob, env.master, - checkAmount - drops ((baseFeeDrops * 3) + 1))); + checkAmount - drops (baseFeeDrops * 3 + 1))); env.close(); env.require (balance (alice, startBalance)); env.require (balance (bob, startBalance)); @@ -1016,7 +1016,7 @@ class Check_test : public beast::unit_test::suite BEAST_EXPECT (ownerCount (env, bob ) == signersCount + 1); // bob uses multisigning to cash a check. - std::uint64_t const baseFeeDrops {env.current()->fees().base}; + XRPAmount const baseFeeDrops {env.current()->fees().base}; env (check::cash (bob, chkId2, (USD(2))), msig (bogie, demon), fee (3 * baseFeeDrops)); env.close(); @@ -1746,7 +1746,7 @@ class Check_test : public beast::unit_test::suite BEAST_EXPECT (ownerCount (env, alice) == signersCount + 3); // alice uses multisigning to cancel a check. - std::uint64_t const baseFeeDrops {env.current()->fees().base}; + XRPAmount const baseFeeDrops {env.current()->fees().base}; env (check::cancel (alice, chkIdMSig), msig (bogie, demon), fee (3 * baseFeeDrops)); env.close(); diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index bcd53394710..369de5d1c2d 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -52,7 +52,7 @@ xrpMinusFee (jtx::Env const& env, std::int64_t xrpAmount) using namespace jtx; auto feeDrops = env.current ()->fees ().base; return drops ( - dropsPerXRP::value * xrpAmount - feeDrops); + dropsPerXRP * xrpAmount - feeDrops); }; struct Flow_test : public beast::unit_test::suite @@ -1211,8 +1211,8 @@ struct Flow_test : public beast::unit_test::suite auto const CTB = gw["CTB"]; auto const fee = env.current ()->fees ().base; - env.fund (reserve(env, 2) + drops (9999640) + (fee), ann); - env.fund (reserve(env, 2) + (fee*4), gw); + env.fund (reserve(env, 2) + drops (9999640) + fee, ann); + env.fund (reserve(env, 2) + fee*4, gw); env.close(); env (rate(gw, 1.002)); diff --git a/src/test/app/LoadFeeTrack_test.cpp b/src/test/app/LoadFeeTrack_test.cpp index 973ed15051c..0d8495c2fcf 100644 --- a/src/test/app/LoadFeeTrack_test.cpp +++ b/src/test/app/LoadFeeTrack_test.cpp @@ -31,18 +31,58 @@ class LoadFeeTrack_test : public beast::unit_test::suite { Config d; // get a default configuration object LoadFeeTrack l; - Fees const fees = [&]() { - Fees f; - f.base = d.FEE_DEFAULT; - f.units = d.TRANSACTION_FEE_BASE; - f.reserve = 200 * SYSTEM_CURRENCY_PARTS; - f.increment = 50 * SYSTEM_CURRENCY_PARTS; - return f; - }(); - - BEAST_EXPECT (scaleFeeLoad (10000, l, fees, false) == 10000); - BEAST_EXPECT (scaleFeeLoad (1, l, fees, false) == 1); + Fees const fees = [&]() { + Fees f; + f.base = d.FEE_DEFAULT; + f.units = d.TRANSACTION_FEE_BASE; + f.reserve = 200 * DROPS_PER_XRP; + f.increment = 50 * DROPS_PER_XRP; + return f; + }(); + + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 0 }, l, fees, false) == + XRPAmount{ 0 }); + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 10000 }, l, fees, false) == + XRPAmount{ 10000 }); + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 1 }, l, fees, false) == + XRPAmount{ 1 }); + } + { + Fees const fees = [&]() { + Fees f; + f.base = d.FEE_DEFAULT * 10; + f.units = d.TRANSACTION_FEE_BASE; + f.reserve = 200 * DROPS_PER_XRP; + f.increment = 50 * DROPS_PER_XRP; + return f; + }(); + + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 0 }, l, fees, false) == + XRPAmount{ 0 }); + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 10000 }, l, fees, false) == + XRPAmount{ 100000 }); + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 1 }, l, fees, false) == + XRPAmount{ 10 }); + } + { + Fees const fees = [&]() + { + Fees f; + f.base = d.FEE_DEFAULT; + f.units = d.TRANSACTION_FEE_BASE * 10; + f.reserve = 200 * DROPS_PER_XRP; + f.increment = 50 * DROPS_PER_XRP; + return f; + }(); + + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 0 }, l, fees, false) == + XRPAmount{ 0 }); + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 10000 }, l, fees, false) == + XRPAmount{ 1000 }); + BEAST_EXPECT(scaleFeeLoad(FeeUnit64{ 1 }, l, fees, false) == + XRPAmount{ 0 }); + } } }; diff --git a/src/test/app/MultiSign_test.cpp b/src/test/app/MultiSign_test.cpp index d5b8fdb81ee..b3aa634ef59 100644 --- a/src/test/app/MultiSign_test.cpp +++ b/src/test/app/MultiSign_test.cpp @@ -64,7 +64,7 @@ class MultiSign_test : public beast::unit_test::suite env.require (owners (alice, 0)); // Fund alice enough to set the signer list, then attach signers. - env(pay(env.master, alice, fee + drops (1))); + env(pay(env.master, alice, fee + drops(1))); env.close(); env(smallSigners); env.close(); @@ -281,7 +281,8 @@ class MultiSign_test : public beast::unit_test::suite // This should fail because the fee is too small. aliceSeq = env.seq (alice); env(noop(alice), - msig(bogie), fee((2 * baseFee) - 1), ter(telINSUF_FEE_P)); + msig(bogie), fee((2 * baseFee) - 1), + ter(telINSUF_FEE_P)); env.close(); BEAST_EXPECT(env.seq(alice) == aliceSeq); @@ -482,7 +483,7 @@ class MultiSign_test : public beast::unit_test::suite Json::Value jv; jv[jss::tx_json][jss::Account] = alice.human(); jv[jss::tx_json][jss::TransactionType] = jss::AccountSet; - jv[jss::tx_json][jss::Fee] = static_cast(8 * baseFee); + jv[jss::tx_json][jss::Fee] = (8 * baseFee).json(); jv[jss::tx_json][jss::Sequence] = env.seq(alice); jv[jss::tx_json][jss::SigningPubKey] = ""; return jv; @@ -1211,14 +1212,14 @@ class MultiSign_test : public beast::unit_test::suite // Use sign_for to sign a transaction where alice pays 10 XRP to // masterpassphrase. - std::uint32_t baseFee = env.current()->fees().base; + auto const baseFee = env.current()->fees().base; Json::Value jvSig1; jvSig1[jss::account] = bogie.human(); jvSig1[jss::secret] = bogie.name(); jvSig1[jss::tx_json][jss::Account] = alice.human(); jvSig1[jss::tx_json][jss::Amount] = 10000000; jvSig1[jss::tx_json][jss::Destination] = env.master.human(); - jvSig1[jss::tx_json][jss::Fee] = 3 * baseFee; + jvSig1[jss::tx_json][jss::Fee] = (3 * baseFee).json(); jvSig1[jss::tx_json][jss::Sequence] = env.seq(alice); jvSig1[jss::tx_json][jss::TransactionType] = jss::Payment; diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index b494b90f738..2d1efbda964 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -45,7 +45,7 @@ class Offer_test : public beast::unit_test::suite { using namespace jtx; auto feeDrops = env.current()->fees().base; - return drops (dropsPerXRP::value * xrpAmount - feeDrops); + return drops (dropsPerXRP * xrpAmount - feeDrops); } static auto @@ -1046,7 +1046,7 @@ class Offer_test : public beast::unit_test::suite env.fund (XRP(1000000), gw); // The fee that's charged for transactions - auto const f = env.current ()->fees ().base; + auto const f = env.current()->fees().base; // Account is at the reserve, and will dip below once // fees are subtracted. @@ -1342,10 +1342,10 @@ class Offer_test : public beast::unit_test::suite jrr = ledgerEntryRoot (env, bob); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - std::to_string( - XRP (10000).value ().mantissa () - - XRP (reverse_order ? 4000 : 3000).value ().mantissa () - - env.current ()->fees ().base * 2) + to_string( + (XRP (10000) - + XRP (reverse_order ? 4000 : 3000) - + env.current ()->fees ().base * 2).xrp()) ); jrr = ledgerEntryState (env, alice, gw, "USD"); @@ -1353,10 +1353,10 @@ class Offer_test : public beast::unit_test::suite jrr = ledgerEntryRoot (env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - std::to_string( - XRP (10000).value ().mantissa( ) + - XRP(reverse_order ? 4000 : 3000).value ().mantissa () - - env.current ()->fees ().base * 2) + to_string( + (XRP (10000) + + XRP(reverse_order ? 4000 : 3000) - + env.current ()->fees ().base * 2).xrp ()) ); } @@ -1392,10 +1392,10 @@ class Offer_test : public beast::unit_test::suite jrr = ledgerEntryRoot(env, bob); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - std::to_string( - XRP (100000).value ().mantissa () - - XRP (3000).value ().mantissa () - - env.current ()->fees ().base * 1) + to_string( + (XRP (100000) - + XRP (3000) - + env.current ()->fees ().base * 1).xrp ()) ); jrr = ledgerEntryState (env, alice, gw, "USD"); @@ -1403,10 +1403,10 @@ class Offer_test : public beast::unit_test::suite jrr = ledgerEntryRoot (env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - std::to_string( - XRP (100000).value ().mantissa () + - XRP (3000).value ().mantissa () - - env.current ()->fees ().base * 2) + to_string( + (XRP (100000) + + XRP (3000) - + env.current ()->fees ().base * 2).xrp ()) ); } @@ -1526,10 +1526,10 @@ class Offer_test : public beast::unit_test::suite jrr = ledgerEntryRoot (env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - std::to_string( - XRP (10000).value ().mantissa () + - XRP (500).value ().mantissa () - - env.current ()->fees ().base * 2) + to_string( + (XRP (10000) + + XRP (500) - + env.current ()->fees ().base * 2).xrp ()) ); jrr = ledgerEntryState (env, bob, gw, "USD"); @@ -1622,10 +1622,10 @@ class Offer_test : public beast::unit_test::suite jrr = ledgerEntryRoot (env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - std::to_string( - XRP (10000).value ().mantissa () + - XRP (200).value ().mantissa () - - env.current ()->fees ().base * 2) + to_string( + (XRP (10000) + + XRP (200) - + env.current ()->fees ().base * 2).xrp ()) ); // bob got 40 USD from partial consumption of the offer @@ -1656,11 +1656,11 @@ class Offer_test : public beast::unit_test::suite jrr = ledgerEntryRoot (env, alice); BEAST_EXPECT( jrr[jss::node][sfBalance.fieldName] == - std::to_string( - XRP (10000).value ().mantissa () + - XRP (200).value ().mantissa () + - XRP (300).value ().mantissa () - - env.current ()->fees ().base * 4) + to_string( + (XRP (10000) + + XRP (200) + + XRP (300) - + env.current ()->fees ().base * 4).xrp ()) ); // bob now has 100 USD - 40 from the first payment and 60 from the @@ -2154,7 +2154,7 @@ class Offer_test : public beast::unit_test::suite env.current ()->read ( keylet::account (bob.id ()))->getFieldU32 (sfSequence); payment[jss::tx_json][jss::Fee] = - std::to_string( env.current ()->fees ().base); + to_string( env.current()->fees().base); payment[jss::tx_json][jss::SendMax] = bob ["XTS"] (1.5).value ().getJson (JsonOptions::none); auto jrr = wsc->invoke("submit", payment); @@ -2397,7 +2397,7 @@ class Offer_test : public beast::unit_test::suite // alice's account has enough for the reserve, one trust line plus two // offers, and two fees. - env.fund (reserve (env, 2) + (fee * 2), alice); + env.fund (reserve (env, 2) + fee * 2, alice); env.close(); env (trust(alice, usdOffer)); @@ -2482,8 +2482,8 @@ class Offer_test : public beast::unit_test::suite // Each account has enough for the reserve, two trust lines, one // offer, and two fees. - env.fund (reserve (env, 3) + (fee * 3), alice); - env.fund (reserve (env, 3) + (fee * 2), bob); + env.fund (reserve (env, 3) + fee * 3, alice); + env.fund (reserve (env, 3) + fee * 2, bob); env.close(); env (trust(alice, usdOffer)); env (trust(bob, eurOffer)); @@ -3004,7 +3004,7 @@ class Offer_test : public beast::unit_test::suite auto const eve = Account("eve"); auto const fyn = Account("fyn"); - env.fund (XRP(20000) + fee*2, eve, fyn); + env.fund (XRP(20000) + (fee*2), eve, fyn); env.close(); env (trust (eve, USD(1000))); @@ -3672,8 +3672,7 @@ class Offer_test : public beast::unit_test::suite auto const fee = env.current ()->fees ().base; env.fund (reserve(env, 2) + drops (400000000000) + (fee), alice, bob); - env.fund (reserve(env, 2) + (fee*4), gw); - env.close(); + env.fund (reserve(env, 2) + drops(fee*4), gw); env.close(); env (trust(bob, CNY(500))); env.close(); diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index bcd7b755ea1..978dd2de85a 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -55,7 +55,7 @@ struct Regression_test : public beast::unit_test::suite auto closed = std::make_shared( create_genesis, env.app().config(), std::vector{}, env.app().family()); - auto expectedDrops = SYSTEM_CURRENCY_START; + auto expectedDrops = INITIAL_XRP; BEAST_EXPECT(closed->info().drops == expectedDrops); auto const aliceXRP = 400; @@ -110,7 +110,7 @@ struct Regression_test : public beast::unit_test::suite BEAST_EXPECT(balance == XRP(0)); } - expectedDrops -= aliceXRP * dropsPerXRP::value; + expectedDrops -= aliceXRP * dropsPerXRP; BEAST_EXPECT(next->info().drops == expectedDrops); } diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index 6abb5a1c1c4..8900eca42af 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -50,16 +50,18 @@ class TxQ_test : public beast::unit_test::suite std::uint64_t expectedMinFeeLevel, std::uint64_t expectedMedFeeLevel = 256 * 500) { + FeeLevel64 const expectedMin{ expectedMinFeeLevel }; + FeeLevel64 const expectedMed{ expectedMedFeeLevel }; auto const metrics = env.app().getTxQ().getMetrics(*env.current()); - BEAST_EXPECT(metrics.referenceFeeLevel == 256); + BEAST_EXPECT(metrics.referenceFeeLevel == FeeLevel64{ 256 }); BEAST_EXPECT(metrics.txCount == expectedCount); BEAST_EXPECT(metrics.txQMaxSize == expectedMaxCount); BEAST_EXPECT(metrics.txInLedger == expectedInLedger); BEAST_EXPECT(metrics.txPerLedger == expectedPerLedger); - BEAST_EXPECT(metrics.minProcessingFeeLevel == expectedMinFeeLevel); - BEAST_EXPECT(metrics.medFeeLevel == expectedMedFeeLevel); + BEAST_EXPECT(metrics.minProcessingFeeLevel == expectedMin); + BEAST_EXPECT(metrics.medFeeLevel == expectedMed); auto expectedCurFeeLevel = expectedInLedger > expectedPerLedger ? - expectedMedFeeLevel * expectedInLedger * expectedInLedger / + expectedMed *expectedInLedger * expectedInLedger / (expectedPerLedger * expectedPerLedger) : metrics.referenceFeeLevel; BEAST_EXPECT(metrics.openLedgerFeeLevel == expectedCurFeeLevel); @@ -84,8 +86,8 @@ class TxQ_test : public beast::unit_test::suite auto metrics = env.app().getTxQ().getMetrics(view); // Don't care about the overflow flag - return fee(mulDiv(metrics.openLedgerFeeLevel, - view.fees().base, metrics.referenceFeeLevel).second + 1); + return fee(toDrops(metrics.openLedgerFeeLevel, + view.fees().base).second + 1); } static @@ -156,10 +158,10 @@ class TxQ_test : public beast::unit_test::suite env.close(env.now() + 5s, 10000ms); checkMetrics(env, 0, flagMaxQueue, 0, expectedPerLedger, 256); auto const fees = env.current()->fees(); - BEAST_EXPECT(fees.base == base); - BEAST_EXPECT(fees.units == units); - BEAST_EXPECT(fees.reserve == reserve); - BEAST_EXPECT(fees.increment == increment); + BEAST_EXPECT(fees.base == XRPAmount{ base }); + BEAST_EXPECT(fees.units == FeeUnit64{ units }); + BEAST_EXPECT(fees.reserve == XRPAmount{ reserve }); + BEAST_EXPECT(fees.increment == XRPAmount{ increment }); return flagMaxQueue; } @@ -494,14 +496,16 @@ class TxQ_test : public beast::unit_test::suite auto& txQ = env.app().getTxQ(); auto aliceStat = txQ.getAccountTxs(alice.id(), *env.current()); BEAST_EXPECT(aliceStat.size() == 1); - BEAST_EXPECT(aliceStat.begin()->second.feeLevel == 256); + BEAST_EXPECT(aliceStat.begin()->second.feeLevel == + FeeLevel64{ 256 }); BEAST_EXPECT(aliceStat.begin()->second.lastValid && *aliceStat.begin()->second.lastValid == 8); BEAST_EXPECT(!aliceStat.begin()->second.consequences); auto bobStat = txQ.getAccountTxs(bob.id(), *env.current()); BEAST_EXPECT(bobStat.size() == 1); - BEAST_EXPECT(bobStat.begin()->second.feeLevel == 7000 * 256 / 10); + BEAST_EXPECT(bobStat.begin()->second.feeLevel == + FeeLevel64{ 7000 * 256 / 10 }); BEAST_EXPECT(!bobStat.begin()->second.lastValid); BEAST_EXPECT(!bobStat.begin()->second.consequences); @@ -833,13 +837,14 @@ class TxQ_test : public beast::unit_test::suite { auto& txQ = env.app().getTxQ(); auto aliceStat = txQ.getAccountTxs(alice.id(), *env.current()); - std::int64_t fee = 20; + constexpr XRPAmount fee{ 20 }; + auto const& baseFee = env.current()->fees().base; auto seq = env.seq(alice); BEAST_EXPECT(aliceStat.size() == 7); for (auto const& tx : aliceStat) { BEAST_EXPECT(tx.first == seq); - BEAST_EXPECT(tx.second.feeLevel == mulDiv(fee, 256, 10).second); + BEAST_EXPECT(tx.second.feeLevel == toFeeLevel(fee, baseFee).second); BEAST_EXPECT(tx.second.lastValid); BEAST_EXPECT((tx.second.consequences && tx.second.consequences->fee == drops(fee) && @@ -1995,7 +2000,7 @@ class TxQ_test : public beast::unit_test::suite for (auto const& tx : aliceStat) { BEAST_EXPECT(tx.first == seq); - BEAST_EXPECT(tx.second.feeLevel == 25600); + BEAST_EXPECT(tx.second.feeLevel == FeeLevel64{ 25600 }); if(seq == aliceSeq + 2) { BEAST_EXPECT(tx.second.lastValid && @@ -2049,7 +2054,7 @@ class TxQ_test : public beast::unit_test::suite ++seq; BEAST_EXPECT(tx.first == seq); - BEAST_EXPECT(tx.second.feeLevel == 25600); + BEAST_EXPECT(tx.second.feeLevel == FeeLevel64{ 25600 }); BEAST_EXPECT(!tx.second.lastValid); ++seq; } @@ -2064,7 +2069,7 @@ class TxQ_test : public beast::unit_test::suite for (auto const& tx : aliceStat) { BEAST_EXPECT(tx.first == seq); - BEAST_EXPECT(tx.second.feeLevel == 25600); + BEAST_EXPECT(tx.second.feeLevel == FeeLevel64{ 25600 }); BEAST_EXPECT(!tx.second.lastValid); ++seq; } @@ -2763,10 +2768,10 @@ class TxQ_test : public beast::unit_test::suite totalFactor += inLedger * inLedger; } auto result = - mulDiv (metrics.medFeeLevel * totalFactor / + toDrops (metrics.medFeeLevel * totalFactor / (metrics.txPerLedger * metrics.txPerLedger), - env.current ()->fees ().base, metrics.referenceFeeLevel) - .second; + env.current ()->fees ().base) + .second.drops(); // Subtract the fees already paid result -= alreadyPaid; // round up @@ -2807,7 +2812,8 @@ class TxQ_test : public beast::unit_test::suite // Now repeat the process including the new tx // and avoiding the rounding error - std::uint64_t const totalFee2 = calcTotalFee (100 * 2 + 8889 + 60911); + std::uint64_t const totalFee2 = calcTotalFee (100 * 2 + 8889 + + 60911); BEAST_EXPECT(totalFee2 == 35556); // Submit a transaction with that fee. It will succeed. env(noop(alice), fee(totalFee2), seq(aliceSeq++)); @@ -2877,7 +2883,7 @@ class TxQ_test : public beast::unit_test::suite for (auto const& tx : aliceQueue) { BEAST_EXPECT(tx.first == seq); - BEAST_EXPECT(tx.second.feeLevel == 2560); + BEAST_EXPECT(tx.second.feeLevel == FeeLevel64{ 2560 }); ++seq; } diff --git a/src/test/basics/feeunits_test.cpp b/src/test/basics/feeunits_test.cpp new file mode 100644 index 00000000000..e058ccd63a8 --- /dev/null +++ b/src/test/basics/feeunits_test.cpp @@ -0,0 +1,110 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2019 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include + +namespace ripple { +namespace test { + +class feeunits_test + : public beast::unit_test::suite +{ +private: + +public: + void run() override + { + BEAST_EXPECT(INITIAL_XRP.drops() == 100'000'000'000'000'000); + BEAST_EXPECT(INITIAL_XRP == + XRPAmount{ 100'000'000'000'000'000 }); + { + XRPAmount x{ 100 }; + BEAST_EXPECT(x.drops() == 100); + BEAST_EXPECT((std::is_same_v)); + auto y = 4u * x; + BEAST_EXPECT(y.value() == 400); + BEAST_EXPECT((std::is_same_v)); + + auto z = 4 * y; + BEAST_EXPECT(z.value() == 1600); + BEAST_EXPECT((std::is_same_v)); + + FeeUnit32 f{ 10 }; + FeeUnit32 baseFee{ 100 }; + + auto drops = mulDiv(baseFee, x, f).second; + + BEAST_EXPECT(drops.value() == 1000); + BEAST_EXPECT((std::is_same_v)); + BEAST_EXPECT((std::is_same_v)); + } + { + XRPAmount x{ 100 }; + BEAST_EXPECT(x.value() == 100); + BEAST_EXPECT((std::is_same_v)); + auto y = 4u * x; + BEAST_EXPECT(y.value() == 400); + BEAST_EXPECT((std::is_same_v)); + + FeeUnit64 f{ 10 }; + FeeUnit64 baseFee{ 100 }; + + auto drops = mulDiv(baseFee, x, f).second; + + BEAST_EXPECT(drops.value() == 1000); + BEAST_EXPECT((std::is_same_v)); + BEAST_EXPECT((std::is_same_v)); + } + { + FeeLevel64 x{ 1024 }; + BEAST_EXPECT(x.value() == 1024); + BEAST_EXPECT((std::is_same_v)); + std::uint64_t m = 4; + auto y = m * x; + BEAST_EXPECT(y.value() == 4096); + BEAST_EXPECT((std::is_same_v)); + + XRPAmount basefee{ 10 }; + FeeLevel64 referencefee{ 256 }; + + auto drops = mulDiv(x, basefee, referencefee).second; + + BEAST_EXPECT(drops.value() == 40); + BEAST_EXPECT((std::is_same_v)); + BEAST_EXPECT((std::is_same_v)); + } + } +}; + +BEAST_DEFINE_TESTSUITE(feeunits,ripple_basics,ripple); + +} // test +} //ripple diff --git a/src/test/jtx/Env_test.cpp b/src/test/jtx/Env_test.cpp index edde0c4dc07..ba9a24f8f92 100644 --- a/src/test/jtx/Env_test.cpp +++ b/src/test/jtx/Env_test.cpp @@ -79,13 +79,13 @@ class Env_test : public beast::unit_test::suite PrettyAmount(0u); PrettyAmount(1u); PrettyAmount(-1); - static_assert(! std::is_constructible< + static_assert(! std::is_trivially_constructible< PrettyAmount, char>::value, ""); - static_assert(! std::is_constructible< + static_assert(! std::is_trivially_constructible< PrettyAmount, unsigned char>::value, ""); - static_assert(! std::is_constructible< + static_assert(! std::is_trivially_constructible< PrettyAmount, short>::value, ""); - static_assert(! std::is_constructible< + static_assert(! std::is_trivially_constructible< PrettyAmount, unsigned short>::value, ""); try @@ -367,7 +367,7 @@ class Env_test : public beast::unit_test::suite env(noop("alice"), msig("carol"), fee(2 * baseFee)); env(noop("alice"), msig("bob", "carol"), fee(3 * baseFee)); env(noop("alice"), msig("bob", "carol", "dilbert"), - fee(4 * baseFee), ter(tefBAD_SIGNATURE)); + fee(4 * baseFee), ter(tefBAD_SIGNATURE)); env(signers("alice", none)); } diff --git a/src/test/jtx/amount.h b/src/test/jtx/amount.h index 1e2d99d90fb..3f73a2230c3 100644 --- a/src/test/jtx/amount.h +++ b/src/test/jtx/amount.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -60,11 +61,10 @@ struct None //------------------------------------------------------------------------------ -template -struct dropsPerXRP -{ - static T const value = 1000000; -}; +// This value is also defined in SystemParameters.h. It's +// duplicated here to catch any possible future errors that +// could change that value (however unlikely). +constexpr XRPAmount dropsPerXRP = { 1'000'000 }; /** Represents an XRP or IOU quantity This customizes the string conversion and supports @@ -93,8 +93,8 @@ struct PrettyAmount template PrettyAmount (T v, std::enable_if_t< sizeof(T) >= sizeof(int) && - std::is_integral::value && - std::is_signed::value>* = nullptr) + std::is_integral_v && + std::is_signed_v>* = nullptr) : amount_((v > 0) ? v : -v, v < 0) { @@ -104,8 +104,13 @@ struct PrettyAmount template PrettyAmount (T v, std::enable_if_t< sizeof(T) >= sizeof(int) && - std::is_integral::value && - std::is_unsigned::value>* = nullptr) + std::is_unsigned_v>* = nullptr) + : amount_(v) + { + } + + /** drops */ + PrettyAmount (XRPAmount v) : amount_(v) { } @@ -172,27 +177,28 @@ struct XRP_t return xrpIssue(); } - /** Returns an amount of XRP as STAmount + /** Returns an amount of XRP as PrettyAmount, + which is trivially convertable to STAmount @param v The number of XRP (not drops) */ /** @{ */ template ::value>> + std::is_integral_v>> PrettyAmount operator()(T v) const { - return { std::conditional_t< - std::is_signed::value, - std::int64_t, std::uint64_t>{v} * - dropsPerXRP::value }; + using TOut = std::conditional_t< + std::is_signed_v, + std::int64_t, std::uint64_t>; + return { TOut{v} * dropsPerXRP }; } PrettyAmount operator()(double v) const { auto const c = - dropsPerXRP::value; + dropsPerXRP.drops(); if (v >= 0) { auto const d = std::uint64_t( @@ -235,20 +241,32 @@ struct XRP_t */ extern XRP_t const XRP; -/** Returns an XRP STAmount. +/** Returns an XRP PrettyAmount, which is trivially convertible to STAmount. Example: - drops(10) Returns STAmount of 10 drops + drops(10) Returns PrettyAmount of 10 drops */ template ::value>> + std::is_integral_v>> PrettyAmount drops (Integer i) { return { i }; } +/** Returns an XRP PrettyAmount, which is trivially convertible to STAmount. + +Example: +drops(view->fee().basefee) Returns PrettyAmount of 10 drops +*/ +inline +PrettyAmount +drops (XRPAmount i) +{ + return { i }; +} + //------------------------------------------------------------------------------ namespace detail { diff --git a/src/test/jtx/impl/amount.cpp b/src/test/jtx/impl/amount.cpp index e1c4ecbcfee..bd612d86787 100644 --- a/src/test/jtx/impl/amount.cpp +++ b/src/test/jtx/impl/amount.cpp @@ -78,7 +78,7 @@ operator<< (std::ostream& os, { // measure in hundredths auto const c = - dropsPerXRP::value / 100; + dropsPerXRP / 100; auto const n = amount.value().mantissa(); if(n < c) { @@ -89,7 +89,7 @@ operator<< (std::ostream& os, return os; } auto const d = double(n) / - dropsPerXRP::value; + dropsPerXRP.drops(); if (amount.value().negative()) os << "-"; diff --git a/src/test/jtx/impl/utility.cpp b/src/test/jtx/impl/utility.cpp index 2cc24116d50..7d70a2a220c 100644 --- a/src/test/jtx/impl/utility.cpp +++ b/src/test/jtx/impl/utility.cpp @@ -62,7 +62,7 @@ fill_fee (Json::Value& jv, { if (jv.isMember(jss::Fee)) return; - jv[jss::Fee] = std::to_string( + jv[jss::Fee] = to_string( view.fees().base); } diff --git a/src/test/ledger/Invariants_test.cpp b/src/test/ledger/Invariants_test.cpp index 9f16ec9f5c4..f5ffd587694 100644 --- a/src/test/ledger/Invariants_test.cpp +++ b/src/test/ledger/Invariants_test.cpp @@ -94,7 +94,7 @@ class Invariants_test : public beast::unit_test::suite ov, tx, tesSUCCESS, - env.current()->fees().base, + env.current()->fees().units, tapNONE, jlog }; @@ -272,7 +272,10 @@ class Invariants_test : public beast::unit_test::suite auto const sle = ac.view().peek (keylet::account(A1.id())); if(! sle) return false; - sle->setFieldAmount (sfBalance, SYSTEM_CURRENCY_START + 1); + // Use `drops(1)` to bypass a call to STAmount::canonicalize + // with an invalid value + sle->setFieldAmount (sfBalance, + INITIAL_XRP + drops(1)); ac.view().update (sle); return true; }); @@ -308,11 +311,11 @@ class Invariants_test : public beast::unit_test::suite doInvariantCheck (enabled, {{ "fee paid exceeds system limit: "s + - std::to_string(SYSTEM_CURRENCY_START) }, + to_string(INITIAL_XRP) }, { "XRP net change of 0 doesn't match fee "s + - std::to_string(SYSTEM_CURRENCY_START) }}, + to_string(INITIAL_XRP) }}, [](Account const&, Account const&, ApplyContext&) { return true; }, - XRPAmount{SYSTEM_CURRENCY_START}); + XRPAmount{INITIAL_XRP}); doInvariantCheck (enabled, {{ "fee paid is 20 exceeds fee specified in transaction." }, @@ -437,7 +440,10 @@ class Invariants_test : public beast::unit_test::suite return false; auto sleNew = std::make_shared ( keylet::escrow(A1, (*sle)[sfSequence] + 2)); - sleNew->setFieldAmount (sfAmount, SYSTEM_CURRENCY_START + 1); + // Use `drops(1)` to bypass a call to STAmount::canonicalize + // with an invalid value + sleNew->setFieldAmount (sfAmount, + INITIAL_XRP + drops(1)); ac.view().insert (sleNew); return true; }); diff --git a/src/test/protocol/XRPAmount_test.cpp b/src/test/protocol/XRPAmount_test.cpp index c4aac63c306..e11e95f32b2 100644 --- a/src/test/protocol/XRPAmount_test.cpp +++ b/src/test/protocol/XRPAmount_test.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include +#include #include namespace ripple { diff --git a/src/test/rpc/AmendmentBlocked_test.cpp b/src/test/rpc/AmendmentBlocked_test.cpp index a9067c8debb..6d720d077e1 100644 --- a/src/test/rpc/AmendmentBlocked_test.cpp +++ b/src/test/rpc/AmendmentBlocked_test.cpp @@ -91,7 +91,7 @@ class AmendmentBlocked_test : public beast::unit_test::suite set_tx[jss::Account] = bob.human(); set_tx[jss::TransactionType] = jss::AccountSet; set_tx[jss::Fee] = - static_cast(8 * env.current()->fees().base); + (8 * env.current()->fees().base).json(); set_tx[jss::Sequence] = env.seq(bob); set_tx[jss::SigningPubKey] = ""; diff --git a/src/test/rpc/NoRippleCheck_test.cpp b/src/test/rpc/NoRippleCheck_test.cpp index a128cf10229..6fc9fbd47ee 100644 --- a/src/test/rpc/NoRippleCheck_test.cpp +++ b/src/test/rpc/NoRippleCheck_test.cpp @@ -288,16 +288,23 @@ class NoRippleCheckLimits_test : public beast::unit_test::suite auto& txq = env.app().getTxQ(); auto const gw = Account {"gw" + std::to_string(i)}; env.memoize(gw); + auto const baseFee = env.current()->fees().base; env (pay (env.master, gw, XRP(1000)), seq (autofill), - fee (txq.getMetrics(*env.current()).openLedgerFeeLevel + 1), + fee (toDrops( + txq.getMetrics(*env.current()).openLedgerFeeLevel, + baseFee).second + 1), sig (autofill)); env (fset (gw, asfDefaultRipple), seq (autofill), - fee (txq.getMetrics(*env.current()).openLedgerFeeLevel + 1), + fee (toDrops( + txq.getMetrics(*env.current()).openLedgerFeeLevel, + baseFee).second + 1), sig (autofill)); env (trust (alice, gw["USD"](10)), - fee (txq.getMetrics(*env.current()).openLedgerFeeLevel + 1)); + fee (toDrops( + txq.getMetrics(*env.current()).openLedgerFeeLevel, + baseFee).second + 1)); env.close(); } diff --git a/src/test/unity/basics_test_unity.cpp b/src/test/unity/basics_test_unity.cpp index ab9ab552bc9..623e1562ef3 100644 --- a/src/test/unity/basics_test_unity.cpp +++ b/src/test/unity/basics_test_unity.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include