Use Rate to represent transfer rates (RIPD-201, RIPD-983):

The Ripple protocol represent transfer rates and trust line
qualities as fractions of one billion. For example, a transfer
rate of 1% is represented as 1010000000.

Previously, such rates where represented either as std::uint32_t
or std::uint64_t. Other, nominally related types, also used an
integral representation and could be unintentionally substituted.

The new Rate class addresses this by providing a simple, type
safe alternative which also helps make the code self-documenting
since arithmetic operations now can be clearly understood to
involve the scaling of an amount by a rate.
This commit is contained in:
Nik Bougalis
2016-06-08 03:07:19 -07:00
committed by Miguel Portilla
parent f060820f3b
commit a698104c55
34 changed files with 579 additions and 433 deletions

View File

@@ -1001,6 +1001,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\paths\cursor\EffectiveRate.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\paths\cursor\EffectiveRate.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\paths\cursor\ForwardLiquidity.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -1482,6 +1482,12 @@
<ClCompile Include="..\..\src\ripple\app\paths\cursor\DeliverNodeReverse.cpp">
<Filter>ripple\app\paths\cursor</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\paths\cursor\EffectiveRate.cpp">
<Filter>ripple\app\paths\cursor</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\paths\cursor\EffectiveRate.h">
<Filter>ripple\app\paths\cursor</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\paths\cursor\ForwardLiquidity.cpp">
<Filter>ripple\app\paths\cursor</Filter>
</ClCompile>

View File

@@ -65,6 +65,7 @@
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/HashPrefix.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/Rate.h>
#include <ripple/resource/Fees.h>
#include <ripple/resource/Gossip.h>
#include <ripple/resource/ResourceManager.h>
@@ -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;

View File

@@ -22,7 +22,9 @@
#include <ripple/app/paths/NodeDirectory.h>
#include <ripple/app/paths/Types.h>
#include <ripple/protocol/Rate.h>
#include <ripple/protocol/UintTypes.h>
#include <boost/optional.hpp>
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<Rate> 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<Rate> rateMax;
// The nodes are partitioned into a buckets called "directories".
//

View File

@@ -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;

View File

@@ -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_

View File

@@ -41,21 +41,18 @@ RippleState::makeItem (
RippleState::RippleState (
std::shared_ptr<SLE const>&& 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)

View File

@@ -21,6 +21,7 @@
#define RIPPLE_APP_PATHS_RIPPLESTATE_H_INCLUDED
#include <ripple/ledger/View.h>
#include <ripple/protocol/Rate.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/STLedgerEntry.h>
#include <cstdint>
@@ -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<SLE const> mLedgerEntry;
std::shared_ptr<SLE const> 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;
};

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <BeastConfig.h>
#include <ripple/app/paths/cursor/EffectiveRate.h>
#include <ripple/app/paths/cursor/RippleLiquidity.h>
#include <ripple/basics/Log.h>
@@ -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);
}

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <BeastConfig.h>
#include <ripple/app/paths/cursor/EffectiveRate.h>
#include <ripple/app/paths/cursor/RippleLiquidity.h>
#include <ripple/basics/Log.h>
@@ -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())

View File

@@ -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 <BeastConfig.h>
#include <ripple/app/paths/cursor/EffectiveRate.h>
#include <ripple/basics/contract.h>
namespace ripple {
namespace path {
Rate
effectiveRate(
Issue const& issue,
AccountID const& account1,
AccountID const& account2,
boost::optional<Rate> 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

View File

@@ -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 <ripple/protocol/AccountID.h>
#include <ripple/protocol/Issue.h>
#include <ripple/protocol/Rate.h>
#include <boost/optional.hpp>
namespace ripple {
namespace path {
Rate
effectiveRate(
Issue const& issue,
AccountID const& account1,
AccountID const& account2,
boost::optional<Rate> const& rate);
} // path
} // ripple
#endif

View File

@@ -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,

View File

@@ -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;

View File

@@ -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 ();

View File

@@ -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,

View File

@@ -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,

View File

@@ -24,28 +24,29 @@
#include <ripple/app/paths/RippleCalc.h>
#include <ripple/app/paths/Tuning.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Rate.h>
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,

View File

@@ -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<TAmtIn, TAmtOut>::StepCounter counter (limit, j);
FlowOfferStream<TAmtIn, TAmtOut> offers (

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -24,6 +24,7 @@
#include <ripple/core/Config.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/Quality.h>
#include <ripple/protocol/Rate.h>
#include <ripple/protocol/TER.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/beast/utility/Journal.h>
@@ -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);

View File

@@ -25,6 +25,7 @@
#include <ripple/ledger/RawView.h>
#include <ripple/ledger/ReadView.h>
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/Rate.h>
#include <ripple/protocol/Serializer.h>
#include <ripple/protocol/STLedgerEntry.h>
#include <ripple/protocol/STObject.h>
@@ -90,16 +91,10 @@ forEachItemAfter (ReadView const& view, AccountID const& id,
unsigned int limit, std::function<
bool (std::shared_ptr<SLE const> 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
*/

View File

@@ -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> " <<

View File

@@ -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

View File

@@ -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 <Rate>
{
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;

View File

@@ -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);

View File

@@ -149,8 +149,6 @@ mulRatio (
if (!den)
Throw<std::runtime_error> ("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;

View File

@@ -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);
}
}

View File

@@ -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)
{

View File

@@ -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." } },
};

View File

@@ -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<Json::UInt> (line.getQualityIn ());
jPeer[jss::quality_out]
= static_cast<Json::UInt> (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 ())

View File

@@ -38,6 +38,7 @@
#include <ripple/app/paths/cursor/AdvanceNode.cpp>
#include <ripple/app/paths/cursor/DeliverNodeForward.cpp>
#include <ripple/app/paths/cursor/DeliverNodeReverse.cpp>
#include <ripple/app/paths/cursor/EffectiveRate.cpp>
#include <ripple/app/paths/cursor/ForwardLiquidity.cpp>
#include <ripple/app/paths/cursor/ForwardLiquidityForAccount.cpp>
#include <ripple/app/paths/cursor/Liquidity.cpp>