diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj index 97deae1369..34a50db41f 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj +++ b/Builds/VisualStudio2015/RippleD.vcxproj @@ -1001,6 +1001,12 @@ True True + + True + True + + + True True diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters index c215923e99..8c90529468 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters @@ -1482,6 +1482,12 @@ ripple\app\paths\cursor + + ripple\app\paths\cursor + + + ripple\app\paths\cursor + ripple\app\paths\cursor diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index e32edc923e..55822e1e91 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -2851,7 +2852,7 @@ void NetworkOPsImp::getBookPage ( unsigned int uBookEntry; STAmount saDirRate; - auto uTransferRate = rippleTransferRate(view, book.out.account); + auto const rate = transferRate(view, book.out.account); auto viewJ = app_.journal ("View"); unsigned int left (iLimit == 0 ? 300 : iLimit); @@ -2948,12 +2949,11 @@ void NetworkOPsImp::getBookPage ( Json::Value jvOffer = sleOffer->getJson (0); - STAmount saTakerGetsFunded; - STAmount saOwnerFundsLimit; - std::uint32_t uOfferRate; + STAmount saTakerGetsFunded; + STAmount saOwnerFundsLimit = saOwnerFunds; + Rate offerRate = parityRate; - - if (uTransferRate != QUALITY_ONE + if (rate != parityRate // Have a tranfer fee. && uTakerID != book.out.account // Not taking offers of own IOUs. @@ -2961,16 +2961,9 @@ void NetworkOPsImp::getBookPage ( // Offer owner not issuing ownfunds { // Need to charge a transfer fee to offer owner. - uOfferRate = uTransferRate; - saOwnerFundsLimit = divide ( - saOwnerFunds, - amountFromRate (uOfferRate), - saOwnerFunds.issue ()); - } - else - { - uOfferRate = QUALITY_ONE; - saOwnerFundsLimit = saOwnerFunds; + offerRate = rate; + saOwnerFundsLimit = divide ( + saOwnerFunds, offerRate); } if (saOwnerFundsLimit >= saTakerGets) @@ -2982,7 +2975,7 @@ void NetworkOPsImp::getBookPage ( { // Only provide, if not fully funded. - saTakerGetsFunded = saOwnerFundsLimit; + saTakerGetsFunded = saOwnerFundsLimit; saTakerGetsFunded.setJson (jvOffer[jss::taker_gets_funded]); std::min ( @@ -2991,14 +2984,11 @@ void NetworkOPsImp::getBookPage ( (jvOffer[jss::taker_pays_funded]); } - STAmount saOwnerPays = (QUALITY_ONE == uOfferRate) + STAmount saOwnerPays = (parityRate == offerRate) ? saTakerGetsFunded : std::min ( saOwnerFunds, - multiply ( - saTakerGetsFunded, - amountFromRate (uOfferRate), - saTakerGetsFunded.issue ())); + multiply (saTakerGetsFunded, offerRate)); umBalance[uOfferOwnerID] = saOwnerFunds - saOwnerPays; @@ -3055,7 +3045,7 @@ void NetworkOPsImp::getBookPage ( MetaView lesActive (lpLedger, tapNONE, true); OrderBookIterator obIterator (lesActive, book); - auto uTransferRate = rippleTransferRate (lesActive, book.out.account); + auto const rate = transferRate(lesActive, book.out.account); const bool bGlobalFreeze = lesActive.isGlobalFrozen (book.out.account) || lesActive.isGlobalFrozen (book.in.account); @@ -3115,12 +3105,11 @@ void NetworkOPsImp::getBookPage ( Json::Value jvOffer = sleOffer->getJson (0); - STAmount saTakerGetsFunded; - STAmount saOwnerFundsLimit; - std::uint32_t uOfferRate; + STAmount saTakerGetsFunded; + STAmount saOwnerFundsLimit = saOwnerFunds; + Rate offerRate = parityRate; - - if (uTransferRate != QUALITY_ONE + if (rate != parityRate // Have a tranfer fee. && uTakerID != book.out.account // Not taking offers of own IOUs. @@ -3128,14 +3117,8 @@ void NetworkOPsImp::getBookPage ( // Offer owner not issuing ownfunds { // Need to charge a transfer fee to offer owner. - uOfferRate = uTransferRate; - saOwnerFundsLimit = divide (saOwnerFunds, - amountFromRate (uOfferRate)); - } - else - { - uOfferRate = QUALITY_ONE; - saOwnerFundsLimit = saOwnerFunds; + offerRate = rate; + saOwnerFundsLimit = divide (saOwnerFunds, offerRate); } if (saOwnerFundsLimit >= saTakerGets) @@ -3157,11 +3140,11 @@ void NetworkOPsImp::getBookPage ( jvOffer[jss::taker_pays_funded]); } - STAmount saOwnerPays = (uOfferRate == QUALITY_ONE) + STAmount saOwnerPays = (parityRate == offerRate) ? saTakerGetsFunded : std::min ( saOwnerFunds, - multiply (saTakerGetsFunded, amountFromRate (uOfferRate))); + multiply (saTakerGetsFunded, offerRate)); umBalance[uOfferOwnerID] = saOwnerFunds - saOwnerPays; diff --git a/src/ripple/app/paths/Node.h b/src/ripple/app/paths/Node.h index efd32de808..2c56420f7a 100644 --- a/src/ripple/app/paths/Node.h +++ b/src/ripple/app/paths/Node.h @@ -22,7 +22,9 @@ #include #include +#include #include +#include namespace ripple { namespace path { @@ -42,12 +44,12 @@ struct Node std::uint16_t uFlags; // --> From path. - AccountID account_; // --> Accounts: Receiving/sending account. + AccountID account_; // --> Accounts: Receiving/sending account. Issue issue_; // --> Accounts: Receive and send, Offers: send. // --- For offer's next has currency out. - STAmount transferRate_; // Transfer rate for issuer. + boost::optional transferRate_; // Transfer rate for issuer. // Computed by Reverse. STAmount saRevRedeem; // <-- Amount to redeem to next. @@ -65,8 +67,7 @@ struct Node // fee. // For offers: - - STAmount saRateMax; + boost::optional rateMax; // The nodes are partitioned into a buckets called "directories". // diff --git a/src/ripple/app/paths/PathState.cpp b/src/ripple/app/paths/PathState.cpp index 2bcb32d259..f42f1f5c24 100644 --- a/src/ripple/app/paths/PathState.cpp +++ b/src/ripple/app/paths/PathState.cpp @@ -366,7 +366,6 @@ TER PathState::pushNode ( else node.issue_.account = backNode.issue_.account; - node.saRateMax = STAmount::saZero; node.saRevDeliver = STAmount (node.issue_); node.saFwdDeliver = node.saRevDeliver; diff --git a/src/ripple/app/paths/RippleCalc.cpp b/src/ripple/app/paths/RippleCalc.cpp index 8e5b26cade..98646629e8 100644 --- a/src/ripple/app/paths/RippleCalc.cpp +++ b/src/ripple/app/paths/RippleCalc.cpp @@ -366,7 +366,7 @@ TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo) << "rippleCalc: AFTER:" << " mIndex=" << pathState->index() << " uQuality=" << pathState->quality() - << " rate=" << amountFromRate (pathState->quality()); + << " rate=" << amountFromQuality (pathState->quality()); if (flowDebugInfo) flowDebugInfo->pushLiquiditySrc ( @@ -399,7 +399,7 @@ TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo) JLOG (j_.debug()) << "rippleCalc: better:" << " uQuality=" - << amountFromRate (pathState->quality()) + << amountFromQuality (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass=" << pathState->outPass(); } @@ -427,7 +427,7 @@ TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo) << " mIndex=" << pathState->index() << " uQuality=" << pathState->quality() << " rate=" - << amountFromRate (pathState->quality()) + << amountFromQuality (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass=" << pathState->outPass(); @@ -452,7 +452,7 @@ TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo) << "rippleCalc: " << "Summary: " << pathState->index() << " rate: " - << amountFromRate (pathState->quality()) + << amountFromQuality (pathState->quality()) << " quality:" << pathState->quality() << " best: " << (iBest == pathState->index ()); } @@ -470,7 +470,7 @@ TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo) JLOG (j_.debug ()) << "rippleCalc: best:" - << " uQuality=" << amountFromRate (pathState->quality ()) + << " uQuality=" << amountFromQuality (pathState->quality ()) << " inPass()=" << pathState->inPass () << " saOutPass=" << pathState->outPass () << " iBest=" << iBest; @@ -490,7 +490,7 @@ TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo) JLOG (j_.trace()) << "rippleCalc: best:" << " uQuality=" - << amountFromRate (pathState->quality()) + << amountFromQuality (pathState->quality()) << " inPass()=" << pathState->inPass() << " saOutPass=" << pathState->outPass() << " actualIn=" << actualAmountIn_ diff --git a/src/ripple/app/paths/RippleState.cpp b/src/ripple/app/paths/RippleState.cpp index d6d2eaf0eb..562569fc2b 100644 --- a/src/ripple/app/paths/RippleState.cpp +++ b/src/ripple/app/paths/RippleState.cpp @@ -41,21 +41,18 @@ RippleState::makeItem ( RippleState::RippleState ( std::shared_ptr&& sle, AccountID const& viewAccount) - : mLedgerEntry (std::move(sle)) - , mLowLimit (mLedgerEntry->getFieldAmount (sfLowLimit)) - , mHighLimit (mLedgerEntry->getFieldAmount (sfHighLimit)) + : sle_ (std::move(sle)) + , mFlags (sle_->getFieldU32 (sfFlags)) + , mLowLimit (sle_->getFieldAmount (sfLowLimit)) + , mHighLimit (sle_->getFieldAmount (sfHighLimit)) , mLowID (mLowLimit.getIssuer ()) , mHighID (mHighLimit.getIssuer ()) - , mBalance (mLedgerEntry->getFieldAmount (sfBalance)) + , lowQualityIn_ (sle_->getFieldU32 (sfLowQualityIn)) + , lowQualityOut_ (sle_->getFieldU32 (sfLowQualityOut)) + , highQualityIn_ (sle_->getFieldU32 (sfHighQualityIn)) + , highQualityOut_ (sle_->getFieldU32 (sfHighQualityOut)) + , mBalance (sle_->getFieldAmount (sfBalance)) { - mFlags = mLedgerEntry->getFieldU32 (sfFlags); - - mLowQualityIn = mLedgerEntry->getFieldU32 (sfLowQualityIn); - mLowQualityOut = mLedgerEntry->getFieldU32 (sfLowQualityOut); - - mHighQualityIn = mLedgerEntry->getFieldU32 (sfHighQualityIn); - mHighQualityOut = mLedgerEntry->getFieldU32 (sfHighQualityOut); - mViewLowest = (mLowID == viewAccount); if (!mViewLowest) diff --git a/src/ripple/app/paths/RippleState.h b/src/ripple/app/paths/RippleState.h index ec83aa6bdb..1166485402 100644 --- a/src/ripple/app/paths/RippleState.h +++ b/src/ripple/app/paths/RippleState.h @@ -21,6 +21,7 @@ #define RIPPLE_APP_PATHS_RIPPLESTATE_H_INCLUDED #include +#include #include #include #include @@ -58,7 +59,7 @@ public: uint256 key() const { - return mLedgerEntry->getIndex(); + return sle_->getIndex(); } // VFALCO Take off the "get" from each function name @@ -121,20 +122,22 @@ public: return !mViewLowest ? mLowLimit : mHighLimit; } - std::uint32_t getQualityIn () const + Rate const& + getQualityIn () const { - return ((std::uint32_t) (mViewLowest ? mLowQualityIn : mHighQualityIn)); + return mViewLowest ? lowQualityIn_ : highQualityIn_; } - std::uint32_t getQualityOut () const + Rate const& + getQualityOut () const { - return ((std::uint32_t) (mViewLowest ? mLowQualityOut : mHighQualityOut)); + return mViewLowest ? lowQualityOut_ : highQualityOut_; } Json::Value getJson (int); private: - std::shared_ptr mLedgerEntry; + std::shared_ptr sle_; bool mViewLowest; @@ -146,10 +149,10 @@ private: AccountID const& mLowID; AccountID const& mHighID; - std::uint64_t mLowQualityIn; - std::uint64_t mLowQualityOut; - std::uint64_t mHighQualityIn; - std::uint64_t mHighQualityOut; + Rate lowQualityIn_; + Rate lowQualityOut_; + Rate highQualityIn_; + Rate highQualityOut_; STAmount mBalance; }; diff --git a/src/ripple/app/paths/cursor/DeliverNodeForward.cpp b/src/ripple/app/paths/cursor/DeliverNodeForward.cpp index 7e0c16f413..9a69610103 100644 --- a/src/ripple/app/paths/cursor/DeliverNodeForward.cpp +++ b/src/ripple/app/paths/cursor/DeliverNodeForward.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include @@ -82,14 +83,11 @@ TER PathCursor::deliverNodeForward ( } else if (resultCode == tesSUCCESS) { - // Doesn't charge input. Input funds are in limbo. - // There's no fee if we're transferring XRP, if the sender is the - // issuer, or if the receiver is the issuer. - bool noFee = isXRP (previousNode().issue_) - || uInAccountID == previousNode().issue_.account - || node().offerOwnerAccount_ == previousNode().issue_.account; - const STAmount saInFeeRate = noFee ? STAmount::saOne - : previousNode().transferRate_; // Transfer rate of issuer. + auto const xferRate = effectiveRate ( + previousNode().issue_, + uInAccountID, + node().offerOwnerAccount_, + previousNode().transferRate_); // First calculate assuming no output fees: saInPassAct, // saInPassFees, saOutPassAct. @@ -111,8 +109,8 @@ TER PathCursor::deliverNodeForward ( true); // Offer maximum in with fees. - auto saInTotal = mulRound (saInFunded, saInFeeRate, - saInFunded.issue (), true); + auto saInTotal = multiplyRound ( + saInFunded, xferRate, true); auto saInRemaining = saInReq - saInAct - saInFees; if (saInRemaining < zero) @@ -123,8 +121,8 @@ TER PathCursor::deliverNodeForward ( // In without fees. auto saInPassAct = std::min ( - node().saTakerPays, divRound ( - saInSum, saInFeeRate, saInSum.issue (), true)); + node().saTakerPays, + divideRound (saInSum, xferRate, true)); // Out limited by in remaining. auto outPass = divRound ( @@ -247,8 +245,8 @@ TER PathCursor::deliverNodeForward ( auto inPassAct = mulRound ( saOutPassAct, node().saOfrRate, saInReq.issue (), true); saInPassAct = std::min (node().saTakerPays, inPassAct); - auto inPassFees = mulRound ( - saInPassAct, saInFeeRate, saInPassAct.issue (), true); + auto inPassFees = multiplyRound ( + saInPassAct, xferRate, true); saInPassFees = std::min (saInPassFeesMax, inPassFees); } diff --git a/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp b/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp index 4420db9f8a..efb67a639c 100644 --- a/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp +++ b/src/ripple/app/paths/cursor/DeliverNodeReverse.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include @@ -81,66 +82,58 @@ TER PathCursor::deliverNodeReverseImpl ( // Error or out of offers. break; - auto const hasFee = node().offerOwnerAccount_ == node().issue_.account - || uOutAccountID == node().issue_.account; - // Issuer sending or receiving. - - const STAmount saOutFeeRate = hasFee - ? STAmount::saOne // No fee. - : node().transferRate_; // Transfer rate of issuer. + auto const xferRate = effectiveRate ( + node().issue_, + uOutAccountID, + node().offerOwnerAccount_, + node().transferRate_); JLOG (j_.trace()) << "deliverNodeReverse:" - << " offerOwnerAccount_=" - << node().offerOwnerAccount_ - << " uOutAccountID=" - << uOutAccountID - << " node().issue_.account=" - << node().issue_.account - << " node().transferRate_=" << node().transferRate_ - << " saOutFeeRate=" << saOutFeeRate; + << " offerOwnerAccount_=" << node().offerOwnerAccount_ + << " uOutAccountID=" << uOutAccountID + << " node().issue_.account=" << node().issue_.account + << " xferRate=" << xferRate; - if (multiQuality_) + // Only use rate when not in multi-quality mode + if (!multiQuality_) { - // In multi-quality mode, ignore rate. - } - else if (!node().saRateMax) - { - // Set initial rate. - node().saRateMax = saOutFeeRate; + if (!node().rateMax) + { + // Set initial rate. + JLOG (j_.trace()) + << "Set initial rate"; - JLOG (j_.trace()) - << "deliverNodeReverse: Set initial rate:" - << " node().saRateMax=" << node().saRateMax - << " saOutFeeRate=" << saOutFeeRate; - } - else if (saOutFeeRate > node().saRateMax) - { - // Offer exceeds initial rate. - JLOG (j_.trace()) - << "deliverNodeReverse: Offer exceeds initial rate:" - << " node().saRateMax=" << node().saRateMax - << " saOutFeeRate=" << saOutFeeRate; + node().rateMax = xferRate; + } + else if (xferRate > node().rateMax) + { + // Offer exceeds initial rate. + JLOG (j_.trace()) + << "Offer exceeds initial rate: " << *node().rateMax; - break; // Done. Don't bother looking for smaller transferRates. - } - else if (saOutFeeRate < node().saRateMax) - { - // Reducing rate. Additional offers will only considered for this - // increment if they are at least this good. - // - // At this point, the overall rate is reducing, while the overall - // rate is not saOutFeeRate, it would be wrong to add anything with - // a rate above saOutFeeRate. - // - // The rate would be reduced if the current offer was from the - // issuer and the previous offer wasn't. + break; // Done. Don't bother looking for smaller transferRates. + } + else if (xferRate < node().rateMax) + { + // Reducing rate. Additional offers will only + // be considered for this increment if they + // are at least this good. + // + // At this point, the overall rate is reducing, + // while the overall rate is not xferRate, it + // would be wrong to add anything with a rate + // above xferRate. + // + // The rate would be reduced if the current + // offer was from the issuer and the previous + // offer wasn't. - node().saRateMax = saOutFeeRate; + JLOG (j_.trace()) + << "Reducing rate: " << *node().rateMax; - JLOG (j_.trace()) - << "deliverNodeReverse: Reducing rate:" - << " node().saRateMax=" << node().saRateMax; + node().rateMax = xferRate; + } } // Amount that goes to the taker. @@ -157,8 +150,8 @@ TER PathCursor::deliverNodeReverseImpl ( // as a cost to taker. // // Round down: prefer liquidity rather than microscopic fees. - STAmount saOutPlusFees = mulRound ( - saOutPassAct, saOutFeeRate, saOutPassAct.issue (), false); + STAmount saOutPlusFees = multiplyRound ( + saOutPassAct, xferRate, false); // Offer out with fees. @@ -180,8 +173,7 @@ TER PathCursor::deliverNodeReverseImpl ( // Round up: prefer liquidity rather than microscopic fees. But, // limit by requested. - auto fee = divRound (saOutPlusFees, saOutFeeRate, - saOutPlusFees.issue (), true); + auto fee = divideRound (saOutPlusFees, xferRate, true); saOutPassAct = std::min (saOutPassReq, fee); JLOG (j_.trace()) @@ -280,8 +272,7 @@ TER PathCursor::deliverNodeReverseImpl ( auto outputRequirements = divRound (saInPassAct, node ().saOfrRate, node ().saTakerGets.issue (), true); saOutPassAct = std::min (saOutPassReq, outputRequirements); - auto outputFees = mulRound (saOutPassAct, saOutFeeRate, - saOutPassAct.issue (), true); + auto outputFees = multiplyRound (saOutPassAct, xferRate, true); saOutPlusFees = std::min (node().saOfferFunds, outputFees); JLOG (j_.trace()) diff --git a/src/ripple/app/paths/cursor/EffectiveRate.cpp b/src/ripple/app/paths/cursor/EffectiveRate.cpp new file mode 100644 index 0000000000..9f527c1690 --- /dev/null +++ b/src/ripple/app/paths/cursor/EffectiveRate.cpp @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +/* + 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. +*/ +//============================================================================== + +#include +#include +#include + +namespace ripple { +namespace path { + +Rate +effectiveRate( + Issue const& issue, + AccountID const& account1, + AccountID const& account2, + boost::optional const& rate) +{ + // 1:1 transfer rate for XRP + if (isXRP (issue)) + return parityRate; + + if (!rate) + LogicError ("No transfer rate set for node."); + + // 1:1 transfer rate if either of the accounts is the issuer + if (issue.account == account1 || issue.account == account2) + return parityRate; + + return rate.get(); +} + +} // path +} // ripple diff --git a/src/ripple/app/paths/cursor/EffectiveRate.h b/src/ripple/app/paths/cursor/EffectiveRate.h new file mode 100644 index 0000000000..21f4ef2fba --- /dev/null +++ b/src/ripple/app/paths/cursor/EffectiveRate.h @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +/* + 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_APP_PATHS_CURSOR_EFFECTIVERATE_H_INCLUDED +#define RIPPLE_APP_PATHS_CURSOR_EFFECTIVERATE_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { +namespace path { + +Rate +effectiveRate( + Issue const& issue, + AccountID const& account1, + AccountID const& account2, + boost::optional const& rate); + +} // path +} // ripple + +#endif diff --git a/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp b/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp index d6cd5017fe..3225f1c8f5 100644 --- a/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp +++ b/src/ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp @@ -55,18 +55,19 @@ TER PathCursor::forwardLiquidityForAccount () const AccountID const& nextAccountID = nextNode().isAccount() ? nextNode().account_ : node().account_; - std::uint32_t uQualityIn = nodeIndex_ + auto const qualityIn = nodeIndex_ ? quality_in (view(), node().account_, previousAccountID, node().issue_.currency) - : QUALITY_ONE; - std::uint32_t uQualityOut = (nodeIndex_ == lastNodeIndex) + : parityRate; + + auto const qualityOut = (nodeIndex_ == lastNodeIndex) ? quality_out (view(), node().account_, nextAccountID, node().issue_.currency) - : QUALITY_ONE; + : parityRate; // When looking backward (prv) for req we care about what we just // calculated: use fwd. @@ -153,12 +154,11 @@ TER PathCursor::forwardLiquidityForAccount () const // Last node. Accept all funds. Calculate amount actually to credit. auto& saCurReceive = pathState_.outPass(); - STAmount saIssueCrd = uQualityIn >= QUALITY_ONE + STAmount saIssueCrd = qualityIn >= parityRate ? previousNode().saFwdIssue // No fee. - : mulRound ( + : multiplyRound ( previousNode().saFwdIssue, - amountFromRate (uQualityIn), - previousNode().saFwdIssue.issue (), + qualityIn, true); // Amount to credit. // Amount to credit. Credit for less than received as a surcharge. @@ -196,8 +196,8 @@ TER PathCursor::forwardLiquidityForAccount () const // Rate : 1.0 : quality out rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - uQualityOut, + parityRate, + qualityOut, previousNode().saFwdRedeem, node().saRevRedeem, saPrvRedeemAct, @@ -214,8 +214,8 @@ TER PathCursor::forwardLiquidityForAccount () const // Rate: quality in : quality out rippleLiquidity ( rippleCalc_, - uQualityIn, - uQualityOut, + qualityIn, + qualityOut, previousNode().saFwdIssue, node().saRevRedeem, saPrvIssueAct, @@ -234,8 +234,8 @@ TER PathCursor::forwardLiquidityForAccount () const // Rate : 1.0 : transfer_rate rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - rippleTransferRate (view(), node().account_), + parityRate, + transferRate (view(), node().account_), previousNode().saFwdRedeem, node().saRevIssue, saPrvRedeemAct, @@ -254,8 +254,8 @@ TER PathCursor::forwardLiquidityForAccount () const // Rate: quality in : 1.0 rippleLiquidity ( rippleCalc_, - uQualityIn, - QUALITY_ONE, + qualityIn, + parityRate, previousNode().saFwdIssue, node().saRevIssue, saPrvIssueAct, @@ -303,8 +303,8 @@ TER PathCursor::forwardLiquidityForAccount () const // XXX Is having the transfer rate here correct? rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - rippleTransferRate (view(), node().account_), + parityRate, + transferRate (view(), node().account_), previousNode().saFwdRedeem, node().saRevDeliver, saPrvRedeemAct, @@ -321,8 +321,8 @@ TER PathCursor::forwardLiquidityForAccount () const // Rate: quality in : 1.0 rippleLiquidity ( rippleCalc_, - uQualityIn, - QUALITY_ONE, + qualityIn, + parityRate, previousNode().saFwdIssue, node().saRevDeliver, saPrvIssueAct, @@ -428,8 +428,8 @@ TER PathCursor::forwardLiquidityForAccount () const // Rate : 1.0 : quality out rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - uQualityOut, + parityRate, + qualityOut, previousNode().saFwdDeliver, node().saRevRedeem, saPrvDeliverAct, @@ -449,8 +449,8 @@ TER PathCursor::forwardLiquidityForAccount () const // Rate : 1.0 : transfer_rate rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - rippleTransferRate (view(), node().account_), + parityRate, + transferRate (view(), node().account_), previousNode().saFwdDeliver, node().saRevIssue, saPrvDeliverAct, @@ -480,8 +480,8 @@ TER PathCursor::forwardLiquidityForAccount () const // Rate : 1.0 : transfer_rate rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - rippleTransferRate (view(), node().account_), + parityRate, + transferRate (view(), node().account_), previousNode().saFwdDeliver, node().saRevDeliver, saPrvDeliverAct, diff --git a/src/ripple/app/paths/cursor/Liquidity.cpp b/src/ripple/app/paths/cursor/Liquidity.cpp index d8a7638edf..ee8176a394 100644 --- a/src/ripple/app/paths/cursor/Liquidity.cpp +++ b/src/ripple/app/paths/cursor/Liquidity.cpp @@ -39,14 +39,17 @@ TER PathCursor::liquidity () const << " nodeIndex=" << pc.nodeIndex_ << ".issue_.account=" << to_string (pc.node().issue_.account); - resultCode = pc.reverseLiquidity(); + resultCode = pc.reverseLiquidity(); + + if (!pc.node().transferRate_) + return tefINTERNAL; JLOG (j_.trace()) << "reverseLiquidity< " << "nodeIndex=" << pc.nodeIndex_ << " resultCode=" << transToken (resultCode) - << " transferRate_=" << pc.node().transferRate_ - << "/" << resultCode; + << " transferRate_=" << *pc.node().transferRate_ + << ": " << resultCode; if (resultCode != tesSUCCESS) break; diff --git a/src/ripple/app/paths/cursor/ReverseLiquidity.cpp b/src/ripple/app/paths/cursor/ReverseLiquidity.cpp index ad190de08d..f34f1c1cb7 100644 --- a/src/ripple/app/paths/cursor/ReverseLiquidity.cpp +++ b/src/ripple/app/paths/cursor/ReverseLiquidity.cpp @@ -55,9 +55,8 @@ TER PathCursor::reverseLiquidity () const // TOMOVE: The account charges // a fee when third parties transfer that account's own issuances. - // node.transferRate_ caches the output transfer rate for this node. - node().transferRate_ = amountFromRate ( - rippleTransferRate (view(), node().issue_.account)); + // Cache the output transfer rate for this node. + node().transferRate_ = transferRate (view(), node().issue_.account); if (node().isAccount ()) return reverseLiquidityForAccount (); diff --git a/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp b/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp index be6da3fa95..1af3cdb315 100644 --- a/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp +++ b/src/ripple/app/paths/cursor/ReverseLiquidityForAccount.cpp @@ -49,8 +49,8 @@ TER PathCursor::reverseLiquidityForAccount () const auto const isFinalNode = (nodeIndex_ == lastNodeIndex); // 0 quality means none has yet been determined. - std::uint64_t uRateMax = 0 -; + std::uint64_t uRateMax = 0; + // Current is allowed to redeem to next. const bool previousNodeIsAccount = !nodeIndex_ || previousNode().isAccount(); @@ -63,22 +63,22 @@ TER PathCursor::reverseLiquidityForAccount () const : node().account_; // Offers are always issue. // This is the quality from from the previous node to this one. - const std::uint32_t uQualityIn + auto const qualityIn = (nodeIndex_ != 0) ? quality_in (view(), node().account_, previousAccountID, node().issue_.currency) - : QUALITY_ONE; + : parityRate; // And this is the quality from the next one to this one. - const std::uint32_t uQualityOut + auto const qualityOut = (nodeIndex_ != lastNodeIndex) ? quality_out (view(), node().account_, nextAccountID, node().issue_.currency) - : QUALITY_ONE; + : parityRate; // For previousNodeIsAccount: // Previous account is already owed. @@ -112,8 +112,8 @@ TER PathCursor::reverseLiquidityForAccount () const << " node.account_=" << node().account_ << " nextAccountID=" << nextAccountID << " currency=" << node().issue_.currency - << " uQualityIn=" << uQualityIn - << " uQualityOut=" << uQualityOut + << " qualityIn=" << qualityIn + << " qualityOut=" << qualityOut << " saPrvOwed=" << saPrvOwed << " saPrvLimit=" << saPrvLimit; @@ -208,7 +208,7 @@ TER PathCursor::reverseLiquidityForAccount () const << " (available) previousNode.saRevRedeem=" << previousNode().saRevRedeem << " uRateMax=" - << amountFromRate (uRateMax).getText (); + << amountFromQuality (uRateMax).getText (); } else { @@ -227,8 +227,8 @@ TER PathCursor::reverseLiquidityForAccount () const // won't be included the current increment. rippleLiquidity ( rippleCalc_, - uQualityIn, - QUALITY_ONE, + qualityIn, + parityRate, saPrvIssueReq, saCurWantedReq, previousNode().saRevIssue, @@ -266,8 +266,8 @@ TER PathCursor::reverseLiquidityForAccount () const // as 1:1. rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - uQualityOut, + parityRate, + qualityOut, saPrvRedeemReq, node().saRevRedeem, previousNode().saRevRedeem, @@ -290,8 +290,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate: quality in : quality out rippleLiquidity ( rippleCalc_, - uQualityIn, - uQualityOut, + qualityIn, + qualityOut, saPrvIssueReq, node().saRevRedeem, previousNode().saRevIssue, @@ -316,8 +316,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate : 1.0 : transfer_rate rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - rippleTransferRate (view(), node().account_), + parityRate, + transferRate (view(), node().account_), saPrvRedeemReq, node().saRevIssue, previousNode().saRevRedeem, @@ -344,8 +344,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate: quality in : 1.0 rippleLiquidity ( rippleCalc_, - uQualityIn, - QUALITY_ONE, + qualityIn, + parityRate, saPrvIssueReq, node().saRevIssue, previousNode().saRevIssue, @@ -402,8 +402,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate : 1.0 : transfer_rate rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - rippleTransferRate (view(), node().account_), + parityRate, + transferRate (view(), node().account_), saPrvRedeemReq, node().saRevDeliver, previousNode().saRevRedeem, @@ -419,8 +419,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate: quality in : 1.0 rippleLiquidity ( rippleCalc_, - uQualityIn, - QUALITY_ONE, + qualityIn, + parityRate, saPrvIssueReq, node().saRevDeliver, previousNode().saRevIssue, @@ -483,8 +483,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate: quality in : 1.0 rippleLiquidity ( rippleCalc_, - uQualityIn, - QUALITY_ONE, + qualityIn, + parityRate, saPrvDeliverReq, saCurWantedReq, previousNode().saRevDeliver, @@ -527,8 +527,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate : 1.0 : quality out rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - uQualityOut, + parityRate, + qualityOut, saPrvDeliverReq, node().saRevRedeem, previousNode().saRevDeliver, @@ -545,8 +545,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate : 1.0 : transfer_rate rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - rippleTransferRate (view(), node().account_), + parityRate, + transferRate (view(), node().account_), saPrvDeliverReq, node().saRevIssue, previousNode().saRevDeliver, @@ -578,8 +578,8 @@ TER PathCursor::reverseLiquidityForAccount () const // Rate : 1.0 : transfer_rate rippleLiquidity ( rippleCalc_, - QUALITY_ONE, - rippleTransferRate (view(), node().account_), + parityRate, + transferRate (view(), node().account_), saPrvDeliverReq, node().saRevDeliver, previousNode().saRevDeliver, diff --git a/src/ripple/app/paths/cursor/RippleLiquidity.cpp b/src/ripple/app/paths/cursor/RippleLiquidity.cpp index 997bdc84b9..1e71e68d60 100644 --- a/src/ripple/app/paths/cursor/RippleLiquidity.cpp +++ b/src/ripple/app/paths/cursor/RippleLiquidity.cpp @@ -49,8 +49,8 @@ namespace path { void rippleLiquidity ( RippleCalc& rippleCalc, - std::uint32_t const uQualityIn, - std::uint32_t const uQualityOut, + Rate const& qualityIn, + Rate const& qualityOut, STAmount const& saPrvReq, // --> in limit including fees, <0 = unlimited STAmount const& saCurReq, // --> out limit STAmount& saPrvAct, // <-> in limit including achieved so far: <-- <= --> @@ -59,8 +59,8 @@ void rippleLiquidity ( { JLOG (rippleCalc.j_.trace()) << "rippleLiquidity>" - << " uQualityIn=" << uQualityIn - << " uQualityOut=" << uQualityOut + << " qualityIn=" << qualityIn + << " qualityOut=" << qualityOut << " saPrvReq=" << saPrvReq << " saCurReq=" << saCurReq << " saPrvAct=" << saPrvAct @@ -94,7 +94,7 @@ void rippleLiquidity ( if (saPrv == zero || saCur == zero) return; - if (uQualityIn >= uQualityOut) + if (qualityIn >= qualityOut) { // You're getting better quality than you asked for, so no fee. JLOG (rippleCalc.j_.trace()) << "rippleLiquidity: No fees"; @@ -129,22 +129,18 @@ void rippleLiquidity ( // If the quality is worse than the previous JLOG (rippleCalc.j_.trace()) << "rippleLiquidity: Fee"; - std::uint64_t uRate = getRate ( - STAmount (uQualityOut), STAmount (uQualityIn)); + std::uint64_t const uRate = getRate ( + STAmount (qualityOut.value), + STAmount (qualityIn.value)); // If the next rate is at least as good as the current rate, process. if (!uRateMax || uRate <= uRateMax) { - auto currency = saCur.getCurrency (); - auto uCurIssuerID = saCur.getIssuer (); - // current actual = current request * (quality out / quality in). - auto numerator = mulRound ( - saCur, uQualityOut, {currency, uCurIssuerID}, true); + auto numerator = multiplyRound (saCur, qualityOut, true); // True means "round up" to get best flow. - STAmount saCurIn = divRound ( - numerator, uQualityIn, {currency, uCurIssuerID}, true); + STAmount saCurIn = divideRound (numerator, qualityIn, true); JLOG (rippleCalc.j_.trace()) << "rippleLiquidity:" @@ -170,13 +166,12 @@ void rippleLiquidity ( // * (quality in / quality out). // This is inverted compared to the code above because we're // going the other way - - Issue issue{currency, uCurIssuerID}; - auto numerator = mulRound (saPrv, uQualityIn, issue, true); + auto numerator = multiplyRound (saPrv, + qualityIn, saCur.issue(), true); // A part of current. All of previous. (Cur is the driver // variable.) - STAmount saCurOut = divRound ( - numerator, uQualityOut, issue, true); + STAmount saCurOut = divideRound (numerator, + qualityOut, saCur.issue(), true); JLOG (rippleCalc.j_.trace()) << "rippleLiquidity:4: saCurReq=" << saCurReq; @@ -191,8 +186,8 @@ void rippleLiquidity ( JLOG (rippleCalc.j_.trace()) << "rippleLiquidity<" - << " uQualityIn=" << uQualityIn - << " uQualityOut=" << uQualityOut + << " qualityIn=" << qualityIn + << " qualityOut=" << qualityOut << " saPrvReq=" << saPrvReq << " saCurReq=" << saCurReq << " saPrvAct=" << saPrvAct @@ -200,7 +195,7 @@ void rippleLiquidity ( } static -std::uint32_t +Rate rippleQuality ( ReadView const& view, AccountID const& destination, @@ -209,32 +204,28 @@ rippleQuality ( SField const& sfLow, SField const& sfHigh) { - std::uint32_t uQuality (QUALITY_ONE); + if (destination == source) + return parityRate; - if (destination != source) - { - auto const sleRippleState = view.read( - keylet::line(destination, source, currency)); + auto const& sfField = destination < source ? sfLow : sfHigh; - // we should be able to assert(sleRippleState) here + auto const sle = view.read( + keylet::line(destination, source, currency)); - if (sleRippleState) - { - auto const& sfField = destination < source ? sfLow : sfHigh; + if (!sle || !sle->isFieldPresent (sfField)) + return parityRate; - uQuality = sleRippleState->isFieldPresent (sfField) - ? sleRippleState->getFieldU32 (sfField) - : QUALITY_ONE; + auto quality = sle->getFieldU32 (sfField); - if (!uQuality) - uQuality = 1; // Avoid divide by zero. - } - } + // Avoid divide by zero. NIKB CHECKME: if we + // allow zero qualities now, then we shouldn't. + if (quality == 0) + quality = 1; - return uQuality; + return Rate{ quality }; } -std::uint32_t +Rate quality_in ( ReadView const& view, AccountID const& uToAccountID, @@ -245,7 +236,7 @@ quality_in ( sfLowQualityIn, sfHighQualityIn); } -std::uint32_t +Rate quality_out ( ReadView const& view, AccountID const& uToAccountID, diff --git a/src/ripple/app/paths/cursor/RippleLiquidity.h b/src/ripple/app/paths/cursor/RippleLiquidity.h index 51ddf6b3c1..767c248deb 100644 --- a/src/ripple/app/paths/cursor/RippleLiquidity.h +++ b/src/ripple/app/paths/cursor/RippleLiquidity.h @@ -24,28 +24,29 @@ #include #include #include +#include namespace ripple { namespace path { void rippleLiquidity ( RippleCalc&, - const std::uint32_t uQualityIn, - const std::uint32_t uQualityOut, + Rate const& qualityIn, + Rate const& qualityOut, STAmount const& saPrvReq, STAmount const& saCurReq, STAmount& saPrvAct, STAmount& saCurAct, std::uint64_t& uRateMax); -std::uint32_t +Rate quality_in ( ReadView const& view, AccountID const& uToAccountID, AccountID const& uFromAccountID, Currency const& currency); -std::uint32_t +Rate quality_out ( ReadView const& view, AccountID const& uToAccountID, diff --git a/src/ripple/app/paths/impl/BookStep.cpp b/src/ripple/app/paths/impl/BookStep.cpp index 2ee6143112..4ad559855c 100644 --- a/src/ripple/app/paths/impl/BookStep.cpp +++ b/src/ripple/app/paths/impl/BookStep.cpp @@ -256,18 +256,20 @@ forEachOffer ( // Charge a fee even if the owner is the same as the issuer // (the old code does not charge a fee) // Calculate amount that goes to the taker and the amount charged the offer owner - auto transferRate = [&](AccountID const& id)->std::uint32_t + auto rate = [&](AccountID const& id)->std::uint32_t { if (isXRP (id) || id == dst) return QUALITY_ONE; - return rippleTransferRate (sb, id); + return transferRate (sb, id).value; }; - std::uint32_t const trIn = - prevStepRedeems ? transferRate (book.in.account) : QUALITY_ONE; + std::uint32_t const trIn = prevStepRedeems + ? rate (book.in.account) + : QUALITY_ONE; // Always charge the transfer fee, even if the owner is the issuer - std::uint32_t const trOut = - ownerPaysTransferFee ? transferRate (book.out.account) : QUALITY_ONE; + std::uint32_t const trOut = ownerPaysTransferFee + ? rate (book.out.account) + : QUALITY_ONE; typename FlowOfferStream::StepCounter counter (limit, j); FlowOfferStream offers ( diff --git a/src/ripple/app/paths/impl/DirectStep.cpp b/src/ripple/app/paths/impl/DirectStep.cpp index 106a8d6da4..a46d5a553e 100644 --- a/src/ripple/app/paths/impl/DirectStep.cpp +++ b/src/ripple/app/paths/impl/DirectStep.cpp @@ -546,8 +546,9 @@ DirectStepI::qualities ( { // Charge a transfer rate when issuing and previous step redeems auto const prevStepRedeems = prevStep_ && prevStep_->redeems (sb, fwd); - std::uint32_t const srcQOut = - prevStepRedeems ? rippleTransferRate (sb, src_) : QUALITY_ONE; + std::uint32_t const srcQOut = prevStepRedeems + ? transferRate (sb, src_).value + : QUALITY_ONE; return std::make_pair( srcQOut, quality ( // dst quality in diff --git a/src/ripple/app/tests/Taker.test.cpp b/src/ripple/app/tests/Taker.test.cpp index 3a270f1e47..25cb97f611 100644 --- a/src/ripple/app/tests/Taker.test.cpp +++ b/src/ripple/app/tests/Taker.test.cpp @@ -37,10 +37,21 @@ class Taker_test : public beast::unit_test::suite public: TestTaker ( - CrossType cross_type, Amounts const& amount, Quality const& quality, - STAmount const& funds, std::uint32_t flags, std::uint32_t rate_in, - std::uint32_t rate_out) - : BasicTaker (cross_type, AccountID(0x4701), amount, quality, flags, rate_in, rate_out) + CrossType cross_type, + Amounts const& amount, + Quality const& quality, + STAmount const& funds, + std::uint32_t flags, + Rate const& rate_in, + Rate const& rate_out) + : BasicTaker ( + cross_type, + AccountID(0x4701), + amount, + quality, + flags, + rate_in, + rate_out) , funds_ (funds) { } @@ -178,8 +189,8 @@ private: cross_attempt_offer const flow, Issue const& issue_in, Issue const& issue_out, - std::uint32_t rate_in = QUALITY_ONE, - std::uint32_t rate_out = QUALITY_ONE) + Rate rate_in = parityRate, + Rate rate_out = parityRate) { Amounts taker_offer (parse_amounts ( offer.in, issue_in, @@ -333,7 +344,7 @@ public: Quality q1 = get_quality ("1", "1"); // Highly exaggerated 50% transfer rate for the input and output: - std::uint32_t rate = QUALITY_ONE + (QUALITY_ONE / 2); + Rate const rate { parityRate.value + (parityRate.value / 2) }; // TAKER OWNER // QUAL OFFER FUNDS QUAL OFFER FUNDS EXPECTED diff --git a/src/ripple/app/tx/impl/Taker.cpp b/src/ripple/app/tx/impl/Taker.cpp index b8cabc447e..daee7f6b07 100644 --- a/src/ripple/app/tx/impl/Taker.cpp +++ b/src/ripple/app/tx/impl/Taker.cpp @@ -34,28 +34,10 @@ format_amount (STAmount const& amount) return txt; } -STAmount -BasicTaker::Rate::divide (STAmount const& amount) const -{ - if (quality_ == QUALITY_ONE) - return amount; - - return ripple::divide (amount, rate_, amount.issue ()); -} - -STAmount -BasicTaker::Rate::multiply (STAmount const& amount) const -{ - if (quality_ == QUALITY_ONE) - return amount; - - return ripple::multiply (amount, rate_, amount.issue ()); -} - BasicTaker::BasicTaker ( CrossType cross_type, AccountID const& account, Amounts const& amount, - Quality const& quality, std::uint32_t flags, std::uint32_t rate_in, - std::uint32_t rate_out, beast::Journal journal) + Quality const& quality, std::uint32_t flags, Rate const& rate_in, + Rate const& rate_out, beast::Journal journal) : account_ (account) , quality_ (quality) , threshold_ (quality_) @@ -72,8 +54,8 @@ BasicTaker::BasicTaker ( assert (remaining_.in > zero); assert (remaining_.out > zero); - assert (m_rate_in != 0); - assert (m_rate_out != 0); + assert (m_rate_in.value != 0); + assert (m_rate_out.value != 0); // If we are dealing with a particular flavor, make sure that it's the // flavor we expect: @@ -92,24 +74,23 @@ BasicTaker::BasicTaker ( ++threshold_; } -BasicTaker::Rate +Rate BasicTaker::effective_rate ( - std::uint32_t rate, Issue const &issue, + Rate const& rate, Issue const &issue, AccountID const& from, AccountID const& to) { - assert (rate != 0); - - if (rate != QUALITY_ONE) + // If there's a transfer rate, the issuer is not involved + // and the sender isn't the same as the recipient, return + // the actual transfer rate. + if (rate != parityRate && + from != to && + from != issue.account && + to != issue.account) { - // We ignore the transfer if the sender is also the recipient since no - // actual transfer takes place in that case. We also ignore if either - // the sender or the receiver is the issuer. - - if (from != to && from != issue.account && to != issue.account) - return Rate (rate); + return rate; } - return Rate (QUALITY_ONE); + return parityRate; } bool @@ -232,7 +213,7 @@ BasicTaker::flow_xrp_to_iou ( { Flow f; f.order = order; - f.issuers.out = rate_out.multiply (f.order.out); + f.issuers.out = multiply (f.order.out, rate_out); log_flow ("flow_xrp_to_iou", f); @@ -240,7 +221,7 @@ BasicTaker::flow_xrp_to_iou ( if (owner_funds < f.issuers.out) { f.issuers.out = owner_funds; - f.order.out = rate_out.divide (f.issuers.out); + f.order.out = divide (f.issuers.out, rate_out); f.order.in = qual_mul (f.order.out, quality, f.order.in); log_flow ("(clamped on owner balance)", f); } @@ -250,7 +231,7 @@ BasicTaker::flow_xrp_to_iou ( { f.order.out = remaining_.out; f.order.in = qual_mul (f.order.out, quality, f.order.in); - f.issuers.out = rate_out.multiply (f.order.out); + f.issuers.out = multiply (f.order.out, rate_out); log_flow ("(clamped on taker output)", f); } @@ -259,7 +240,7 @@ BasicTaker::flow_xrp_to_iou ( { f.order.in = taker_funds; f.order.out = qual_div (f.order.in, quality, f.order.out); - f.issuers.out = rate_out.multiply (f.order.out); + f.issuers.out = multiply (f.order.out, rate_out); log_flow ("(clamped on taker funds)", f); } @@ -269,7 +250,7 @@ BasicTaker::flow_xrp_to_iou ( { f.order.in = remaining_.in; f.order.out = qual_div (f.order.in, quality, f.order.out); - f.issuers.out = rate_out.multiply (f.order.out); + f.issuers.out = multiply (f.order.out, rate_out); log_flow ("(clamped on taker input)", f); } @@ -284,7 +265,7 @@ BasicTaker::flow_iou_to_xrp ( { Flow f; f.order = order; - f.issuers.in = rate_in.multiply (f.order.in); + f.issuers.in = multiply (f.order.in, rate_in); log_flow ("flow_iou_to_xrp", f); @@ -293,7 +274,7 @@ BasicTaker::flow_iou_to_xrp ( { f.order.out = owner_funds; f.order.in = qual_mul (f.order.out, quality, f.order.in); - f.issuers.in = rate_in.multiply (f.order.in); + f.issuers.in = multiply (f.order.in, rate_in); log_flow ("(clamped on owner funds)", f); } @@ -305,7 +286,7 @@ BasicTaker::flow_iou_to_xrp ( { f.order.out = remaining_.out; f.order.in = qual_mul (f.order.out, quality, f.order.in); - f.issuers.in = rate_in.multiply (f.order.in); + f.issuers.in = multiply (f.order.in, rate_in); log_flow ("(clamped on taker output)", f); } } @@ -314,7 +295,7 @@ BasicTaker::flow_iou_to_xrp ( if (remaining_.in < f.order.in) { f.order.in = remaining_.in; - f.issuers.in = rate_in.multiply (f.order.in); + f.issuers.in = multiply (f.order.in, rate_in); f.order.out = qual_div (f.order.in, quality, f.order.out); log_flow ("(clamped on taker input)", f); } @@ -323,7 +304,7 @@ BasicTaker::flow_iou_to_xrp ( if (taker_funds < f.issuers.in) { f.issuers.in = taker_funds; - f.order.in = rate_in.divide (f.issuers.in); + f.order.in = divide (f.issuers.in, rate_in); f.order.out = qual_div (f.order.in, quality, f.order.out); log_flow ("(clamped on taker funds)", f); } @@ -339,8 +320,8 @@ BasicTaker::flow_iou_to_iou ( { Flow f; f.order = order; - f.issuers.in = rate_in.multiply (f.order.in); - f.issuers.out = rate_out.multiply (f.order.out); + f.issuers.in = multiply (f.order.in, rate_in); + f.issuers.out = multiply (f.order.out, rate_out); log_flow ("flow_iou_to_iou", f); @@ -348,9 +329,9 @@ BasicTaker::flow_iou_to_iou ( if (owner_funds < f.issuers.out) { f.issuers.out = owner_funds; - f.order.out = rate_out.divide (f.issuers.out); + f.order.out = divide (f.issuers.out, rate_out); f.order.in = qual_mul (f.order.out, quality, f.order.in); - f.issuers.in = rate_in.multiply (f.order.in); + f.issuers.in = multiply (f.order.in, rate_in); log_flow ("(clamped on owner funds)", f); } @@ -359,8 +340,8 @@ BasicTaker::flow_iou_to_iou ( { f.order.out = remaining_.out; f.order.in = qual_mul (f.order.out, quality, f.order.in); - f.issuers.out = rate_out.multiply (f.order.out); - f.issuers.in = rate_in.multiply (f.order.in); + f.issuers.out = multiply (f.order.out, rate_out); + f.issuers.in = multiply (f.order.in, rate_in); log_flow ("(clamped on taker output)", f); } @@ -368,9 +349,9 @@ BasicTaker::flow_iou_to_iou ( if (remaining_.in < f.order.in) { f.order.in = remaining_.in; - f.issuers.in = rate_in.multiply (f.order.in); + f.issuers.in = multiply (f.order.in, rate_in); f.order.out = qual_div (f.order.in, quality, f.order.out); - f.issuers.out = rate_out.multiply (f.order.out); + f.issuers.out = multiply (f.order.out, rate_out); log_flow ("(clamped on taker input)", f); } @@ -378,9 +359,9 @@ BasicTaker::flow_iou_to_iou ( if (taker_funds < f.issuers.in) { f.issuers.in = taker_funds; - f.order.in = rate_in.divide (f.issuers.in); + f.order.in = divide (f.issuers.in, rate_in); f.order.out = qual_div (f.order.in, quality, f.order.out); - f.issuers.out = rate_out.multiply (f.order.out); + f.issuers.out = multiply (f.order.out, rate_out); log_flow ("(clamped on taker funds)", f); } @@ -503,7 +484,7 @@ BasicTaker::do_cross ( // Adjust the second leg of the offer down: flow2.order.in = flow1.order.out; flow2.order.out = qual_div (flow2.order.in, quality2, flow2.order.out); - flow2.issuers.out = leg2_rate.multiply (flow2.order.out); + flow2.issuers.out = multiply (flow2.order.out, leg2_rate); log_flow ("Balancing: adjusted second leg down", flow2); } else if (flow1.order.out > flow2.order.in) @@ -511,7 +492,7 @@ BasicTaker::do_cross ( // Adjust the first leg of the offer down: flow1.order.out = flow2.order.in; flow1.order.in = qual_mul (flow1.order.out, quality1, flow1.order.in); - flow1.issuers.in = leg1_rate.multiply (flow1.order.in); + flow1.issuers.in = multiply (flow1.order.in, leg1_rate); log_flow ("Balancing: adjusted first leg down", flow2); } @@ -779,15 +760,15 @@ Taker::cross (Offer& leg1, Offer& leg2) return fill (ret.first, leg1, ret.second, leg2); } -std::uint32_t +Rate Taker::calculateRate ( ApplyView const& view, AccountID const& issuer, AccountID const& account) { return isXRP (issuer) || (account == issuer) - ? QUALITY_ONE - : rippleTransferRate (view, issuer); + ? parityRate + : transferRate (view, issuer); } } // ripple diff --git a/src/ripple/app/tx/impl/Taker.h b/src/ripple/app/tx/impl/Taker.h index 7fbdfef85e..ee1706813b 100644 --- a/src/ripple/app/tx/impl/Taker.h +++ b/src/ripple/app/tx/impl/Taker.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -42,28 +43,6 @@ enum class CrossType /** State for the active party during order book or payment operations. */ class BasicTaker { -private: - class Rate - { - private: - std::uint32_t quality_; - STAmount rate_; - - public: - Rate (std::uint32_t quality) - : quality_ (quality) - { - assert (quality_ != 0); - rate_ = amountFromRate (quality_); - } - - STAmount - divide (STAmount const& amount) const; - - STAmount - multiply (STAmount const& amount) const; - }; - private: AccountID account_; Quality quality_; @@ -82,9 +61,9 @@ private: Issue const& issue_out_; // The rates that will be paid when the input and output currencies are - // transfer when the currency issuer isn't involved: - std::uint32_t const m_rate_in; - std::uint32_t const m_rate_out; + // transfered and the currency issuer isn't involved: + Rate const m_rate_in; + Rate const m_rate_out; // The type of crossing that we are performing CrossType cross_type_; @@ -132,7 +111,7 @@ private: // flows for a particular issue between two accounts. static Rate - effective_rate (std::uint32_t rate, Issue const &issue, + effective_rate (Rate const& rate, Issue const &issue, AccountID const& from, AccountID const& to); // The transfer rate for the input currency between the given accounts @@ -155,8 +134,8 @@ public: BasicTaker ( CrossType cross_type, AccountID const& account, Amounts const& amount, - Quality const& quality, std::uint32_t flags, std::uint32_t rate_in, - std::uint32_t rate_out, beast::Journal journal = beast::Journal ()); + Quality const& quality, std::uint32_t flags, Rate const& rate_in, + Rate const& rate_out, beast::Journal journal = beast::Journal ()); virtual ~BasicTaker () = default; @@ -291,7 +270,7 @@ public: private: static - std::uint32_t + Rate calculateRate (ApplyView const& view, AccountID const& issuer, AccountID const& account); diff --git a/src/ripple/ledger/View.h b/src/ripple/ledger/View.h index 78f02c1b96..7e225f4880 100644 --- a/src/ripple/ledger/View.h +++ b/src/ripple/ledger/View.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -90,16 +91,10 @@ forEachItemAfter (ReadView const& view, AccountID const& id, unsigned int limit, std::function< bool (std::shared_ptr const&)> f); -std::uint32_t -rippleTransferRate (ReadView const& view, +Rate +transferRate (ReadView const& view, AccountID const& issuer); -std::uint32_t -rippleTransferRate (ReadView const& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - AccountID const& issuer); - /** Returns `true` if the directory is empty @param key The key of the directory */ diff --git a/src/ripple/ledger/impl/View.cpp b/src/ripple/ledger/impl/View.cpp index 1c19449ac4..3cbea894a5 100644 --- a/src/ripple/ledger/impl/View.cpp +++ b/src/ripple/ledger/impl/View.cpp @@ -328,31 +328,16 @@ forEachItemAfter (ReadView const& view, AccountID const& id, } } -std::uint32_t -rippleTransferRate (ReadView const& view, +Rate +transferRate (ReadView const& view, AccountID const& issuer) { auto const sle = view.read(keylet::account(issuer)); - std::uint32_t quality; - if (sle && sle->isFieldPresent (sfTransferRate)) - quality = sle->getFieldU32 (sfTransferRate); - else - quality = QUALITY_ONE; - return quality; -} -std::uint32_t -rippleTransferRate (ReadView const& view, - AccountID const& uSenderID, - AccountID const& uReceiverID, - AccountID const& issuer) -{ - // If calculating the transfer rate from - // or to the issuer of the currency no - // fees are assessed. - return (uSenderID == issuer || uReceiverID == issuer) - ? QUALITY_ONE - : rippleTransferRate(view, issuer); + if (sle && sle->isFieldPresent (sfTransferRate)) + return Rate{ sle->getFieldU32 (sfTransferRate) }; + + return parityRate; } bool @@ -1367,27 +1352,26 @@ rippleTransferFee (ReadView const& view, AccountID const& from, AccountID const& to, AccountID const& issuer, - STAmount const& saAmount, + STAmount const& amount, beast::Journal j) { if (from != issuer && to != issuer) { - std::uint32_t uTransitRate = rippleTransferRate (view, issuer); + Rate const rate = transferRate (view, issuer); - if (QUALITY_ONE != uTransitRate) + if (parityRate != rate) { - STAmount saTransferTotal = multiply ( - saAmount, amountFromRate (uTransitRate), saAmount.issue ()); - STAmount saTransferFee = saTransferTotal - saAmount; + auto const fee = multiply (amount, rate) - amount; JLOG (j.debug()) << "rippleTransferFee:" << - " saTransferFee=" << saTransferFee.getFullText (); + " amount=" << amount.getFullText () << + " fee=" << fee.getFullText (); - return saTransferFee; + return fee; } } - return saAmount.zeroed(); + return amount.zeroed(); } // Send regardless of limits. @@ -1426,12 +1410,8 @@ rippleSend (ApplyView& view, } else { - auto const rate = rippleTransferRate (view, issuer); - if (QUALITY_ONE == rate) - saActual = saAmount; - else - saActual = - multiply (saAmount, amountFromRate (rate), saAmount.issue ()); + saActual = multiply (saAmount, + transferRate (view, issuer)); } JLOG (j.debug()) << "rippleSend> " << diff --git a/src/ripple/ledger/tests/View_test.cpp b/src/ripple/ledger/tests/View_test.cpp index 88744f54b9..151d08fcf0 100644 --- a/src/ripple/ledger/tests/View_test.cpp +++ b/src/ripple/ledger/tests/View_test.cpp @@ -646,27 +646,20 @@ class View_test using namespace jtx; Env env(*this); - auto const alice = Account("alice"); - auto const bob = Account("bob"); auto const gw1 = Account("gw1"); - env.fund(XRP(10000), alice, bob, gw1); + env.fund(XRP(10000), gw1); env.close(); auto rdView = env.closed(); // Test with no rate set on gw1. - expect (rippleTransferRate (*rdView, alice, bob, gw1) == 1000000000); - expect (rippleTransferRate (*rdView, gw1, alice, gw1) == 1000000000); - expect (rippleTransferRate (*rdView, alice, gw1, gw1) == 1000000000); + expect (transferRate (*rdView, gw1) == parityRate); env(rate(gw1, 1.02)); env.close(); rdView = env.closed(); - // Test with a non-unity rate set on gw1. - expect (rippleTransferRate (*rdView, alice, bob, gw1) == 1020000000); - expect (rippleTransferRate (*rdView, gw1, alice, gw1) == 1000000000); - expect (rippleTransferRate (*rdView, alice, gw1, gw1) == 1000000000); + expect (transferRate (*rdView, gw1) == Rate{ 1020000000 }); } void diff --git a/src/ripple/protocol/Rate.h b/src/ripple/protocol/Rate.h index b7268d6979..cbb4894ab7 100644 --- a/src/ripple/protocol/Rate.h +++ b/src/ripple/protocol/Rate.h @@ -30,20 +30,21 @@ namespace ripple { /** Represents a transfer rate - Transfer rates are specified as fractions of 1 billion. For example, a - transfer rate of 1% is represented as 1010000000. + Transfer rates are specified as fractions of 1 billion. + For example, a transfer rate of 1% is represented as + 1,010,000,000. */ struct Rate : private boost::totally_ordered { std::uint32_t value; - Rate () = default; + Rate () = delete; + explicit Rate (std::uint32_t rate) : value (rate) { - assert (rate != 0); } }; @@ -69,6 +70,42 @@ operator<< (std::ostream& os, Rate const& rate) return os; } +STAmount +multiply ( + STAmount const& amount, + Rate const& rate); + +STAmount +multiplyRound ( + STAmount const& amount, + Rate const& rate, + bool roundUp); + +STAmount +multiplyRound ( + STAmount const& amount, + Rate const& rate, + Issue const& issue, + bool roundUp); + +STAmount +divide ( + STAmount const& amount, + Rate const& rate); + +STAmount +divideRound ( + STAmount const& amount, + Rate const& rate, + bool roundUp); + +STAmount +divideRound ( + STAmount const& amount, + Rate const& rate, + Issue const& issue, + bool roundUp); + /** A transfer rate signifying a 1:1 exchange */ extern Rate const parityRate; diff --git a/src/ripple/protocol/STAmount.h b/src/ripple/protocol/STAmount.h index 4338316f64..4330c0cac4 100644 --- a/src/ripple/protocol/STAmount.h +++ b/src/ripple/protocol/STAmount.h @@ -75,9 +75,6 @@ public: static std::uint64_t const uRateOne; - static STAmount const saZero; - static STAmount const saOne; - //-------------------------------------------------------------------------- STAmount(SerialIter& sit, SField const& name); @@ -294,9 +291,6 @@ public: // //------------------------------------------------------------------------------ -STAmount -amountFromRate (std::uint64_t uRate); - // VFALCO TODO The parameter type should be Quality not uint64_t STAmount amountFromQuality (std::uint64_t rate); diff --git a/src/ripple/protocol/XRPAmount.h b/src/ripple/protocol/XRPAmount.h index 93bc3df7ea..2a45ecf78a 100644 --- a/src/ripple/protocol/XRPAmount.h +++ b/src/ripple/protocol/XRPAmount.h @@ -149,8 +149,6 @@ mulRatio ( if (!den) Throw ("division by zero"); - int128_t const den128 (den); - int128_t const num128 (num); int128_t const amt128 (amt.drops ()); auto const neg = amt.drops () < 0; auto const m = amt128 * num; diff --git a/src/ripple/protocol/impl/Rate2.cpp b/src/ripple/protocol/impl/Rate2.cpp index 0d44d6ad1c..67702b994a 100644 --- a/src/ripple/protocol/impl/Rate2.cpp +++ b/src/ripple/protocol/impl/Rate2.cpp @@ -25,4 +25,121 @@ namespace ripple { Rate const parityRate (QUALITY_ONE); +namespace detail { + +STAmount as_amount (Rate const& rate) +{ + return { noIssue(), rate.value, -9, false }; +} + +} + +STAmount +multiply ( + STAmount const& amount, + Rate const& rate) +{ + assert (rate.value != 0); + + if (rate == parityRate) + return amount; + + return multiply ( + amount, + detail::as_amount(rate), + amount.issue()); +} + +STAmount +multiplyRound ( + STAmount const& amount, + Rate const& rate, + bool roundUp) +{ + assert (rate.value != 0); + + if (rate == parityRate) + return amount; + + return mulRound ( + amount, + detail::as_amount(rate), + amount.issue(), + roundUp); +} + +STAmount +multiplyRound ( + STAmount const& amount, + Rate const& rate, + Issue const& issue, + bool roundUp) +{ + assert (rate.value != 0); + + if (rate == parityRate) + { + return amount; + } + + return mulRound ( + amount, + detail::as_amount(rate), + issue, + roundUp); +} + +STAmount +divide ( + STAmount const& amount, + Rate const& rate) +{ + assert (rate.value != 0); + + if (rate == parityRate) + return amount; + + return divide ( + amount, + detail::as_amount(rate), + amount.issue()); +} + +STAmount +divideRound ( + STAmount const& amount, + Rate const& rate, + bool roundUp) +{ + assert (rate.value != 0); + + if (rate == parityRate) + return amount; + + return divRound ( + amount, + detail::as_amount(rate), + amount.issue(), + roundUp); +} + +STAmount +divideRound ( + STAmount const& amount, + Rate const& rate, + Issue const& issue, + bool roundUp) +{ + assert (rate.value != 0); + + if (rate == parityRate) + return amount; + + return divRound ( + amount, + detail::as_amount(rate), + issue, + roundUp); +} + } diff --git a/src/ripple/protocol/impl/STAmount.cpp b/src/ripple/protocol/impl/STAmount.cpp index 40914ffd93..1170416f6e 100644 --- a/src/ripple/protocol/impl/STAmount.cpp +++ b/src/ripple/protocol/impl/STAmount.cpp @@ -406,9 +406,6 @@ STAmount operator- (STAmount const& v1, STAmount const& v2) std::uint64_t const STAmount::uRateOne = getRate (STAmount (1), STAmount (1)); -STAmount const STAmount::saZero (noIssue (), 0u); -STAmount const STAmount::saOne (noIssue (), 1u); - void STAmount::setIssue (Issue const& issue) { @@ -721,12 +718,6 @@ void STAmount::set (std::int64_t v) //------------------------------------------------------------------------------ -STAmount -amountFromRate (std::uint64_t uRate) -{ - return { noIssue(), uRate, -9, false }; -} - STAmount amountFromQuality (std::uint64_t rate) { diff --git a/src/ripple/protocol/impl/TER.cpp b/src/ripple/protocol/impl/TER.cpp index 02c6642bcb..8a5b2c42ba 100644 --- a/src/ripple/protocol/impl/TER.cpp +++ b/src/ripple/protocol/impl/TER.cpp @@ -133,7 +133,7 @@ bool transResultInfo (TER code, std::string& token, std::string& text) { terNO_LINE, { "terNO_LINE", "No such line." } }, { terPRE_SEQ, { "terPRE_SEQ", "Missing/inapplicable prior transaction." } }, { terOWNERS, { "terOWNERS", "Non-zero owner count." } }, - { terQUEUED, { "terQUEUED", "Held until escalated fee drops." } }, + { terQUEUED, { "terQUEUED", "Held until escalated fee drops." } }, { tesSUCCESS, { "tesSUCCESS", "The transaction was applied. Only final in a validated ledger." } }, }; diff --git a/src/ripple/rpc/handlers/AccountLines.cpp b/src/ripple/rpc/handlers/AccountLines.cpp index 58213faf6c..ff78502136 100644 --- a/src/ripple/rpc/handlers/AccountLines.cpp +++ b/src/ripple/rpc/handlers/AccountLines.cpp @@ -56,10 +56,8 @@ void addLine (Json::Value& jsonLines, RippleState const& line) jPeer[jss::currency] = to_string (saBalance.issue ().currency); jPeer[jss::limit] = saLimit.getText (); jPeer[jss::limit_peer] = saLimitPeer.getText (); - jPeer[jss::quality_in] - = static_cast (line.getQualityIn ()); - jPeer[jss::quality_out] - = static_cast (line.getQualityOut ()); + jPeer[jss::quality_in] = line.getQualityIn ().value; + jPeer[jss::quality_out] = line.getQualityOut ().value; if (line.getAuth ()) jPeer[jss::authorized] = true; if (line.getAuthPeer ()) diff --git a/src/ripple/unity/app_paths.cpp b/src/ripple/unity/app_paths.cpp index da78056027..e48209eb56 100644 --- a/src/ripple/unity/app_paths.cpp +++ b/src/ripple/unity/app_paths.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include