Further cleanups of RippleCalc.

* Rename many variables.
* Make most of PathState private.
* Extract out common Node::isAccount() code.
* Rename bConsumed to allLiquidityConsumed_.
* Extract out code into PathState::clear().
This commit is contained in:
Tom Ritchford
2014-06-06 13:36:00 -04:00
committed by Vinnie Falco
parent bf116308d4
commit 27620af1bf
26 changed files with 955 additions and 977 deletions

View File

@@ -2233,6 +2233,11 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\Calculators.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\Node.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\app\paths\Node.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\PathNext.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
@@ -2256,6 +2261,8 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\app\paths\Pathfinder.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\QualityConstraint.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\RippleCalc.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
@@ -2273,6 +2280,8 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\Tuning.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\Types.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\peers\ClusterNodeStatus.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\peers\PeerSet.cpp">
@@ -2932,6 +2941,9 @@
<ClCompile Include="..\..\src\ripple\module\rpc\handlers\LedgerHeader.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\rpc\handlers\LedgerRequest.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\rpc\handlers\LogLevel.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
@@ -3068,9 +3080,6 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\rpc\impl\GetMasterGenerator.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\rpc\impl\Handlers.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\rpc\impl\LegacyPathFind.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>

View File

@@ -3279,6 +3279,12 @@
<ClInclude Include="..\..\src\ripple\module\app\paths\Calculators.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\Node.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\module\app\paths\Node.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\PathNext.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
@@ -3306,6 +3312,9 @@
<ClInclude Include="..\..\src\ripple\module\app\paths\Pathfinder.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\QualityConstraint.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\app\paths\RippleCalc.cpp">
<Filter>ripple\module\app\paths</Filter>
</ClCompile>
@@ -3327,6 +3336,9 @@
<ClInclude Include="..\..\src\ripple\module\app\paths\Tuning.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\paths\Types.h">
<Filter>ripple\module\app\paths</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\module\app\peers\ClusterNodeStatus.h">
<Filter>ripple\module\app\peers</Filter>
</ClInclude>
@@ -4119,6 +4131,9 @@
<ClCompile Include="..\..\src\ripple\module\rpc\handlers\LedgerHeader.cpp">
<Filter>ripple\module\rpc\handlers</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\rpc\handlers\LedgerRequest.cpp">
<Filter>ripple\module\rpc\handlers</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\rpc\handlers\LogLevel.cpp">
<Filter>ripple\module\rpc\handlers</Filter>
</ClCompile>
@@ -4260,9 +4275,6 @@
<ClInclude Include="..\..\src\ripple\module\rpc\impl\GetMasterGenerator.h">
<Filter>ripple\module\rpc\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\module\rpc\impl\Handlers.cpp">
<Filter>ripple\module\rpc\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\module\rpc\impl\LegacyPathFind.cpp">
<Filter>ripple\module\rpc\impl</Filter>
</ClCompile>

View File

@@ -25,34 +25,35 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
TER calcNodeFwd (
TER nodeFwd (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, PathState& pathState,
const bool bMultiQuality)
{
auto const& node = pathState.vpnNodes[nodeIndex];
auto const nodeIsAccount = isAccount(node);
auto const& node = pathState.nodes()[nodeIndex];
auto const isAccount = node.isAccount();
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeFwd> nodeIndex=" << nodeIndex;
<< "nodeFwd> nodeIndex=" << nodeIndex;
TER errorCode = nodeIsAccount
? calcNodeAccountFwd (rippleCalc, nodeIndex, pathState, bMultiQuality)
: calcNodeOfferFwd (rippleCalc, nodeIndex, pathState, bMultiQuality);
TER resultCode = isAccount
? nodeAccountFwd (rippleCalc, nodeIndex, pathState, bMultiQuality)
: nodeOfferFwd (rippleCalc, nodeIndex, pathState, bMultiQuality);
if (errorCode == tesSUCCESS && nodeIndex + 1 != pathState.vpnNodes.size ())
errorCode = calcNodeFwd (rippleCalc, nodeIndex + 1, pathState, bMultiQuality);
if (resultCode == tesSUCCESS && nodeIndex + 1 != pathState.nodes().size ())
resultCode = nodeFwd (rippleCalc, nodeIndex + 1, pathState, bMultiQuality);
if (errorCode == tesSUCCESS && !(pathState.saInPass && pathState.saOutPass))
errorCode = tecPATH_DRY;
if (resultCode == tesSUCCESS && !(pathState.inPass() && pathState.outPass()))
resultCode = tecPATH_DRY;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeFwd<"
<< "nodeFwd<"
<< " nodeIndex:" << nodeIndex
<< " errorCode:" << errorCode;
<< " resultCode:" << resultCode;
return errorCode;
return resultCode;
}
// Calculate a node and its previous nodes.
@@ -61,51 +62,51 @@ TER calcNodeFwd (
// must be asked for.
//
// Then work forward, figuring out how much can actually be delivered.
// <-- errorCode: tesSUCCESS or tecPATH_DRY
// <-- resultCode: tesSUCCESS or tecPATH_DRY
// <-> pnNodes:
// --> [end]saWanted.mAmount
// --> [all]saWanted.mCurrency
// --> [all]saAccount
// <-> [0]saWanted.mAmount : --> limit, <-- actual
TER calcNodeRev (
TER nodeRev (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, PathState& pathState,
const bool bMultiQuality)
{
PathState::Node& node = pathState.vpnNodes[nodeIndex];
bool const nodeIsAccount
= is_bit_set (node.uFlags, STPathElement::typeAccount);
TER errorCode;
auto& node = pathState.nodes()[nodeIndex];
auto const isAccount = node.isAccount();
TER resultCode;
node.saTransferRate = STAmount::saFromRate (
rippleCalc.mActiveLedger.rippleTransferRate (node.uIssuerID));
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeRev>"
<< "nodeRev>"
<< " nodeIndex=" << nodeIndex
<< " nodeIsAccount=" << nodeIsAccount
<< " isAccount=" << isAccount
<< " uIssuerID=" << RippleAddress::createHumanAccountID (node.uIssuerID)
<< " saTransferRate=" << node.saTransferRate;
errorCode = nodeIsAccount
? calcNodeAccountRev (rippleCalc, nodeIndex, pathState, bMultiQuality)
: calcNodeOfferRev (rippleCalc, nodeIndex, pathState, bMultiQuality);
resultCode = isAccount
? nodeAccountRev (rippleCalc, nodeIndex, pathState, bMultiQuality)
: nodeOfferRev (rippleCalc, nodeIndex, pathState, bMultiQuality);
// Do previous.
if (errorCode != tesSUCCESS)
if (resultCode != tesSUCCESS)
// Error, don't continue.
nothing ();
else if (nodeIndex)
// Continue in reverse. TODO(tom): remove unnecessary recursion.
errorCode = calcNodeRev (rippleCalc, nodeIndex - 1, pathState, bMultiQuality);
resultCode = nodeRev (rippleCalc, nodeIndex - 1, pathState, bMultiQuality);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeRev< "
<< "nodeRev< "
<< "nodeIndex=" << nodeIndex
<< " errorCode=%s" << transToken (errorCode)
<< "/" << errorCode;
<< " resultCode=%s" << transToken (resultCode)
<< "/" << resultCode;
return errorCode;
return resultCode;
}
} // path
} // ripple

View File

@@ -22,6 +22,7 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// The reverse pass has been narrowing by credit available and inflating by fees
// as it worked backwards. Now, for the current account node, take the actual
@@ -38,25 +39,23 @@ namespace ripple {
// do not need to push funds.
// - If next node is an offer and output is XRP then we need to deliver funds to
// limbo.
TER calcNodeAccountFwd (
TER nodeAccountFwd (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, // 0 <= nodeIndex <= lastNodeIndex
PathState& pathState,
const bool bMultiQuality)
{
TER errorCode = tesSUCCESS;
const unsigned int lastNodeIndex = pathState.vpnNodes.size () - 1;
TER resultCode = tesSUCCESS;
const unsigned int lastNodeIndex = pathState.nodes().size () - 1;
std::uint64_t uRateMax = 0;
auto& previousNode = pathState.vpnNodes[nodeIndex ? nodeIndex - 1 : 0];
auto& node = pathState.vpnNodes[nodeIndex];
auto& nextNode = pathState.vpnNodes[nodeIndex == lastNodeIndex ? lastNodeIndex : nodeIndex + 1];
auto& previousNode = pathState.nodes()[nodeIndex ? nodeIndex - 1 : 0];
auto& node = pathState.nodes()[nodeIndex];
auto& nextNode = pathState.nodes()[nodeIndex == lastNodeIndex ? lastNodeIndex : nodeIndex + 1];
const bool previousNodeIsAccount
= is_bit_set (previousNode.uFlags, STPathElement::typeAccount);
const bool nextNodeIsAccount
= is_bit_set (nextNode.uFlags, STPathElement::typeAccount);
const bool previousNodeIsAccount = previousNode.isAccount();
const bool nextNodeIsAccount = nextNode.isAccount();
const uint160& previousAccountID
= previousNodeIsAccount ? previousNode.uAccountID : node.uAccountID;
@@ -93,7 +92,7 @@ TER calcNodeAccountFwd (
previousNode.saFwdDeliver.getIssuer ());
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd> "
<< "nodeAccountFwd> "
<< "nodeIndex=" << nodeIndex << "/" << lastNodeIndex
<< " previousNode.saFwdRedeem:" << previousNode.saFwdRedeem
<< " saPrvIssueReq:" << previousNode.saFwdIssue
@@ -116,43 +115,43 @@ TER calcNodeAccountFwd (
// available.
node.saFwdRedeem = node.saRevRedeem;
if (pathState.saInReq >= zero)
if (pathState.inReq() >= zero)
{
// Limit by send max.
node.saFwdRedeem = std::min (
node.saFwdRedeem, pathState.saInReq - pathState.saInAct);
node.saFwdRedeem, pathState.inReq() - pathState.inAct());
}
pathState.saInPass = node.saFwdRedeem;
pathState.inPass() = node.saFwdRedeem;
node.saFwdIssue = node.saFwdRedeem == node.saRevRedeem
// Fully redeemed.
? node.saRevIssue : STAmount (node.saRevIssue);
if (!!node.saFwdIssue && pathState.saInReq >= zero)
if (!!node.saFwdIssue && pathState.inReq() >= zero)
{
// Limit by send max.
node.saFwdIssue = std::min (
node.saFwdIssue,
pathState.saInReq - pathState.saInAct - node.saFwdRedeem);
pathState.inReq() - pathState.inAct() - node.saFwdRedeem);
}
pathState.saInPass += node.saFwdIssue;
pathState.inPass() += node.saFwdIssue;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: ^ --> ACCOUNT --> account :"
<< " saInReq=" << pathState.saInReq
<< " saInAct=" << pathState.saInAct
<< "nodeAccountFwd: ^ --> ACCOUNT --> account :"
<< " saInReq=" << pathState.inReq()
<< " saInAct=" << pathState.inAct()
<< " node.saFwdRedeem:" << node.saFwdRedeem
<< " node.saRevIssue:" << node.saRevIssue
<< " node.saFwdIssue:" << node.saFwdIssue
<< " pathState.saInPass:" << pathState.saInPass;
<< " pathState.saInPass:" << pathState.inPass();
}
else if (nodeIndex == lastNodeIndex)
{
// account --> ACCOUNT --> $
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: account --> ACCOUNT --> $ :"
<< "nodeAccountFwd: account --> ACCOUNT --> $ :"
<< " previousAccountID="
<< RippleAddress::createHumanAccountID (previousAccountID)
<< " node.uAccountID="
@@ -162,7 +161,7 @@ TER calcNodeAccountFwd (
// Last node. Accept all funds. Calculate amount actually to credit.
STAmount& saCurReceive = pathState.saOutPass;
STAmount& saCurReceive = pathState.outPass();
STAmount saIssueCrd = uQualityIn >= QUALITY_ONE
? previousNode.saFwdIssue // No fee.
@@ -177,21 +176,21 @@ TER calcNodeAccountFwd (
if (saCurReceive)
{
// Actually receive.
errorCode = rippleCalc.mActiveLedger.rippleCredit (
resultCode = rippleCalc.mActiveLedger.rippleCredit (
previousAccountID, node.uAccountID,
previousNode.saFwdRedeem + previousNode.saFwdIssue, false);
}
else
{
// After applying quality, total payment was microscopic.
errorCode = tecPATH_DRY;
resultCode = tecPATH_DRY;
}
}
else
{
// account --> ACCOUNT --> account
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: account --> ACCOUNT --> account";
<< "nodeAccountFwd: account --> ACCOUNT --> account";
node.saFwdRedeem.clear (node.saRevRedeem);
node.saFwdIssue.clear (node.saRevIssue);
@@ -201,7 +200,7 @@ TER calcNodeAccountFwd (
// Previous wants to redeem.
{
// Rate : 1.0 : quality out
calcNodeRipple (
nodeRipple (
rippleCalc,
QUALITY_ONE, uQualityOut, previousNode.saFwdRedeem, node.saRevRedeem,
saPrvRedeemAct, node.saFwdRedeem, uRateMax);
@@ -214,7 +213,7 @@ TER calcNodeAccountFwd (
// Current has more to redeem to next.
{
// Rate: quality in : quality out
calcNodeRipple (
nodeRipple (
rippleCalc,
uQualityIn, uQualityOut, previousNode.saFwdIssue, node.saRevRedeem,
saPrvIssueAct, node.saFwdRedeem, uRateMax);
@@ -229,7 +228,7 @@ TER calcNodeAccountFwd (
// Current wants to issue.
{
// Rate : 1.0 : transfer_rate
calcNodeRipple (
nodeRipple (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
previousNode.saFwdRedeem, node.saRevIssue, saPrvRedeemAct,
@@ -245,7 +244,7 @@ TER calcNodeAccountFwd (
// Current wants to issue.
{
// Rate: quality in : 1.0
calcNodeRipple (
nodeRipple (
rippleCalc,
uQualityIn, QUALITY_ONE, previousNode.saFwdIssue, node.saRevIssue,
saPrvIssueAct, node.saFwdIssue, uRateMax);
@@ -254,7 +253,7 @@ TER calcNodeAccountFwd (
STAmount saProvide = node.saFwdRedeem + node.saFwdIssue;
// Adjust prv --> cur balance : take all inbound
errorCode = saProvide
resultCode = saProvide
? rippleCalc.mActiveLedger.rippleCredit (
previousAccountID, node.uAccountID,
previousNode.saFwdRedeem + previousNode.saFwdIssue, false)
@@ -273,7 +272,7 @@ TER calcNodeAccountFwd (
{
// Non-XRP, current node is the issuer.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: account --> ACCOUNT --> offer";
<< "nodeAccountFwd: account --> ACCOUNT --> offer";
node.saFwdDeliver.clear (node.saRevDeliver);
@@ -286,7 +285,7 @@ TER calcNodeAccountFwd (
{
// Rate : 1.0 : transfer_rate
// XXX Is having the transfer rate here correct?
calcNodeRipple (
nodeRipple (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
previousNode.saFwdRedeem, node.saRevDeliver, saPrvRedeemAct,
@@ -300,14 +299,14 @@ TER calcNodeAccountFwd (
// Previous wants to issue. To next must be ok.
{
// Rate: quality in : 1.0
calcNodeRipple (
nodeRipple (
rippleCalc,
uQualityIn, QUALITY_ONE, previousNode.saFwdIssue, node.saRevDeliver,
saPrvIssueAct, node.saFwdDeliver, uRateMax);
}
// Adjust prv --> cur balance : take all inbound
errorCode = node.saFwdDeliver
resultCode = node.saFwdDeliver
? rippleCalc.mActiveLedger.rippleCredit (
previousAccountID, node.uAccountID,
previousNode.saFwdRedeem + previousNode.saFwdIssue, false)
@@ -319,11 +318,11 @@ TER calcNodeAccountFwd (
node.saFwdDeliver = node.saRevDeliver;
// If limited, then limit by send max and available.
if (pathState.saInReq >= zero)
if (pathState.inReq() >= zero)
{
// Limit by send max.
node.saFwdDeliver = std::min (node.saFwdDeliver,
pathState.saInReq - pathState.saInAct);
pathState.inReq() - pathState.inAct());
// Limit XRP by available. No limit for non-XRP as issuer.
if (node.uCurrencyID.isZero ())
@@ -335,11 +334,11 @@ TER calcNodeAccountFwd (
}
// Record amount sent for pass.
pathState.saInPass = node.saFwdDeliver;
pathState.inPass() = node.saFwdDeliver;
if (!node.saFwdDeliver)
{
errorCode = tecPATH_DRY;
resultCode = tecPATH_DRY;
}
else if (!!node.uCurrencyID)
{
@@ -348,7 +347,7 @@ TER calcNodeAccountFwd (
// which ripple balance will be adjusted. Assume just issuing.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: ^ --> ACCOUNT -- !XRP --> offer";
<< "nodeAccountFwd: ^ --> ACCOUNT -- !XRP --> offer";
// As the issuer, would only issue.
// Don't need to actually deliver. As from delivering leave in
@@ -358,10 +357,10 @@ TER calcNodeAccountFwd (
else
{
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: ^ --> ACCOUNT -- XRP --> offer";
<< "nodeAccountFwd: ^ --> ACCOUNT -- XRP --> offer";
// Deliver XRP to limbo.
errorCode = rippleCalc.mActiveLedger.accountSend (
resultCode = rippleCalc.mActiveLedger.accountSend (
node.uAccountID, ACCOUNT_XRP, node.saFwdDeliver);
}
}
@@ -372,10 +371,10 @@ TER calcNodeAccountFwd (
{
// offer --> ACCOUNT --> $
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: offer --> ACCOUNT --> $ : "
<< "nodeAccountFwd: offer --> ACCOUNT --> $ : "
<< previousNode.saFwdDeliver;
STAmount& saCurReceive = pathState.saOutPass;
STAmount& saCurReceive = pathState.outPass();
// Amount to credit.
saCurReceive = previousNode.saFwdDeliver;
@@ -387,7 +386,7 @@ TER calcNodeAccountFwd (
{
// offer --> ACCOUNT --> account
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: offer --> ACCOUNT --> account";
<< "nodeAccountFwd: offer --> ACCOUNT --> account";
node.saFwdRedeem.clear (node.saRevRedeem);
node.saFwdIssue.clear (node.saRevIssue);
@@ -397,7 +396,7 @@ TER calcNodeAccountFwd (
// Previous wants to deliver and can current redeem.
{
// Rate : 1.0 : quality out
calcNodeRipple (
nodeRipple (
rippleCalc,
QUALITY_ONE, uQualityOut, previousNode.saFwdDeliver, node.saRevRedeem,
saPrvDeliverAct, node.saFwdRedeem, uRateMax);
@@ -413,7 +412,7 @@ TER calcNodeAccountFwd (
// Current wants issue.
{
// Rate : 1.0 : transfer_rate
calcNodeRipple (
nodeRipple (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
previousNode.saFwdDeliver, node.saRevIssue, saPrvDeliverAct,
@@ -425,7 +424,7 @@ TER calcNodeAccountFwd (
STAmount saProvide = node.saFwdRedeem + node.saFwdIssue;
if (!saProvide)
errorCode = tecPATH_DRY;
resultCode = tecPATH_DRY;
}
}
else
@@ -433,7 +432,7 @@ TER calcNodeAccountFwd (
// offer --> ACCOUNT --> offer
// deliver/redeem -> deliver/issue.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountFwd: offer --> ACCOUNT --> offer";
<< "nodeAccountFwd: offer --> ACCOUNT --> offer";
node.saFwdDeliver.clear (node.saRevDeliver);
@@ -443,7 +442,7 @@ TER calcNodeAccountFwd (
// Current wants issue.
{
// Rate : 1.0 : transfer_rate
calcNodeRipple (
nodeRipple (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
previousNode.saFwdDeliver, node.saRevDeliver, saPrvDeliverAct,
@@ -453,10 +452,11 @@ TER calcNodeAccountFwd (
// No income balance adjustments necessary. The paying side inside the
// offer paid and the next link will receive.
if (!node.saFwdDeliver)
errorCode = tecPATH_DRY;
resultCode = tecPATH_DRY;
}
return errorCode;
return resultCode;
}
} // path
} // ripple

View File

@@ -22,6 +22,7 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur, based on
// required deliverable, propagate redeem, issue, and deliver requests to the
@@ -36,26 +37,24 @@ namespace ripple {
//
// <-- tesSUCCESS or tecPATH_DRY
TER calcNodeAccountRev (
TER nodeAccountRev (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, PathState& pathState,
const bool bMultiQuality)
{
TER terResult = tesSUCCESS;
auto const lastNodeIndex = pathState.vpnNodes.size () - 1;
auto const lastNodeIndex = pathState.nodes().size () - 1;
auto const isFinalNode = (nodeIndex == lastNodeIndex);
std::uint64_t uRateMax = 0;
auto& previousNode = pathState.vpnNodes[nodeIndex ? nodeIndex - 1 : 0];
auto& node = pathState.vpnNodes[nodeIndex];
auto& nextNode = pathState.vpnNodes[isFinalNode ? lastNodeIndex : nodeIndex + 1];
auto& previousNode = pathState.nodes()[nodeIndex ? nodeIndex - 1 : 0];
auto& node = pathState.nodes()[nodeIndex];
auto& nextNode = pathState.nodes()[isFinalNode ? lastNodeIndex : nodeIndex + 1];
// Current is allowed to redeem to next.
const bool previousNodeIsAccount = !nodeIndex ||
is_bit_set (previousNode.uFlags, STPathElement::typeAccount);
const bool nextNodeIsAccount = isFinalNode ||
is_bit_set (nextNode.uFlags, STPathElement::typeAccount);
const bool previousNodeIsAccount = !nodeIndex || previousNode.isAccount();
const bool nextNodeIsAccount = isFinalNode || nextNode.isAccount();
const uint160& previousAccountID = previousNodeIsAccount
? previousNode.uAccountID : node.uAccountID;
@@ -92,7 +91,7 @@ TER calcNodeAccountRev (
: STAmount (node.uCurrencyID, node.uAccountID);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev>"
<< "nodeAccountRev>"
<< " nodeIndex=%d/%d" << nodeIndex << "/" << lastNodeIndex
<< " previousAccountID="
<< RippleAddress::createHumanAccountID (previousAccountID)
@@ -143,7 +142,7 @@ TER calcNodeAccountRev (
saCurDeliverReq.getCurrency (), saCurDeliverReq.getIssuer ());
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev:"
<< "nodeAccountRev:"
<< " saPrvRedeemReq:" << saPrvRedeemReq
<< " saPrvIssueReq:" << saPrvIssueReq
<< " saPrvDeliverAct:" << saPrvDeliverAct
@@ -178,12 +177,12 @@ TER calcNodeAccountRev (
// Overall deliverable.
// If previous is an account, limit.
const STAmount saCurWantedReq = std::min (
pathState.saOutReq - pathState.saOutAct, saPrvLimit + saPrvOwed);
pathState.outReq() - pathState.outAct(), saPrvLimit + saPrvOwed);
STAmount saCurWantedAct (
saCurWantedReq.getCurrency (), saCurWantedReq.getIssuer ());
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: account --> ACCOUNT --> $ :"
<< "nodeAccountRev: account --> ACCOUNT --> $ :"
<< " saCurWantedReq=" << saCurWantedReq;
// Calculate redeem
@@ -197,7 +196,7 @@ TER calcNodeAccountRev (
uRateMax = STAmount::uRateOne;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: Redeem at 1:1"
<< "nodeAccountRev: Redeem at 1:1"
<< " saPrvRedeemReq=" << saPrvRedeemReq
<< " (available) saPrvRedeemAct=" << saPrvRedeemAct
<< " uRateMax="
@@ -218,13 +217,13 @@ TER calcNodeAccountRev (
// If we previously redeemed and this has a poorer rate, this
// won't be included the current increment.
calcNodeRipple (
nodeRipple (
rippleCalc,
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq,
saPrvIssueAct, saCurWantedAct, uRateMax);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: Issuing: Rate: quality in : 1.0"
<< "nodeAccountRev: Issuing: Rate: quality in : 1.0"
<< " saPrvIssueAct:" << saPrvIssueAct
<< " saCurWantedAct:" << saCurWantedAct;
}
@@ -246,13 +245,13 @@ TER calcNodeAccountRev (
&& saPrvRedeemReq) // Previous has IOUs to redeem.
{
// Rate : 1.0 : quality out
calcNodeRipple (
nodeRipple (
rippleCalc,
QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq,
saPrvRedeemAct, saCurRedeemAct, uRateMax);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: Rate : 1.0 : quality out"
<< "nodeAccountRev: Rate : 1.0 : quality out"
<< " saPrvRedeemAct:" << saPrvRedeemAct
<< " saCurRedeemAct:" << saCurRedeemAct;
}
@@ -264,13 +263,13 @@ TER calcNodeAccountRev (
// Previous has no IOUs to redeem remaining.
{
// Rate: quality in : quality out
calcNodeRipple (
nodeRipple (
rippleCalc,
uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq,
saPrvIssueAct, saCurRedeemAct, uRateMax);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: Rate: quality in : quality out:"
<< "nodeAccountRev: Rate: quality in : quality out:"
<< " saPrvIssueAct:" << saPrvIssueAct
<< " saCurRedeemAct:" << saCurRedeemAct;
}
@@ -283,7 +282,7 @@ TER calcNodeAccountRev (
// Did not complete redeeming previous IOUs.
{
// Rate : 1.0 : transfer_rate
calcNodeRipple (
nodeRipple (
rippleCalc,
QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
@@ -291,7 +290,7 @@ TER calcNodeAccountRev (
saCurIssueAct, uRateMax);
WriteLog (lsDEBUG, RippleCalc)
<< "calcNodeAccountRev: Rate : 1.0 : transfer_rate:"
<< "nodeAccountRev: Rate : 1.0 : transfer_rate:"
<< " saPrvRedeemAct:" << saPrvRedeemAct
<< " saCurIssueAct:" << saCurIssueAct;
}
@@ -307,13 +306,13 @@ TER calcNodeAccountRev (
// Previous can issue.
{
// Rate: quality in : 1.0
calcNodeRipple (
nodeRipple (
rippleCalc,
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq,
saPrvIssueAct, saCurIssueAct, uRateMax);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: Rate: quality in : 1.0:"
<< "nodeAccountRev: Rate: quality in : 1.0:"
<< " saPrvIssueAct:" << saPrvIssueAct
<< " saCurIssueAct:" << saCurIssueAct;
}
@@ -325,7 +324,7 @@ TER calcNodeAccountRev (
}
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: ^|account --> ACCOUNT --> account :"
<< "nodeAccountRev: ^|account --> ACCOUNT --> account :"
<< " saCurRedeemReq:" << saCurRedeemReq
<< " saCurIssueReq:" << saCurIssueReq
<< " saPrvOwed:" << saPrvOwed
@@ -339,7 +338,7 @@ TER calcNodeAccountRev (
// Note: deliver is always issue as ACCOUNT is the issuer for the offer
// input.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: account --> ACCOUNT --> offer";
<< "nodeAccountRev: account --> ACCOUNT --> offer";
saPrvRedeemAct.clear (saPrvRedeemReq);
saPrvIssueAct.clear (saPrvIssueReq);
@@ -349,7 +348,7 @@ TER calcNodeAccountRev (
&& saCurDeliverReq) // Need some issued.
{
// Rate : 1.0 : transfer_rate
calcNodeRipple (
nodeRipple (
rippleCalc, QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct,
@@ -361,7 +360,7 @@ TER calcNodeAccountRev (
&& saCurDeliverReq != saCurDeliverAct) // Still need some issued.
{
// Rate: quality in : 1.0
calcNodeRipple (
nodeRipple (
rippleCalc,
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq,
saPrvIssueAct, saCurDeliverAct, uRateMax);
@@ -374,7 +373,7 @@ TER calcNodeAccountRev (
}
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: "
<< "nodeAccountRev: "
<< " saCurDeliverReq:" << saCurDeliverReq
<< " saCurDeliverAct:" << saCurDeliverAct
<< " saPrvOwed:" << saPrvOwed;
@@ -385,15 +384,15 @@ TER calcNodeAccountRev (
{
// offer --> ACCOUNT --> $
// Previous is an offer, no limit: redeem own IOUs.
const STAmount& saCurWantedReq = pathState.saOutReq - pathState.saOutAct;
const STAmount& saCurWantedReq = pathState.outReq() - pathState.outAct();
STAmount saCurWantedAct (
saCurWantedReq.getCurrency (), saCurWantedReq.getIssuer ());
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: offer --> ACCOUNT --> $ :"
<< "nodeAccountRev: offer --> ACCOUNT --> $ :"
<< " saCurWantedReq:" << saCurWantedReq
<< " saOutAct:" << pathState.saOutAct
<< " saOutReq:" << pathState.saOutReq;
<< " saOutAct:" << pathState.outAct()
<< " saOutReq:" << pathState.outReq();
if (saCurWantedReq <= zero)
{
@@ -406,7 +405,7 @@ TER calcNodeAccountRev (
// TODO(tom): can only be a race condition if true!
// Rate: quality in : 1.0
calcNodeRipple (
nodeRipple (
rippleCalc,
uQualityIn, QUALITY_ONE, saPrvDeliverReq, saCurWantedReq,
saPrvDeliverAct, saCurWantedAct, uRateMax);
@@ -418,7 +417,7 @@ TER calcNodeAccountRev (
}
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev:"
<< "nodeAccountRev:"
<< " saPrvDeliverAct:" << saPrvDeliverAct
<< " saPrvDeliverReq:" << saPrvDeliverReq
<< " saCurWantedAct:" << saCurWantedAct
@@ -429,7 +428,7 @@ TER calcNodeAccountRev (
// offer --> ACCOUNT --> account
// Note: offer is always delivering(redeeming) as account is issuer.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: offer --> ACCOUNT --> account :"
<< "nodeAccountRev: offer --> ACCOUNT --> account :"
<< " saCurRedeemReq:" << saCurRedeemReq
<< " saCurIssueReq:" << saCurIssueReq;
@@ -437,7 +436,7 @@ TER calcNodeAccountRev (
if (saCurRedeemReq) // Next wants us to redeem.
{
// Rate : 1.0 : quality out
calcNodeRipple (
nodeRipple (
rippleCalc,
QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq,
saPrvDeliverAct, saCurRedeemAct, uRateMax);
@@ -450,7 +449,7 @@ TER calcNodeAccountRev (
// Need some issued.
{
// Rate : 1.0 : transfer_rate
calcNodeRipple (
nodeRipple (
rippleCalc,
QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
@@ -459,7 +458,7 @@ TER calcNodeAccountRev (
}
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev:"
<< "nodeAccountRev:"
<< " saCurRedeemAct:" << saCurRedeemAct
<< " saCurRedeemReq:" << saCurRedeemReq
<< " saPrvDeliverAct:" << saPrvDeliverAct
@@ -477,10 +476,10 @@ TER calcNodeAccountRev (
// offer --> ACCOUNT --> offer
// deliver/redeem -> deliver/issue.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAccountRev: offer --> ACCOUNT --> offer";
<< "nodeAccountRev: offer --> ACCOUNT --> offer";
// Rate : 1.0 : transfer_rate
calcNodeRipple (
nodeRipple (
rippleCalc,
QUALITY_ONE,
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
@@ -497,4 +496,5 @@ TER calcNodeAccountRev (
return terResult;
}
} // path
} // ripple

View File

@@ -22,6 +22,7 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// OPTIMIZE: When calculating path increment, note if increment consumes all
// liquidity. No need to revisit path in the future if all liquidity is used.
@@ -37,15 +38,15 @@ namespace ripple {
// - Automatically advances to first offer.
// --> bEntryAdvance: true, to advance to next entry. false, recalculate.
// <-- uOfferIndex : 0=end of list.
TER calcNodeAdvance (
TER nodeAdvance (
RippleCalc& rippleCalc,
const unsigned int nodeIndex,
PathState& pathState,
const bool bMultiQuality,
const bool bReverse)
{
auto& previousNode = pathState.vpnNodes[nodeIndex - 1];
auto& node = pathState.vpnNodes[nodeIndex];
auto& previousNode = pathState.nodes()[nodeIndex - 1];
auto& node = pathState.nodes()[nodeIndex];
uint256& uDirectTip = node.uDirectTip;
uint256& uDirectEnd = node.uDirectEnd;
@@ -64,10 +65,10 @@ TER calcNodeAdvance (
STAmount& saTakerGets = node.saTakerGets;
bool& bFundsDirty = node.bFundsDirty;
TER errorCode = tesSUCCESS;
TER resultCode = tesSUCCESS;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: TakerPays:"
<< "nodeAdvance: TakerPays:"
<< saTakerPays << " TakerGets:" << saTakerGets;
int loopCount = 0;
@@ -109,7 +110,7 @@ TER calcNodeAdvance (
bDirectRestart = false;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: Initialize node:"
<< "nodeAdvance: Initialize node:"
<< " uDirectTip=" << uDirectTip
<<" uDirectEnd=" << uDirectEnd
<< " bDirectAdvance=" << bDirectAdvance;
@@ -132,7 +133,7 @@ TER calcNodeAdvance (
{
// Have another quality directory.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: Quality advance: uDirectTip="
<< "nodeAdvance: Quality advance: uDirectTip="
<< uDirectTip;
sleDirectDir = rippleCalc.mActiveLedger.entryCache (ltDIR_NODE, uDirectTip);
@@ -140,7 +141,7 @@ TER calcNodeAdvance (
else if (bReverse)
{
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: No more offers.";
<< "nodeAdvance: No more offers.";
uOfferIndex = 0;
break;
@@ -150,7 +151,7 @@ TER calcNodeAdvance (
// No more offers. Should be done rather than fall off end of
// book.
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeAdvance: Unreachable: "
<< "nodeAdvance: Unreachable: "
<< "Fell off end of order book.";
// FIXME: why?
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING :
@@ -166,7 +167,7 @@ TER calcNodeAdvance (
bEntryAdvance = true;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: directory dirty: saOfrRate="
<< "nodeAdvance: directory dirty: saOfrRate="
<< saOfrRate;
}
@@ -185,12 +186,12 @@ TER calcNodeAdvance (
bFundsDirty = false;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: funds dirty: saOfrRate="
<< "nodeAdvance: funds dirty: saOfrRate="
<< saOfrRate;
}
else
{
WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: as is";
WriteLog (lsTRACE, RippleCalc) << "nodeAdvance: as is";
}
}
else if (!rippleCalc.mActiveLedger.dirNext (
@@ -203,13 +204,13 @@ TER calcNodeAdvance (
// We are allowed to process multiple qualities if this is the
// only path.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: next quality";
<< "nodeAdvance: next quality";
bDirectAdvance = true; // Process next quality.
}
else if (!bReverse)
{
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeAdvance: unreachable: ran out of offers";
<< "nodeAdvance: unreachable: ran out of offers";
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING :
tecFAILED_PROCESSING;
// TEMPORARY
@@ -242,7 +243,7 @@ TER calcNodeAdvance (
uOfrOwnerID, node.uCurrencyID, node.uIssuerID);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: uOfrOwnerID="
<< "nodeAdvance: uOfrOwnerID="
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
<< " saTakerPays=" << saTakerPays
<< " saTakerGets=" << saTakerGets
@@ -254,7 +255,7 @@ TER calcNodeAdvance (
{
// Offer is expired.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: expired offer";
<< "nodeAdvance: expired offer";
rippleCalc.mUnfundedOffers.insert(uOfferIndex);
continue;
}
@@ -267,7 +268,7 @@ TER calcNodeAdvance (
{
// Past internal error, offer had bad amounts.
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeAdvance: PAST INTERNAL ERROR:"
<< "nodeAdvance: PAST INTERNAL ERROR:"
<< " OFFER NON-POSITIVE:"
<< " saTakerPays=" << saTakerPays
<< " saTakerGets=%s" << saTakerGets;
@@ -282,7 +283,7 @@ TER calcNodeAdvance (
// this in mUnfundedOffers.
// Just skip it. It will be deleted.
WriteLog (lsDEBUG, RippleCalc)
<< "calcNodeAdvance: PAST INTERNAL ERROR:"
<< "nodeAdvance: PAST INTERNAL ERROR:"
<< " OFFER NON-POSITIVE:"
<< " saTakerPays=" << saTakerPays
<< " saTakerGets=" << saTakerGets;
@@ -293,14 +294,14 @@ TER calcNodeAdvance (
// Reverse should have previously put bad offer in list.
// An internal error previously left a bad offer.
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeAdvance: INTERNAL ERROR:"
<< "nodeAdvance: INTERNAL ERROR:"
<<" OFFER NON-POSITIVE:"
<< " saTakerPays=" << saTakerPays
<< " saTakerGets=" << saTakerGets;
// Don't process at all, things are in an unexpected
// state for this transactions.
errorCode = tefEXCEPTION;
resultCode = tefEXCEPTION;
}
continue;
@@ -314,8 +315,8 @@ TER calcNodeAdvance (
// XXX Going forward could we fund something with a worse
// quality which was previously skipped? Might need to check
// quality.
auto itForward = pathState.umForward.find (asLine);
const bool bFoundForward = itForward != pathState.umForward.end ();
auto itForward = pathState.forward().find (asLine);
const bool bFoundForward = itForward != pathState.forward().end ();
// Only allow a source to be used once, in the first node
// encountered from initial path scan. This prevents
@@ -328,15 +329,15 @@ TER calcNodeAdvance (
// Temporarily unfunded. Another node uses this source,
// ignore in this offer.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: temporarily unfunded offer"
<< "nodeAdvance: temporarily unfunded offer"
<< " (forward)";
continue;
}
// This is overly strict. For contributions to past. We should
// only count source if actually used.
auto itReverse = pathState.umReverse.find (asLine);
bool bFoundReverse = itReverse != pathState.umReverse.end ();
auto itReverse = pathState.reverse().find (asLine);
bool bFoundReverse = itReverse != pathState.reverse().end ();
// For this quality increment, only allow a source to be used
// from a single node, in the first node encountered from
@@ -348,7 +349,7 @@ TER calcNodeAdvance (
// Temporarily unfunded. Another node uses this source,
// ignore in this offer.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: temporarily unfunded offer"
<< "nodeAdvance: temporarily unfunded offer"
<<" (reverse)";
continue;
}
@@ -367,7 +368,7 @@ TER calcNodeAdvance (
{
// Offer is unfunded.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: unfunded offer";
<< "nodeAdvance: unfunded offer";
if (bReverse && !bFoundReverse && !bFoundPast)
{
@@ -393,14 +394,14 @@ TER calcNodeAdvance (
{
// Consider source mentioned by current path state.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: remember="
<< "nodeAdvance: remember="
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
<< "/"
<< STAmount::createHumanCurrency (node.uCurrencyID)
<< "/"
<< RippleAddress::createHumanAccountID (node.uIssuerID);
pathState.umReverse.insert (std::make_pair (asLine, nodeIndex));
pathState.reverse().insert (std::make_pair (asLine, nodeIndex));
}
bFundsDirty = false;
@@ -408,20 +409,21 @@ TER calcNodeAdvance (
}
}
}
while (errorCode == tesSUCCESS && (bEntryAdvance || bDirectAdvance));
while (resultCode == tesSUCCESS && (bEntryAdvance || bDirectAdvance));
if (errorCode == tesSUCCESS)
if (resultCode == tesSUCCESS)
{
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeAdvance: uOfferIndex=" << uOfferIndex;
<< "nodeAdvance: uOfferIndex=" << uOfferIndex;
}
else
{
WriteLog (lsDEBUG, RippleCalc)
<< "calcNodeAdvance: errorCode=" << transToken (errorCode);
<< "nodeAdvance: resultCode=" << transToken (resultCode);
}
return errorCode;
return resultCode;
}
} // path
} // ripple

View File

@@ -22,14 +22,15 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// For current offer, get input from deliver/limbo and output to next account or
// deliver for next offers.
//
// <-- node.saFwdDeliver: For calcNodeAccountFwd to know how much went through
// <-- node.saFwdDeliver: For nodeAccountFwd to know how much went through
// --> node.saRevDeliver: Do not exceed.
TER calcNodeDeliverFwd (
TER nodeDeliverFwd (
RippleCalc& rippleCalc,
const unsigned int nodeIndex, // 0 < nodeIndex < lastNodeIndex
PathState& pathState,
@@ -39,11 +40,11 @@ TER calcNodeDeliverFwd (
STAmount& saInAct, // <-- Amount delivered, this invokation.
STAmount& saInFees) // <-- Fees charged, this invokation.
{
TER errorCode = tesSUCCESS;
TER resultCode = tesSUCCESS;
PathState::Node& previousNode = pathState.vpnNodes[nodeIndex - 1];
PathState::Node& node = pathState.vpnNodes[nodeIndex];
PathState::Node& nextNode = pathState.vpnNodes[nodeIndex + 1];
auto& previousNode = pathState.nodes()[nodeIndex - 1];
auto& node = pathState.nodes()[nodeIndex];
auto& nextNode = pathState.nodes()[nodeIndex + 1];
const uint160& nextAccountID = nextNode.uAccountID;
const uint160& uCurCurrencyID = node.uCurrencyID;
@@ -73,34 +74,34 @@ TER calcNodeDeliverFwd (
// XXX Perhaps make sure do not exceed saCurDeliverMax as another way to
// stop?
while (errorCode == tesSUCCESS && saInAct + saInFees < saInReq)
while (resultCode == tesSUCCESS && saInAct + saInFees < saInReq)
{
// Did not spend all inbound deliver funds.
if (++loopCount > CALC_NODE_DELIVER_MAX_LOOPS)
{
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeDeliverFwd: max loops cndf";
<< "nodeDeliverFwd: max loops cndf";
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
}
// Determine values for pass to adjust saInAct, saInFees, and
// saCurDeliverAct.
errorCode = calcNodeAdvance (
resultCode = nodeAdvance (
rippleCalc,
nodeIndex, pathState, bMultiQuality || saInAct == zero, false);
// If needed, advance to next funded offer.
if (errorCode != tesSUCCESS)
if (resultCode != tesSUCCESS)
{
nothing ();
}
else if (!uOfferIndex)
{
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeDeliverFwd: INTERNAL ERROR: Ran out of offers.";
<< "nodeDeliverFwd: INTERNAL ERROR: Ran out of offers.";
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
}
else if (errorCode == tesSUCCESS)
else if (resultCode == tesSUCCESS)
{
// Doesn't charge input. Input funds are in limbo.
bool& bEntryAdvance = node.bEntryAdvance;
@@ -163,7 +164,7 @@ TER calcNodeDeliverFwd (
STAmount saInPassFees;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverFwd:"
<< "nodeDeliverFwd:"
<< " nodeIndex=" << nodeIndex
<< " saOutFunded=" << saOutFunded
<< " saOutPassFunded=" << saOutPassFunded
@@ -182,10 +183,10 @@ TER calcNodeDeliverFwd (
if (!saTakerPays || saInSum <= zero)
{
WriteLog (lsDEBUG, RippleCalc)
<< "calcNodeDeliverFwd: Microscopic offer unfunded.";
<< "nodeDeliverFwd: Microscopic offer unfunded.";
// After math offer is effectively unfunded.
pathState.vUnfundedBecame.push_back (uOfferIndex);
pathState.becameUnfunded().push_back (uOfferIndex);
bEntryAdvance = true;
continue;
}
@@ -193,10 +194,10 @@ TER calcNodeDeliverFwd (
{
// Previous check should catch this.
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeDeliverFwd: UNREACHABLE REACHED";
<< "nodeDeliverFwd: UNREACHABLE REACHED";
// After math offer is effectively unfunded.
pathState.vUnfundedBecame.push_back (uOfferIndex);
pathState.becameUnfunded().push_back (uOfferIndex);
bEntryAdvance = true;
continue;
}
@@ -211,7 +212,7 @@ TER calcNodeDeliverFwd (
saInPassFees = saInPassFeesMax;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverFwd: ? --> OFFER --> account:"
<< "nodeDeliverFwd: ? --> OFFER --> account:"
<< " uOfrOwnerID="
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
<< " nextAccountID="
@@ -221,10 +222,10 @@ TER calcNodeDeliverFwd (
// Output: Debit offer owner, send XRP or non-XPR to next
// account.
errorCode = rippleCalc.mActiveLedger.accountSend (
resultCode = rippleCalc.mActiveLedger.accountSend (
uOfrOwnerID, nextAccountID, saOutPassAct);
if (errorCode != tesSUCCESS)
if (resultCode != tesSUCCESS)
break;
}
else
@@ -240,7 +241,7 @@ TER calcNodeDeliverFwd (
// Output fees vary as the next nodes offer owners may vary.
// Therefore, immediately push through output for current offer.
errorCode = calcNodeDeliverFwd (
resultCode = nodeDeliverFwd (
rippleCalc,
nodeIndex + 1,
pathState,
@@ -250,7 +251,7 @@ TER calcNodeDeliverFwd (
saOutPassAct, // <-- Amount delivered.
saOutPassFees); // <-- Fees charged.
if (errorCode != tesSUCCESS)
if (resultCode != tesSUCCESS)
break;
if (saOutPassAct == saOutPassMax)
@@ -282,13 +283,13 @@ TER calcNodeDeliverFwd (
rippleCalc.mActiveLedger.accountSend (uOfrOwnerID, id, outPassTotal);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverFwd: ? --> OFFER --> offer:"
<< "nodeDeliverFwd: ? --> OFFER --> offer:"
<< " saOutPassAct=" << saOutPassAct
<< " saOutPassFees=" << saOutPassFees;
}
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverFwd: "
<< "nodeDeliverFwd: "
<< " nodeIndex=" << nodeIndex
<< " saTakerGets=" << saTakerGets
<< " saTakerPays=" << saTakerPays
@@ -310,10 +311,10 @@ TER calcNodeDeliverFwd (
// same account.
{
auto id = !!uPrvCurrencyID ? uInAccountID : ACCOUNT_XRP;
errorCode = rippleCalc.mActiveLedger.accountSend (
resultCode = rippleCalc.mActiveLedger.accountSend (
id, uOfrOwnerID, saInPassAct);
if (errorCode != tesSUCCESS)
if (resultCode != tesSUCCESS)
break;
}
@@ -327,12 +328,12 @@ TER calcNodeDeliverFwd (
if (saTakerPaysNew < zero || saTakerGetsNew < zero)
{
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeDeliverFwd: NEGATIVE:"
<< "nodeDeliverFwd: NEGATIVE:"
<< " saTakerPaysNew=" << saTakerPaysNew
<< " saTakerGetsNew=" << saTakerGetsNew;
// If mOpenLedger, then ledger is not final, can vote no.
errorCode = rippleCalc.mOpenLedger
resultCode = rippleCalc.mOpenLedger
? telFAILED_PROCESSING : tecFAILED_PROCESSING;
break;
}
@@ -347,17 +348,17 @@ TER calcNodeDeliverFwd (
// Offer became unfunded.
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeDeliverFwd: unfunded:"
<< "nodeDeliverFwd: unfunded:"
<< " saOutPassAct=" << saOutPassAct
<< " saOutFunded=" << saOutFunded;
pathState.vUnfundedBecame.push_back (uOfferIndex);
pathState.becameUnfunded().push_back (uOfferIndex);
bEntryAdvance = true;
}
else
{
CondLog (saOutPassAct >= saOutFunded, lsWARNING, RippleCalc)
<< "calcNodeDeliverFwd: TOO MUCH:"
<< "nodeDeliverFwd: TOO MUCH:"
<< " saOutPassAct=" << saOutPassAct
<< " saOutFunded=" << saOutFunded;
@@ -374,12 +375,13 @@ TER calcNodeDeliverFwd (
}
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverFwd<"
<< "nodeDeliverFwd<"
<< " nodeIndex=" << nodeIndex
<< " saInAct=" << saInAct
<< " saInFees=" << saInFees;
return errorCode;
return resultCode;
}
} // path
} // ripple

View File

@@ -22,6 +22,7 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// At the right most node of a list of consecutive offer nodes, given the amount
// requested to be delivered, push toward node 0 the amount requested for
@@ -33,7 +34,7 @@ namespace ripple {
// spent on fees. Continue processing until the request is satisified as long
// as the rate does not increase past the initial rate.
TER calcNodeDeliverRev (
TER nodeDeliverRev (
RippleCalc& rippleCalc,
const unsigned int nodeIndex,
PathState& pathState,
@@ -45,10 +46,10 @@ TER calcNodeDeliverRev (
STAmount& saOutAct) // <-- Funds actually delivered for an
// increment.
{
TER errorCode = tesSUCCESS;
TER resultCode = tesSUCCESS;
PathState::Node& previousNode = pathState.vpnNodes[nodeIndex - 1];
PathState::Node& node = pathState.vpnNodes[nodeIndex];
auto& previousNode = pathState.nodes()[nodeIndex - 1];
auto& node = pathState.nodes()[nodeIndex];
const uint160& uCurIssuerID = node.uIssuerID;
const uint160& uPrvAccountID = previousNode.uAccountID;
@@ -71,7 +72,7 @@ TER calcNodeDeliverRev (
saOutAct.clear (saOutReq);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev>"
<< "nodeDeliverRev>"
<< " saOutAct=" << saOutAct
<< " saOutReq=" << saOutReq
<< " saPrvDlvReq=" << saPrvDlvReq;
@@ -100,12 +101,12 @@ TER calcNodeDeliverRev (
STAmount& saTakerGets = node.saTakerGets;
STAmount& saRateMax = node.saRateMax;
errorCode = calcNodeAdvance (
resultCode = nodeAdvance (
rippleCalc,
nodeIndex, pathState, bMultiQuality || saOutAct == zero, true);
// If needed, advance to next funded offer.
if (errorCode != tesSUCCESS || !uOfferIndex)
if (resultCode != tesSUCCESS || !uOfferIndex)
{
// Error or out of offers.
break;
@@ -118,7 +119,7 @@ TER calcNodeDeliverRev (
: saTransferRate; // Transfer rate of issuer.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev:"
<< "nodeDeliverRev:"
<< " uOfrOwnerID="
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
<< " uOutAccountID="
@@ -138,7 +139,7 @@ TER calcNodeDeliverRev (
saRateMax = saOutFeeRate;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev: Set initial rate:"
<< "nodeDeliverRev: Set initial rate:"
<< " saRateMax=" << saRateMax
<< " saOutFeeRate=" << saOutFeeRate;
}
@@ -146,7 +147,7 @@ TER calcNodeDeliverRev (
{
// Offer exceeds initial rate.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev: Offer exceeds initial rate:"
<< "nodeDeliverRev: Offer exceeds initial rate:"
<< " saRateMax=" << saRateMax
<< " saOutFeeRate=" << saOutFeeRate;
@@ -167,7 +168,7 @@ TER calcNodeDeliverRev (
saRateMax = saOutFeeRate;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev: Reducing rate:"
<< "nodeDeliverRev: Reducing rate:"
<< " saRateMax=" << saRateMax;
}
@@ -190,7 +191,7 @@ TER calcNodeDeliverRev (
// Offer out with fees.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev:"
<< "nodeDeliverRev:"
<< " saOutReq=" << saOutReq
<< " saOutAct=" << saOutAct
<< " saTakerGets=" << saTakerGets
@@ -210,7 +211,7 @@ TER calcNodeDeliverRev (
saOutPassAct = std::min (saOutPassReq, fee);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev: Total exceeds fees:"
<< "nodeDeliverRev: Total exceeds fees:"
<< " saOutPassAct=" << saOutPassAct
<< " saOutPlusFees=" << saOutPlusFees
<< " saOfferFunds=" << saOfferFunds;
@@ -223,7 +224,7 @@ TER calcNodeDeliverRev (
STAmount saInPassAct;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev:"
<< "nodeDeliverRev:"
<< " outputFee=" << outputFee
<< " saInPassReq=" << saInPassReq
<< " saOfrRate=" << saOfrRate
@@ -234,7 +235,7 @@ TER calcNodeDeliverRev (
{
// After rounding did not want anything.
WriteLog (lsDEBUG, RippleCalc)
<< "calcNodeDeliverRev: micro offer is unfunded.";
<< "nodeDeliverRev: micro offer is unfunded.";
bEntryAdvance = true;
continue;
@@ -257,7 +258,7 @@ TER calcNodeDeliverRev (
saInPassAct = saInPassReq;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev: account --> OFFER --> ? :"
<< "nodeDeliverRev: account --> OFFER --> ? :"
<< " saInPassAct=" << saInPassAct;
}
else
@@ -265,7 +266,7 @@ TER calcNodeDeliverRev (
// offer --> OFFER --> ?
// Compute in previous offer node how much could come in.
errorCode = calcNodeDeliverRev (
resultCode = nodeDeliverRev (
rippleCalc,
nodeIndex - 1,
pathState,
@@ -275,11 +276,11 @@ TER calcNodeDeliverRev (
saInPassAct);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev: offer --> OFFER --> ? :"
<< "nodeDeliverRev: offer --> OFFER --> ? :"
<< " saInPassAct=" << saInPassAct;
}
if (errorCode != tesSUCCESS)
if (resultCode != tesSUCCESS)
break;
if (saInPassAct < saInPassReq)
@@ -293,7 +294,7 @@ TER calcNodeDeliverRev (
saOutPlusFees = std::min (saOfferFunds, outputFees);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev: adjusted:"
<< "nodeDeliverRev: adjusted:"
<< " saOutPassAct=" << saOutPassAct
<< " saOutPlusFees=" << saOutPlusFees;
}
@@ -313,10 +314,10 @@ TER calcNodeDeliverRev (
// visited. However, these deductions and adjustments are tenative.
//
// Must reset balances when going forward to perform actual transfers.
errorCode = rippleCalc.mActiveLedger.accountSend (
resultCode = rippleCalc.mActiveLedger.accountSend (
uOfrOwnerID, uCurIssuerID, saOutPassAct);
if (errorCode != tesSUCCESS)
if (resultCode != tesSUCCESS)
break;
// Adjust offer
@@ -326,12 +327,12 @@ TER calcNodeDeliverRev (
if (saTakerPaysNew < zero || saTakerGetsNew < zero)
{
WriteLog (lsWARNING, RippleCalc)
<< "calcNodeDeliverRev: NEGATIVE:"
<< "nodeDeliverRev: NEGATIVE:"
<< " saTakerPaysNew=" << saTakerPaysNew
<< " saTakerGetsNew=%s" << saTakerGetsNew;
// If mOpenLedger then ledger is not final, can vote no.
errorCode = rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
resultCode = rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
break;
}
@@ -344,7 +345,7 @@ TER calcNodeDeliverRev (
{
// Offer became unfunded.
WriteLog (lsDEBUG, RippleCalc)
<< "calcNodeDeliverRev: offer became unfunded.";
<< "nodeDeliverRev: offer became unfunded.";
bEntryAdvance = true; // XXX When don't we want to set advance?
}
@@ -359,24 +360,25 @@ TER calcNodeDeliverRev (
}
CondLog (saOutAct > saOutReq, lsWARNING, RippleCalc)
<< "calcNodeDeliverRev: TOO MUCH:"
<< "nodeDeliverRev: TOO MUCH:"
<< " saOutAct=" << saOutAct
<< " saOutReq=" << saOutReq;
assert (saOutAct <= saOutReq);
// XXX Perhaps need to check if partial is okay to relax this?
if (errorCode == tesSUCCESS && !saOutAct)
errorCode = tecPATH_DRY;
if (resultCode == tesSUCCESS && !saOutAct)
resultCode = tecPATH_DRY;
// Unable to meet request, consider path dry.
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeDeliverRev<"
<< "nodeDeliverRev<"
<< " saOutAct=" << saOutAct
<< " saOutReq=" << saOutReq
<< " saPrvDlvReq=" << saPrvDlvReq;
return errorCode;
return resultCode;
}
} // path
} // ripple

View File

@@ -22,6 +22,7 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// Called to drive the from the first offer node in a chain.
//
@@ -31,15 +32,15 @@ namespace ripple {
// - Transfer fees credited to issuer.
// - Payout to issuer or limbo.
// - Deliver is set without transfer fees.
TER calcNodeOfferFwd (
TER nodeOfferFwd (
RippleCalc& rippleCalc,
const unsigned int nodeIndex,
PathState& pathState,
const bool bMultiQuality
)
{
TER errorCode;
PathState::Node& previousNode = pathState.vpnNodes [nodeIndex - 1];
TER resultCode;
auto& previousNode = pathState.nodes() [nodeIndex - 1];
if (!!previousNode.uAccountID)
{
@@ -47,7 +48,7 @@ TER calcNodeOfferFwd (
STAmount saInAct;
STAmount saInFees;
errorCode = calcNodeDeliverFwd (
resultCode = nodeDeliverFwd (
rippleCalc,
nodeIndex,
pathState,
@@ -57,30 +58,30 @@ TER calcNodeOfferFwd (
saInAct,
saInFees);
assert (errorCode != tesSUCCESS ||
assert (resultCode != tesSUCCESS ||
previousNode.saFwdDeliver == saInAct + saInFees);
}
else
{
// Previous is an offer. Deliver has already been resolved.
errorCode = tesSUCCESS;
resultCode = tesSUCCESS;
}
return errorCode;
return resultCode;
}
// Called to drive from the last offer node in a chain.
TER calcNodeOfferRev (
TER nodeOfferRev (
RippleCalc& rippleCalc,
const unsigned int nodeIndex,
PathState& pathState,
const bool bMultiQuality)
{
TER errorCode;
TER resultCode;
PathState::Node& node = pathState.vpnNodes [nodeIndex];
PathState::Node& nextNode = pathState.vpnNodes [nodeIndex + 1];
auto& node = pathState.nodes() [nodeIndex];
auto& nextNode = pathState.nodes() [nodeIndex + 1];
if (!!nextNode.uAccountID)
{
@@ -88,11 +89,11 @@ TER calcNodeOfferRev (
STAmount saDeliverAct;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeOfferRev: OFFER --> account:"
<< "nodeOfferRev: OFFER --> account:"
<< " nodeIndex=" << nodeIndex
<< " saRevDeliver=" << node.saRevDeliver;
errorCode = calcNodeDeliverRev (
resultCode = nodeDeliverRev (
rippleCalc,
nodeIndex,
pathState,
@@ -106,13 +107,14 @@ TER calcNodeOfferRev (
else
{
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeOfferRev: OFFER --> offer: nodeIndex=" << nodeIndex;
<< "nodeOfferRev: OFFER --> offer: nodeIndex=" << nodeIndex;
// Next is an offer. Deliver has already been resolved.
errorCode = tesSUCCESS;
resultCode = tesSUCCESS;
}
return errorCode;
return resultCode;
}
} // ripple
} // path
} // ripple

View File

@@ -22,6 +22,7 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// Compute how much might flow for the node for the pass. Does not actually
// adjust balances.
@@ -45,7 +46,7 @@ namespace ripple {
// This routine is called one or two times for a node in a pass. If called once,
// it will work and set a rate. If called again, the new work must not worsen
// the previous rate.
void calcNodeRipple (
void nodeRipple (
RippleCalc& rippleCalc,
const std::uint32_t uQualityIn,
const std::uint32_t uQualityOut,
@@ -56,7 +57,7 @@ void calcNodeRipple (
std::uint64_t& uRateMax)
{
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeRipple>"
<< "nodeRipple>"
<< " uQualityIn=" << uQualityIn
<< " uQualityOut=" << uQualityOut
<< " saPrvReq=" << saPrvReq
@@ -75,7 +76,7 @@ void calcNodeRipple (
const STAmount saCur = saCurReq - saCurAct;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeRipple: "
<< "nodeRipple: "
<< " bPrvUnlimited=" << bPrvUnlimited
<< " saPrv=" << saPrv
<< " saCur=" << saCur;
@@ -83,7 +84,7 @@ void calcNodeRipple (
if (uQualityIn >= uQualityOut)
{
// No fee.
WriteLog (lsTRACE, RippleCalc) << "calcNodeRipple: No fees";
WriteLog (lsTRACE, RippleCalc) << "nodeRipple: No fees";
// Only process if we are not worsing previously processed.
if (!uRateMax || STAmount::uRateOne <= uRateMax)
@@ -109,7 +110,7 @@ void calcNodeRipple (
else
{
// Fee.
WriteLog (lsTRACE, RippleCalc) << "calcNodeRipple: Fee";
WriteLog (lsTRACE, RippleCalc) << "nodeRipple: Fee";
std::uint64_t uRate = STAmount::getRate (
STAmount (uQualityOut), STAmount (uQualityIn));
@@ -127,7 +128,7 @@ void calcNodeRipple (
someFee, uQualityIn, uCurrencyID, uCurIssuerID, true);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeRipple:"
<< "nodeRipple:"
<< " bPrvUnlimited=" << bPrvUnlimited
<< " saPrv=" << saPrv
<< " saCurIn=" << saCurIn;
@@ -138,7 +139,7 @@ void calcNodeRipple (
saCurAct += saCur;
saPrvAct += saCurIn;
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeRipple:3c:"
<< "nodeRipple:3c:"
<< " saCurReq=" << saCurReq
<< " saPrvAct=" << saPrvAct;
}
@@ -151,7 +152,7 @@ void calcNodeRipple (
STAmount saCurOut = STAmount::divRound (
someFee, uQualityOut, uCurrencyID, uCurIssuerID, true);
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeRipple:4: saCurReq=" << saCurReq;
<< "nodeRipple:4: saCurReq=" << saCurReq;
saCurAct += saCurOut;
saPrvAct = saPrvReq;
@@ -163,7 +164,7 @@ void calcNodeRipple (
}
WriteLog (lsTRACE, RippleCalc)
<< "calcNodeRipple<"
<< "nodeRipple<"
<< " uQualityIn=" << uQualityIn
<< " uQualityOut=" << uQualityOut
<< " saPrvReq=" << saPrvReq
@@ -172,4 +173,5 @@ void calcNodeRipple (
<< " saCurAct=" << saCurAct;
}
} // path
} // ripple

View File

@@ -24,7 +24,8 @@ namespace ripple {
typedef TER ErrorCode;
class CalcState {
class CalcState
{
public:
CalcState(
unsigned int nodeIndex, PathState& state, LedgerEntrySet& ledger, bool quality)
@@ -43,42 +44,6 @@ class CalcState {
void nextPath(LedgerEntrySet const& checkpoint) const;
private:
enum NodeCursor { FIRST, PREVIOUS, CURRENT, NEXT, LAST };
unsigned int index(NodeCursor cursor = CURRENT) const
{
switch (cursor) {
case FIRST:
return 0;
case PREVIOUS:
return nodeIndex_ ? nodeIndex_ - 1 : 0;
case CURRENT:
return nodeIndex_;
default:
break;
}
unsigned int size = pathState_.vpnNodes.size ();
auto last = size ? size - 1 : 0;
switch (cursor) {
case NEXT:
return std::min(nodeIndex_ + 1, last);
case LAST:
return last;
default:
bassert(false);
}
}
PathState::Node& node(NodeCursor cursor = CURRENT)
{
return pathState_.vpnNodes[index(cursor)];
}
CalcState state(NodeCursor cursor = CURRENT)
{
return CalcState(index(cursor), pathState_, ledger_, quality_);
}
LedgerEntrySet& ledger()
{
return ledger_;
@@ -96,16 +61,6 @@ class CalcState {
bool const quality_;
};
inline bool isAccount(PathState::Node const& node)
{
return is_bit_set (node.uFlags, STPathElement::typeAccount);
}
inline STAmount copyCurrencyAndIssuer(const STAmount& a)
{
return STAmount(a.getCurrency(), a.getIssuer());
}
} // ripple
#endif

View File

@@ -20,17 +20,29 @@
#ifndef RIPPLE_PATHS_CALCULATORS_H
#define RIPPLE_PATHS_CALCULATORS_H
#include <boost/log/trivial.hpp>
#include <ripple/module/app/paths/CalcState.h>
#include <ripple/module/app/paths/QualityConstraint.h>
#include <ripple/module/app/paths/RippleCalc.h>
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
// TODO(vfalco) What's the difference between a RippleState versus PathState?
//
struct RippleCalc
/** RippleCalc calculates the quality of a payment path.
Quality is the amount of input required to produce a given output along a
specified path. Most people would call this an exchange rate.
*/
class RippleCalc
{
public:
LedgerEntrySet& mActiveLedger;
bool mOpenLedger;
// First time working in reverse a funding source was mentioned. Source may
// only be used there.
//
// Map of currency, issuer to node index.
AccountCurrencyIssuerToNodeIndex mumSource;
@@ -44,38 +56,42 @@ struct RippleCalc
: mActiveLedger (activeLedger), mOpenLedger (bOpenLedger)
{
}
};
namespace path {
void pathNext (
RippleCalc&,
PathState& pathState, const bool bMultiQuality,
const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
TER calcNodeRev (
TER nodeRev (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER calcNodeFwd (
TER nodeFwd (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER calcNodeOfferRev (
TER nodeOfferRev (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER calcNodeOfferFwd (
TER nodeOfferFwd (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER calcNodeAccountRev (
TER nodeAccountRev (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER calcNodeAccountFwd (
TER nodeAccountFwd (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
TER calcNodeAdvance (
// To send money out of an account.
TER nodeAdvance (
RippleCalc&,
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality,
bool bReverse);
TER calcNodeDeliverRev (
// To deliver from an order book.
TER nodeDeliverRev (
RippleCalc&,
const unsigned int nodeIndex,
PathState& pathState,
@@ -84,7 +100,7 @@ TER calcNodeDeliverRev (
const STAmount& saOutReq,
STAmount& saOutAct);
TER calcNodeDeliverFwd (
TER nodeDeliverFwd (
RippleCalc&,
const unsigned int nodeIndex,
PathState& pathState,
@@ -94,16 +110,17 @@ TER calcNodeDeliverFwd (
STAmount& saInAct,
STAmount& saInFees);
void calcNodeRipple (
void nodeRipple (
RippleCalc&,
const std::uint32_t uQualityIn, const std::uint32_t uQualityOut,
const STAmount& saPrvReq, const STAmount& saCurReq,
STAmount& saPrvAct, STAmount& saCurAct, std::uint64_t& uRateMax);
void setCanonical (
STPathSet& spsDst, const std::vector<PathState::pointer>& vpsExpanded,
bool bKeepDefault);
const std::uint32_t uQualityIn,
const std::uint32_t uQualityOut,
const STAmount& saPrvReq,
const STAmount& saCurReq,
STAmount& saPrvAct,
STAmount& saCurAct,
std::uint64_t& uRateMax);
} // path
} // ripple
#endif

View File

@@ -0,0 +1,82 @@
//------------------------------------------------------------------------------
/*
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.
*/
//==============================================================================
namespace ripple {
namespace path {
// Compare the non-calculated fields.
bool Node::operator== (const Node& other) const
{
return other.uFlags == uFlags
&& other.uAccountID == uAccountID
&& other.uCurrencyID == uCurrencyID
&& other.uIssuerID == uIssuerID;
}
// This is for debugging not end users. Output names can be changed without warning.
Json::Value Node::getJson () const
{
Json::Value jvNode (Json::objectValue);
Json::Value jvFlags (Json::arrayValue);
jvNode["type"] = uFlags;
if (isAccount() || !!uAccountID)
jvFlags.append (!isAccount() == !!uAccountID ? "account" : "-account");
if (is_bit_set (uFlags, STPathElement::typeCurrency) || !!uCurrencyID)
jvFlags.append (!!is_bit_set (uFlags, STPathElement::typeCurrency) == !!uCurrencyID ? "currency" : "-currency");
if (is_bit_set (uFlags, STPathElement::typeIssuer) || !!uIssuerID)
jvFlags.append (!!is_bit_set (uFlags, STPathElement::typeIssuer) == !!uIssuerID ? "issuer" : "-issuer");
jvNode["flags"] = jvFlags;
if (!!uAccountID)
jvNode["account"] = RippleAddress::createHumanAccountID (uAccountID);
if (!!uCurrencyID)
jvNode["currency"] = STAmount::createHumanCurrency (uCurrencyID);
if (!!uIssuerID)
jvNode["issuer"] = RippleAddress::createHumanAccountID (uIssuerID);
if (saRevRedeem)
jvNode["rev_redeem"] = saRevRedeem.getFullText ();
if (saRevIssue)
jvNode["rev_issue"] = saRevIssue.getFullText ();
if (saRevDeliver)
jvNode["rev_deliver"] = saRevDeliver.getFullText ();
if (saFwdRedeem)
jvNode["fwd_redeem"] = saFwdRedeem.getFullText ();
if (saFwdIssue)
jvNode["fwd_issue"] = saFwdIssue.getFullText ();
if (saFwdDeliver)
jvNode["fwd_deliver"] = saFwdDeliver.getFullText ();
return jvNode;
}
} // path
} // ripple

View File

@@ -0,0 +1,95 @@
//------------------------------------------------------------------------------
/*
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_PATH_NODE_H
#define RIPPLE_APP_PATH_NODE_H
namespace ripple {
namespace path {
struct Node
{
typedef std::vector<Node> List;
inline bool isAccount() const
{
return is_bit_set (uFlags, STPathElement::typeAccount);
}
Json::Value getJson () const;
bool operator == (Node const&) const;
std::uint16_t uFlags; // --> From path.
uint160 uAccountID; // --> Accounts: Recieving/sending account.
uint160 uCurrencyID; // --> Accounts: Receive and send, Offers: send.
// --- For offer's next has currency out.
uint160 uIssuerID; // --> Currency's issuer
STAmount saTransferRate; // Transfer rate for uIssuerID.
// Computed by Reverse.
STAmount saRevRedeem; // <-- Amount to redeem to next.
STAmount saRevIssue; // <-- Amount to issue to next, limited by
// credit and outstanding IOUs. Issue
// isn't used by offers.
STAmount saRevDeliver; // <-- Amount to deliver to next regardless of fee.
// Computed by forward.
STAmount saFwdRedeem; // <-- Amount node will redeem to next.
STAmount saFwdIssue; // <-- Amount node will issue to next.
// Issue isn't used by offers.
STAmount saFwdDeliver; // <-- Amount to deliver to next regardless of fee.
// For offers:
STAmount saRateMax;
// The nodes are partitioned into a buckets called "directories".
//
// Each "directory" contains nodes with exactly the same "quality" (meaning
// the conversion rate between one corrency and the next).
//
// The "directories" are ordered in "increasing" "quality" value, which
// means that the first "directory" has the "best" (i.e. numerically least)
// "quality".
uint256 uDirectTip; // Current directory.
uint256 uDirectEnd; // Next order book.
bool bDirectAdvance; // Need to advance directory.
bool bDirectRestart; // Need to restart directory.
SLE::pointer sleDirectDir;
STAmount saOfrRate; // For correct ratio.
// PaymentNode
bool bEntryAdvance; // Need to advance entry.
unsigned int uEntry;
uint256 uOfferIndex;
SLE::pointer sleOffer;
uint160 uOfrOwnerID;
bool bFundsDirty; // Need to refresh saOfferFunds, saTakerPays, & saTakerGets.
STAmount saOfferFunds;
STAmount saTakerPays;
STAmount saTakerGets;
};
} // path
} // ripple
#endif

View File

@@ -23,6 +23,7 @@
#include <ripple/module/app/paths/Tuning.h>
namespace ripple {
namespace path {
// Calculate the next increment of a path.
//
@@ -38,28 +39,19 @@ void pathNext (
{
// The next state is what is available in preference order.
// This is calculated when referenced accounts changed.
const unsigned int lastNodeIndex = pathState.vpnNodes.size () - 1;
pathState.bConsumed = false;
// YYY This clearing should only be needed for nice logging.
pathState.saInPass = STAmount (
pathState.saInReq.getCurrency (), pathState.saInReq.getIssuer ());
pathState.saOutPass = STAmount (
pathState.saOutReq.getCurrency (), pathState.saOutReq.getIssuer ());
pathState.vUnfundedBecame.clear ();
pathState.umReverse.clear ();
const unsigned int lastNodeIndex = pathState.nodes().size () - 1;
pathState.clear();
WriteLog (lsTRACE, RippleCalc)
<< "pathNext: Path In: " << pathState.getJson ();
assert (pathState.vpnNodes.size () >= 2);
assert (pathState.nodes().size () >= 2);
lesCurrent = lesCheckpoint.duplicate (); // Restore from checkpoint.
for (unsigned int uIndex = pathState.vpnNodes.size (); uIndex--;)
for (unsigned int uIndex = pathState.nodes().size (); uIndex--;)
{
auto& node = pathState.vpnNodes[uIndex];
auto& node = pathState.nodes()[uIndex];
node.saRevRedeem.clear ();
node.saRevIssue.clear ();
@@ -67,40 +59,41 @@ void pathNext (
node.saFwdDeliver.clear ();
}
pathState.terStatus = calcNodeRev (rippleCalc, lastNodeIndex, pathState, bMultiQuality);
pathState.setStatus(nodeRev (rippleCalc, lastNodeIndex, pathState, bMultiQuality));
WriteLog (lsTRACE, RippleCalc)
<< "pathNext: Path after reverse: " << pathState.getJson ();
if (tesSUCCESS == pathState.terStatus)
if (tesSUCCESS == pathState.status())
{
// Do forward.
lesCurrent = lesCheckpoint.duplicate (); // Restore from checkpoint.
pathState.terStatus = calcNodeFwd (rippleCalc, 0, pathState, bMultiQuality);
pathState.setStatus(nodeFwd (rippleCalc, 0, pathState, bMultiQuality));
}
if (tesSUCCESS == pathState.terStatus)
if (tesSUCCESS == pathState.status())
{
CondLog (!pathState.saInPass || !pathState.saOutPass, lsDEBUG, RippleCalc)
<< "pathNext: Error calcNodeFwd reported success for nothing:"
<< " saOutPass=" << pathState.saOutPass
<< " saInPass=" << pathState.saInPass;
CondLog (!pathState.inPass() || !pathState.outPass(), lsDEBUG, RippleCalc)
<< "pathNext: Error nodeFwd reported success for nothing:"
<< " saOutPass=" << pathState.outPass()
<< " inPass()=" << pathState.inPass();
if (!pathState.saOutPass || !pathState.saInPass)
if (!pathState.outPass() || !pathState.inPass())
throw std::runtime_error ("Made no progress.");
// Calculate relative quality.
pathState.uQuality = STAmount::getRate (
pathState.saOutPass, pathState.saInPass);
pathState.setQuality(STAmount::getRate (
pathState.outPass(), pathState.inPass()));
WriteLog (lsTRACE, RippleCalc)
<< "pathNext: Path after forward: " << pathState.getJson ();
}
else
{
pathState.uQuality = 0;
pathState.setQuality(0);
}
}
} // path
} // ripple

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <tuple>
#include <boost/log/trivial.hpp>
#include <ripple/module/app/paths/Calculators.h>
@@ -417,34 +418,34 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
STPath extraPath;
if (valid && pf.findPaths (iLevel, 4, spsPaths, extraPath))
{
LedgerEntrySet lesSandbox (cache->getLedger (), tapNONE);
std::vector<PathState::pointer> vpsExpanded;
STAmount saMaxAmountAct;
STAmount saDstAmountAct;
STAmount saMaxAmount (currIssuer.first,
LedgerEntrySet lesSandbox (cache->getLedger (), tapNONE);
PathState::List pathStateList;
STAmount saMaxAmountAct;
STAmount saDstAmountAct;
STAmount saMaxAmount (currIssuer.first,
currIssuer.second.isNonZero () ? currIssuer.second :
(currIssuer.first.isZero () ? ACCOUNT_XRP : raSrcAccount.getAccountID ()), 1);
saMaxAmount.negate ();
m_journal.debug << iIdentifier << " Paths found, calling rippleCalc";
TER errorCode = rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
vpsExpanded, saMaxAmount, saDstAmount,
TER resultCode = path::rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
pathStateList, saMaxAmount, saDstAmount,
raDstAccount.getAccountID (), raSrcAccount.getAccountID (),
spsPaths, false, false, false, true);
if ((extraPath.size() > 0) && ((errorCode == terNO_LINE) || (errorCode == tecPATH_PARTIAL)))
if ((extraPath.size() > 0) && ((resultCode == terNO_LINE) || (resultCode == tecPATH_PARTIAL)))
{
m_journal.debug << iIdentifier << " Trying with an extra path element";
spsPaths.addPath(extraPath);
vpsExpanded.clear ();
errorCode = rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
vpsExpanded, saMaxAmount, saDstAmount,
pathStateList.clear ();
resultCode = path::rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
pathStateList, saMaxAmount, saDstAmount,
raDstAccount.getAccountID (), raSrcAccount.getAccountID (),
spsPaths, false, false, false, true);
m_journal.debug << iIdentifier << " Extra path element gives " << transHuman (errorCode);
m_journal.debug << iIdentifier << " Extra path element gives " << transHuman (resultCode);
}
if (errorCode == tesSUCCESS)
if (resultCode == tesSUCCESS)
{
Json::Value jvEntry (Json::objectValue);
jvEntry["source_amount"] = saMaxAmountAct.getJson (0);
@@ -454,7 +455,7 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
}
else
{
m_journal.debug << iIdentifier << " rippleCalc returns " << transHuman (errorCode);
m_journal.debug << iIdentifier << " rippleCalc returns " << transHuman (resultCode);
}
}
else

View File

@@ -50,10 +50,9 @@ public:
// VFALCO TODO Break the cyclic dependency on InfoSub
PathRequest (std::shared_ptr <InfoSub> const& subscriber,
int id, PathRequests&, beast::Journal journal);
~PathRequest ();
bool isValid (RippleLineCache::ref crCache);
bool isValid ();
bool isNew ();
bool needsUpdate (bool newOnly, LedgerIndex index);
@@ -68,6 +67,7 @@ public:
InfoSub::pointer getSubscriber ();
private:
bool isValid (RippleLineCache::ref crCache);
void setValid ();
void resetLevel (int level);
int parseJson (const Json::Value&, bool complete);

View File

@@ -19,14 +19,11 @@
namespace ripple {
// TODO:
// - Do automatic bridging via XRP.
//
// OPTIMIZE: When calculating path increment, note if increment consumes all liquidity. No need to revisit path in the future if
// all liquidity is used.
// OPTIMIZE: When calculating path increment, note if increment consumes all
// liquidity. No need to revisit path in the future if all liquidity is used.
//
struct RippleCalc; // for logging
class RippleCalc; // for logging
std::size_t hash_value (const AccountCurrencyIssuer& asValue)
{
@@ -34,68 +31,14 @@ std::size_t hash_value (const AccountCurrencyIssuer& asValue)
return beast::hardened_hash<AccountCurrencyIssuer>{seed}(asValue);
}
// Compare the non-calculated fields.
bool PathState::Node::operator== (const Node& pnOther) const
{
return pnOther.uFlags == uFlags
&& pnOther.uAccountID == uAccountID
&& pnOther.uCurrencyID == uCurrencyID
&& pnOther.uIssuerID == uIssuerID;
void PathState::clear() {
allLiquidityConsumed_ = false;
saInPass = zeroed (saInReq);
saOutPass = zeroed (saOutReq);
vUnfundedBecame.clear ();
umReverse.clear ();
}
// This is for debugging not end users. Output names can be changed without warning.
Json::Value PathState::Node::getJson () const
{
Json::Value jvNode (Json::objectValue);
Json::Value jvFlags (Json::arrayValue);
jvNode["type"] = uFlags;
if (is_bit_set (uFlags, STPathElement::typeAccount) || !!uAccountID)
jvFlags.append (!!is_bit_set (uFlags, STPathElement::typeAccount) == !!uAccountID ? "account" : "-account");
if (is_bit_set (uFlags, STPathElement::typeCurrency) || !!uCurrencyID)
jvFlags.append (!!is_bit_set (uFlags, STPathElement::typeCurrency) == !!uCurrencyID ? "currency" : "-currency");
if (is_bit_set (uFlags, STPathElement::typeIssuer) || !!uIssuerID)
jvFlags.append (!!is_bit_set (uFlags, STPathElement::typeIssuer) == !!uIssuerID ? "issuer" : "-issuer");
jvNode["flags"] = jvFlags;
if (!!uAccountID)
jvNode["account"] = RippleAddress::createHumanAccountID (uAccountID);
if (!!uCurrencyID)
jvNode["currency"] = STAmount::createHumanCurrency (uCurrencyID);
if (!!uIssuerID)
jvNode["issuer"] = RippleAddress::createHumanAccountID (uIssuerID);
if (saRevRedeem)
jvNode["rev_redeem"] = saRevRedeem.getFullText ();
if (saRevIssue)
jvNode["rev_issue"] = saRevIssue.getFullText ();
if (saRevDeliver)
jvNode["rev_deliver"] = saRevDeliver.getFullText ();
if (saFwdRedeem)
jvNode["fwd_redeem"] = saFwdRedeem.getFullText ();
if (saFwdIssue)
jvNode["fwd_issue"] = saFwdIssue.getFullText ();
if (saFwdDeliver)
jvNode["fwd_deliver"] = saFwdDeliver.getFullText ();
return jvNode;
}
//
// PathState implementation
//
// Return true, iff lhs has less priority than rhs.
bool PathState::lessPriority (PathState& lhs, PathState& rhs)
{
@@ -113,23 +56,28 @@ bool PathState::lessPriority (PathState& lhs, PathState& rhs)
// Make sure last path node delivers to uAccountID: uCurrencyID from uIssuerID.
//
// If the unadded next node as specified by arguments would not work as is, then add the necessary nodes so it would work.
// If the unadded next node as specified by arguments would not work as is, then
// add the necessary nodes so it would work.
// PRECONDITION: the PathState must be non-empty.
//
// Rules:
// - Currencies must be converted via an offer.
// - A node names it's output.
// - A ripple nodes output issuer must be the node's account or the next node's account.
// - Offers can only go directly to another offer if the currency and issuer are an exact match.
// - A node names its output.
// - A ripple nodes output issuer must be the node's account or the next node's
// account.
// - Offers can only go directly to another offer if the currency and issuer are
// an exact match.
// - Real issuers must be specified for non-XRP.
TER PathState::pushImply (
const uint160& uAccountID, // --> Delivering to this account.
const uint160& uCurrencyID, // --> Delivering this currency.
const uint160& uIssuerID) // --> Delivering this issuer.
{
const Node& previousNode = vpnNodes.back ();
TER errorCode = tesSUCCESS;
auto const& previousNode = nodes_.back ();
TER resultCode = tesSUCCESS;
WriteLog (lsTRACE, RippleCalc) << "pushImply>" <<
WriteLog (lsTRACE, RippleCalc) << "pushImply>" <<
" " << RippleAddress::createHumanAccountID (uAccountID) <<
" " << STAmount::createHumanCurrency (uCurrencyID) <<
" " << RippleAddress::createHumanAccountID (uIssuerID);
@@ -137,8 +85,14 @@ TER PathState::pushImply (
if (previousNode.uCurrencyID != uCurrencyID)
{
// Currency is different, need to convert via an offer.
// Currency is different, need to convert via an offer from an order
// book. ACCOUNT_XRP does double duty as signaling "this is an order
// book".
errorCode = pushNode ( // Offer.
// Corresponds to "Implies an offer directory" in the diagram, currently
// at https://docs.google.com/a/ripple.com/document/d/1b1RC8pKIgVZqUmjf9MW4IYxvzU7cBla4-pCSBbV4u8Q/edit
resultCode = pushNode ( // Offer.
!!uCurrencyID
? STPathElement::typeCurrency | STPathElement::typeIssuer
: STPathElement::typeCurrency,
@@ -147,48 +101,54 @@ TER PathState::pushImply (
uIssuerID);
}
const Node& pnBck = vpnNodes.back ();
auto const& pnBck = nodes_.back ();
// For ripple, non-XRP, ensure the issuer is on at least one side of the transaction.
if (errorCode == tesSUCCESS
if (resultCode == tesSUCCESS
&& !!uCurrencyID // Not XRP.
&& (pnBck.uAccountID != uIssuerID // Previous is not issuing own IOUs.
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
{
// Need to ripple through uIssuerID's account.
errorCode = pushNode (
// Case "Implies an another node: (pushImply)" in the document.
resultCode = pushNode (
STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer,
uIssuerID, // Intermediate account is the needed issuer.
uCurrencyID,
uIssuerID);
}
WriteLog (lsTRACE, RippleCalc) << "pushImply< : " << transToken (errorCode);
WriteLog (lsTRACE, RippleCalc) << "pushImply< : " << transToken (resultCode);
return errorCode;
return resultCode;
}
// Append a node and insert before it any implied nodes.
// Offers may go back to back.
// <-- errorCode: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, terNO_LINE, tecPATH_DRY
// Append a node, then create and insert before it any implied nodes. Order
// book nodes may go back to back.
//
// For each non-matching pair of IssuedCurrency, there's an order book.
//
// <-- resultCode: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, terNO_LINE, tecPATH_DRY
TER PathState::pushNode (
const int iType,
const uint160& uAccountID,
const uint160& uCurrencyID,
const uint160& uIssuerID)
const uint160& uAccountID, // If not specified, means an order book.
const uint160& uCurrencyID, // If not specified, default to previous.
const uint160& uIssuerID) // If not specified, default to previous.
{
Node node;
const bool bFirst = vpnNodes.empty ();
const Node& previousNode = bFirst ? Node () : vpnNodes.back ();
path::Node node;
const bool bFirst = nodes_.empty ();
auto const& previousNode = bFirst ? path::Node () : nodes_.back ();
// true, iff node is a ripple account. false, iff node is an offer node.
const bool bAccount = is_bit_set (iType, STPathElement::typeAccount);
// true, iff currency supplied.
// Currency is specified for the output of the current node.
const bool bCurrency = is_bit_set (iType, STPathElement::typeCurrency);
const bool bAccount = is_bit_set (iType, STPathElement::typeAccount);
// Is currency specified for the output of the current node?
const bool bCurrency = is_bit_set (iType, STPathElement::typeCurrency);
// Issuer is specified for the output of the current node.
const bool bIssuer = is_bit_set (iType, STPathElement::typeIssuer);
TER errorCode = tesSUCCESS;
const bool bIssuer = is_bit_set (iType, STPathElement::typeIssuer);
TER resultCode = tesSUCCESS;
WriteLog (lsTRACE, RippleCalc) << "pushNode> " <<
iType <<
@@ -196,32 +156,34 @@ TER PathState::pushNode (
" " << (bCurrency ? STAmount::createHumanCurrency (uCurrencyID) : "-") <<
"/" << (bIssuer ? RippleAddress::createHumanAccountID (uIssuerID) : "-");
node.uFlags = iType;
node.uCurrencyID = bCurrency ? uCurrencyID : previousNode.uCurrencyID;
node.uFlags = iType;
node.uCurrencyID = bCurrency ? uCurrencyID : previousNode.uCurrencyID;
if (iType & ~STPathElement::typeValidBits)
{
WriteLog (lsDEBUG, RippleCalc) << "pushNode: bad bits.";
errorCode = temBAD_PATH;
resultCode = temBAD_PATH;
}
else if (bIssuer && !node.uCurrencyID)
{
WriteLog (lsDEBUG, RippleCalc) << "pushNode: issuer specified for XRP.";
errorCode = temBAD_PATH;
resultCode = temBAD_PATH;
}
else if (bIssuer && !uIssuerID)
{
WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad issuer.";
errorCode = temBAD_PATH;
resultCode = temBAD_PATH;
}
else if (!bAccount && !bCurrency && !bIssuer)
{
// You can't default everything to the previous node as you would make
// no progress.
WriteLog (lsDEBUG, RippleCalc) << "pushNode: offer must specify at least currency or issuer.";
errorCode = temBAD_PATH;
resultCode = temBAD_PATH;
}
else if (bAccount)
{
@@ -230,11 +192,14 @@ TER PathState::pushNode (
node.uAccountID = uAccountID;
node.uIssuerID = bIssuer
? uIssuerID
: !!node.uCurrencyID
: !!node.uCurrencyID // Not XRP.
? uAccountID
: ACCOUNT_XRP;
// Zero value - for accounts.
node.saRevRedeem = STAmount (node.uCurrencyID, uAccountID);
node.saRevIssue = STAmount (node.uCurrencyID, uAccountID);
node.saRevIssue = node.saRevRedeem;
// For order books only - zero currency with the issuer ID.
node.saRevDeliver = STAmount (node.uCurrencyID, node.uIssuerID);
node.saFwdDeliver = node.saRevDeliver;
@@ -248,14 +213,14 @@ TER PathState::pushNode (
{
WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad account.";
errorCode = temBAD_PATH;
resultCode = temBAD_PATH;
}
else
{
// Add required intermediate nodes to deliver to current account.
WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for account.";
errorCode = pushImply (
resultCode = pushImply (
node.uAccountID, // Current account.
node.uCurrencyID, // Wanted currency.
!!node.uCurrencyID ? uAccountID : ACCOUNT_XRP); // Account as wanted issuer.
@@ -263,15 +228,17 @@ TER PathState::pushNode (
// Note: previousNode may no longer be the immediately previous node.
}
if (errorCode == tesSUCCESS && !vpnNodes.empty ())
if (resultCode == tesSUCCESS && !nodes_.empty ())
{
const Node& pnBck = vpnNodes.back ();
bool bBckAccount = is_bit_set (pnBck.uFlags, STPathElement::typeAccount);
auto const& pnBck = nodes_.back ();
bool bBckAccount = pnBck.isAccount();
if (bBckAccount)
{
SLE::pointer sleRippleState = lesEntries.entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (pnBck.uAccountID, node.uAccountID, pnBck.uCurrencyID));
// A "RippleState" means a balance betweeen two accounts for a
// specific currency.
if (!sleRippleState)
{
WriteLog (lsTRACE, RippleCalc) << "pushNode: No credit line between "
@@ -284,7 +251,7 @@ TER PathState::pushNode (
WriteLog (lsTRACE, RippleCalc) << getJson ();
errorCode = terNO_LINE;
resultCode = terNO_LINE;
}
else
{
@@ -297,13 +264,14 @@ TER PathState::pushNode (
<< "." ;
SLE::pointer sleBck = lesEntries.entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (pnBck.uAccountID));
// Is the source account the highest numbered account ID?
bool bHigh = pnBck.uAccountID > node.uAccountID;
if (!sleBck)
{
WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID (pnBck.uAccountID);
errorCode = terNO_ACCOUNT;
resultCode = terNO_ACCOUNT;
}
else if ((is_bit_set (sleBck->getFieldU32 (sfFlags), lsfRequireAuth)
&& !is_bit_set (sleRippleState->getFieldU32 (sfFlags), (bHigh ? lsfHighAuth : lsfLowAuth)))
@@ -311,10 +279,10 @@ TER PathState::pushNode (
{
WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from issuer without auth.";
errorCode = terNO_AUTH;
resultCode = terNO_AUTH;
}
if (errorCode == tesSUCCESS)
if (resultCode == tesSUCCESS)
{
STAmount saOwed = lesEntries.rippleOwed (node.uAccountID, pnBck.uAccountID, node.uCurrencyID);
STAmount saLimit;
@@ -327,16 +295,16 @@ TER PathState::pushNode (
" saOwed=" << saOwed <<
" saLimit=" << saLimit;
errorCode = tecPATH_DRY;
resultCode = tecPATH_DRY;
}
}
}
}
}
if (errorCode == tesSUCCESS)
if (resultCode == tesSUCCESS)
{
vpnNodes.push_back (node);
nodes_.push_back (node);
}
}
else
@@ -354,43 +322,52 @@ TER PathState::pushNode (
node.saRevDeliver = STAmount (node.uCurrencyID, node.uIssuerID);
node.saFwdDeliver = node.saRevDeliver;
if (!!node.uCurrencyID != !!node.uIssuerID)
if (node.uCurrencyID.isZero() != node.uIssuerID.isZero())
{
WriteLog (lsDEBUG, RippleCalc) <<
"pushNode: currency is inconsistent with issuer.";
errorCode = temBAD_PATH;
WriteLog (lsDEBUG, RippleCalc)
<< "pushNode: currency is inconsistent with issuer.";
resultCode = temBAD_PATH;
}
else if (previousNode.uCurrencyID == node.uCurrencyID &&
previousNode.uIssuerID == node.uIssuerID)
previousNode.uIssuerID == node.uIssuerID)
{
WriteLog (lsDEBUG, RippleCalc) <<
"pushNode: bad path: offer to same currency and issuer";
errorCode = temBAD_PATH;
}
else
{
resultCode = temBAD_PATH;
} else {
WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for offer.";
// Insert intermediary issuer account if needed.
errorCode = pushImply (
resultCode = pushImply (
ACCOUNT_XRP, // Rippling, but offers don't have an account.
previousNode.uCurrencyID,
previousNode.uIssuerID);
}
if (errorCode == tesSUCCESS)
if (resultCode == tesSUCCESS)
{
vpnNodes.push_back (node);
nodes_.push_back (node);
}
}
WriteLog (lsTRACE, RippleCalc) << "pushNode< : " << transToken (errorCode);
return errorCode;
WriteLog (lsTRACE, RippleCalc) << "pushNode< : " << transToken (resultCode);
return resultCode;
}
// Set to an expanded path.
// Set this object to be an expanded path from spSourcePath - take the implied
// nodes and makes them explicit. It also sanitizes the path.
//
// There are only two types of nodes: account nodes and order books nodes.
//
// You can infer some nodes automatically. If you're paying me bitstamp USD,
// then there must be an intermediate bitstamp node.
//
// If you have accounts A and B, and they're delivery currency issued by C, then
// there must be a node with account C in the middle.
//
// If you're paying USD and getting bitcoins, there has to be an order book in
// between.
// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, terNO_ACCOUNT, terNO_AUTH, or temBAD_PATH_LOOP
void PathState::setExpanded (
const LedgerEntrySet& lesSource,
@@ -492,7 +469,7 @@ void PathState::setExpanded (
}
}
const Node& previousNode = vpnNodes.back ();
auto const& previousNode = nodes_.back ();
if (tesSUCCESS == terStatus
&& !!uOutCurrencyID // Next is not XRP
@@ -534,11 +511,11 @@ void PathState::setExpanded (
// Look for first mention of source in nodes and detect loops.
// Note: The output is not allowed to be a source.
const unsigned int uNodes = vpnNodes.size ();
const unsigned int uNodes = nodes_.size ();
for (unsigned int nodeIndex = 0; tesSUCCESS == terStatus && nodeIndex != uNodes; ++nodeIndex)
{
const Node& node = vpnNodes[nodeIndex];
const auto& node = nodes_[nodeIndex];
AccountCurrencyIssuer aci(
node.uAccountID, node.uCurrencyID, node.uIssuerID);
@@ -561,220 +538,6 @@ void PathState::setExpanded (
": " << getJson ();
}
// Set to a canonical path.
// - Remove extra elements
// - Assumes path is expanded.
//
// We do canonicalization to:
// - Prevent waste in the ledger.
// - Allow longer paths to be specified than would otherwise be allowed.
//
// Optimization theory:
// - Can omit elements that the expansion routine derives.
// - Can pack some elements into other elements.
//
// Rules:
// - SendMax if not specified, defaults currency to send and if not sending XRP defaults issuer to sender.
// - All paths start with the sender account.
// - Currency and issuer is from SendMax.
// - All paths end with the destination account.
//
// Optimization:
// - An XRP output implies an offer node or destination node is next.
// - A change in currency implies an offer node.
// - A change in issuer...
void PathState::setCanonical (
const PathState& psExpanded
)
{
assert (false);
saInAct = psExpanded.saInAct;
saOutAct = psExpanded.saOutAct;
const uint160 uMaxCurrencyID = saInAct.getCurrency ();
const uint160 uMaxIssuerID = saInAct.getIssuer ();
const uint160 uOutCurrencyID = saOutAct.getCurrency ();
const uint160 uOutIssuerID = saOutAct.getIssuer ();
unsigned int nodeIndex = 0;
unsigned int uEnd = psExpanded.vpnNodes.size (); // The node, indexed by 0, not to include.
uint160 uDstAccountID = psExpanded.vpnNodes[uEnd].uAccountID; // FIXME: This can't be right
uint160 uAccountID = psExpanded.vpnNodes[0].uAccountID;
uint160 uCurrencyID = uMaxCurrencyID;
uint160 uIssuerID = uMaxIssuerID;
// Node 0 is a composite of the sending account and saInAct.
++nodeIndex; // skip node 0
// Last node is implied: Always skip last node
--uEnd; // skip last node
// saInAct
// - currency is always the same as vpnNodes[0].
#if 1
if (nodeIndex != uEnd && uMaxIssuerID != uAccountID)
{
// saInAct issuer is not the sender. This forces an implied node.
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: in diff: nodeIndex=%d uEnd=%d") % nodeIndex % uEnd);
// skip node 1
uIssuerID = psExpanded.vpnNodes[nodeIndex].uIssuerID;
++nodeIndex;
}
#else
if (nodeIndex != uEnd)
{
// Have another node
bool bKeep = false;
if (uMaxIssuerID != uAccountID)
{
}
if (uMaxCurrencyID) // Not sending XRP.
{
// Node 1 must be an account.
if (uMaxIssuerID != uAccountID)
{
// Node 1 is required to specify issuer.
bKeep = true;
}
else
{
// Node 1 must be an account
}
}
else
{
// Node 1 must be an order book.
bKeep = true;
}
if (bKeep)
{
uCurrencyID = psExpanded.vpnNodes[nodeIndex].uCurrencyID;
uIssuerID = psExpanded.vpnNodes[nodeIndex].uIssuerID;
++nodeIndex; // Keep it.
}
}
#endif
if (nodeIndex != uEnd && !!uOutCurrencyID && uOutIssuerID != uDstAccountID)
{
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out diff: nodeIndex=%d uEnd=%d") % nodeIndex % uEnd);
// The next to last node is saOutAct if an issuer different from receiver is supplied.
// The next to last node can be implied.
--uEnd;
}
const Node& pnEnd = psExpanded.vpnNodes[uEnd];
if (nodeIndex != uEnd
&& !pnEnd.uAccountID && pnEnd.uCurrencyID == uOutCurrencyID && pnEnd.uIssuerID == uOutIssuerID)
{
// The current end node is an offer converting to saOutAct's currency and issuer and can be implied.
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out offer: nodeIndex=%d uEnd=%d") % nodeIndex % uEnd);
--uEnd;
}
// Do not include uEnd.
for (; nodeIndex != uEnd; ++nodeIndex)
{
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: loop: nodeIndex=%d uEnd=%d") % nodeIndex % uEnd);
const Node& previousNode = psExpanded.vpnNodes[nodeIndex - 1];
const Node& node = psExpanded.vpnNodes[nodeIndex];
const Node& nextNode = psExpanded.vpnNodes[nodeIndex + 1];
const bool nodeIsAccount = is_bit_set (node.uFlags, STPathElement::typeAccount);
bool bSkip = false;
if (nodeIsAccount)
{
// Currently at an account.
// Output is non-XRP and issuer is account.
if (!!node.uCurrencyID && node.uIssuerID == node.uAccountID)
{
// Account issues itself.
// XXX Not good enough. Previous account must mention it.
bSkip = true;
}
}
else
{
// Currently at an offer.
const bool bPrvAccount = is_bit_set (previousNode.uFlags, STPathElement::typeAccount);
const bool bNxtAccount = is_bit_set (nextNode.uFlags, STPathElement::typeAccount);
if (bPrvAccount && bNxtAccount // Offer surrounded by accounts.
&& previousNode.uCurrencyID != nextNode.uCurrencyID)
{
// Offer can be implied by currency change.
// XXX What about issuer?
bSkip = true;
}
}
if (!bSkip)
{
// Copy node
Node pnNew;
bool bSetCurrency = (uCurrencyID != node.uCurrencyID);
// XXX What if we need the next account because we want to skip it?
bool bSetIssuer = !uCurrencyID && (uIssuerID != node.uIssuerID);
pnNew.uFlags = (nodeIsAccount ? STPathElement::typeAccount : 0)
| (bSetCurrency ? STPathElement::typeCurrency : 0)
| (bSetIssuer ? STPathElement::typeIssuer : 0);
if (nodeIsAccount)
pnNew.uAccountID = node.uAccountID;
if (bSetCurrency)
{
pnNew.uCurrencyID = node.uCurrencyID;
uCurrencyID = pnNew.uCurrencyID;
}
if (bSetIssuer)
pnNew.uIssuerID = node.uIssuerID;
// XXX ^^^ What about setting uIssuerID?
if (bSetCurrency && !uCurrencyID)
uIssuerID.zero ();
vpnNodes.push_back (pnNew);
}
}
WriteLog (lsDEBUG, RippleCalc) << "setCanonical:" <<
" in=" << STAmount::createHumanCurrency (uMaxCurrencyID) <<
"/" << RippleAddress::createHumanAccountID (uMaxIssuerID) <<
" out=" << STAmount::createHumanCurrency (uOutCurrencyID) <<
"/" << RippleAddress::createHumanAccountID (uOutIssuerID) <<
": " << getJson ();
}
/** Check if a sequence of three accounts violates the no ripple constrains
[first] -> [second] -> [third]
Disallowed if 'second' set no ripple on [first]->[second] and [second]->[third]
@@ -813,87 +576,84 @@ void PathState::checkNoRipple (
// Check a fully-expanded path to make sure it doesn't violate no-Ripple settings
void PathState::checkNoRipple (uint160 const& uDstAccountID, uint160 const& uSrcAccountID)
{
// There must be at least one node for there to be two consecutive ripple lines
if (vpnNodes.size() == 0)
if (nodes_.size() == 0)
return;
if (vpnNodes.size() == 1)
if (nodes_.size() == 1)
{
// There's just one link in the path
// We only need to check source-node-dest
if (is_bit_set (vpnNodes[0].uFlags, STPathElement::typeAccount) &&
(vpnNodes[0].uAccountID != uSrcAccountID) &&
(vpnNodes[0].uAccountID != uDstAccountID))
if (nodes_[0].isAccount() &&
(nodes_[0].uAccountID != uSrcAccountID) &&
(nodes_[0].uAccountID != uDstAccountID))
{
if (saInReq.getCurrency() != saOutReq.getCurrency())
terStatus = terNO_LINE;
else
checkNoRipple (uSrcAccountID, vpnNodes[0].uAccountID, uDstAccountID,
vpnNodes[0].uCurrencyID);
checkNoRipple (uSrcAccountID, nodes_[0].uAccountID, uDstAccountID,
nodes_[0].uCurrencyID);
}
return;
}
// Check source <-> first <-> second
if (is_bit_set (vpnNodes[0].uFlags, STPathElement::typeAccount) &&
is_bit_set (vpnNodes[1].uFlags, STPathElement::typeAccount) &&
(vpnNodes[0].uAccountID != uSrcAccountID))
if (nodes_[0].isAccount() &&
nodes_[1].isAccount() &&
(nodes_[0].uAccountID != uSrcAccountID))
{
if ((vpnNodes[0].uCurrencyID != vpnNodes[1].uCurrencyID))
if ((nodes_[0].uCurrencyID != nodes_[1].uCurrencyID))
{
terStatus = terNO_LINE;
return;
}
else
{
checkNoRipple (uSrcAccountID, vpnNodes[0].uAccountID, vpnNodes[1].uAccountID,
vpnNodes[0].uCurrencyID);
checkNoRipple (uSrcAccountID, nodes_[0].uAccountID, nodes_[1].uAccountID,
nodes_[0].uCurrencyID);
if (tesSUCCESS != terStatus)
return;
}
}
// Check second_from_last <-> last <-> destination
size_t s = vpnNodes.size() - 2;
if (is_bit_set (vpnNodes[s].uFlags, STPathElement::typeAccount) &&
is_bit_set (vpnNodes[s+1].uFlags, STPathElement::typeAccount) &&
(uDstAccountID != vpnNodes[s+1].uAccountID))
size_t s = nodes_.size() - 2;
if (nodes_[s].isAccount() &&
nodes_[s + 1].isAccount() &&
(uDstAccountID != nodes_[s+1].uAccountID))
{
if ((vpnNodes[s].uCurrencyID != vpnNodes[s+1].uCurrencyID))
if ((nodes_[s].uCurrencyID != nodes_[s+1].uCurrencyID))
{
terStatus = terNO_LINE;
return;
}
else
{
checkNoRipple (vpnNodes[s].uAccountID, vpnNodes[s+1].uAccountID, uDstAccountID,
vpnNodes[s].uCurrencyID);
checkNoRipple (nodes_[s].uAccountID, nodes_[s+1].uAccountID, uDstAccountID,
nodes_[s].uCurrencyID);
if (tesSUCCESS != terStatus)
return;
}
}
// Loop through all nodes that have a prior node and successor nodes
// These are the nodes whose no ripple constratints could be violated
for (int i = 1; i < (vpnNodes.size() - 1); ++i)
for (int i = 1; i < nodes_.size() - 1; ++i)
{
if (nodes_[i - 1].isAccount() &&
nodes_[i].isAccount() &&
nodes_[i + 1].isAccount())
{ // Two consecutive account-to-account links
if (is_bit_set (vpnNodes[i-1].uFlags, STPathElement::typeAccount) &&
is_bit_set (vpnNodes[i].uFlags, STPathElement::typeAccount) &&
is_bit_set (vpnNodes[i+1].uFlags, STPathElement::typeAccount))
{ // two consecutive account-to-account links
uint160 const& currencyID = vpnNodes[i].uCurrencyID;
if ((vpnNodes[i-1].uCurrencyID != currencyID) ||
(vpnNodes[i+1].uCurrencyID != currencyID))
uint160 const& currencyID = nodes_[i].uCurrencyID;
if ((nodes_[i-1].uCurrencyID != currencyID) ||
(nodes_[i+1].uCurrencyID != currencyID))
{
terStatus = temBAD_PATH;
return;
}
checkNoRipple (
vpnNodes[i-1].uAccountID, vpnNodes[i].uAccountID, vpnNodes[i+1].uAccountID,
nodes_[i-1].uAccountID, nodes_[i].uAccountID, nodes_[i+1].uAccountID,
currencyID);
if (terStatus != tesSUCCESS)
return;
@@ -908,10 +668,8 @@ Json::Value PathState::getJson () const
Json::Value jvPathState (Json::objectValue);
Json::Value jvNodes (Json::arrayValue);
BOOST_FOREACH (const Node & pnNode, vpnNodes)
{
for (auto const &pnNode: nodes_)
jvNodes.append (pnNode.getJson ());
}
jvPathState["status"] = terStatus;
jvPathState["index"] = mIndex;

View File

@@ -20,110 +20,34 @@
#ifndef RIPPLE_PATHSTATE_H
#define RIPPLE_PATHSTATE_H
#include <ripple/module/app/paths/Node.h>
#include <ripple/module/app/paths/Types.h>
namespace ripple {
// account id, currency id, issuer id :: node
typedef std::tuple <uint160, uint160, uint160> AccountCurrencyIssuer;
// Map of currency, issuer to node index.
typedef ripple::unordered_map <AccountCurrencyIssuer, unsigned int>
AccountCurrencyIssuerToNodeIndex;
extern std::size_t hash_value (const AccountCurrencyIssuer& asValue);
// Holds a path state under incremental application.
class PathState : public CountedObject <PathState>
{
public:
public:
typedef std::vector<uint256> OfferIndexList;
typedef std::vector<std::shared_ptr<PathState>> List;
static char const* getCountedObjectName () { return "PathState"; }
class Node
{
public:
bool operator == (Node const& pnOther) const;
Json::Value getJson () const;
public:
std::uint16_t uFlags; // --> From path.
uint160 uAccountID; // --> Accounts: Recieving/sending account.
uint160 uCurrencyID; // --> Accounts: Receive and send, Offers: send.
// --- For offer's next has currency out.
uint160 uIssuerID; // --> Currency's issuer
STAmount saTransferRate; // Transfer rate for uIssuerID.
// Computed by Reverse.
STAmount saRevRedeem; // <-- Amount to redeem to next.
STAmount saRevIssue; // <-- Amount to issue to next limited by credit and outstanding IOUs.
// Issue isn't used by offers.
STAmount saRevDeliver; // <-- Amount to deliver to next regardless of fee.
// Computed by forward.
STAmount saFwdRedeem; // <-- Amount node will redeem to next.
STAmount saFwdIssue; // <-- Amount node will issue to next.
// Issue isn't used by offers.
STAmount saFwdDeliver; // <-- Amount to deliver to next regardless of fee.
// For offers:
STAmount saRateMax;
// Directory
uint256 uDirectTip; // Current directory.
uint256 uDirectEnd; // Next order book.
bool bDirectAdvance; // Need to advance directory.
bool bDirectRestart; // Need to restart directory.
SLE::pointer sleDirectDir;
STAmount saOfrRate; // For correct ratio.
// PaymentNode
bool bEntryAdvance; // Need to advance entry.
unsigned int uEntry;
uint256 uOfferIndex;
SLE::pointer sleOffer;
uint160 uOfrOwnerID;
bool bFundsDirty; // Need to refresh saOfferFunds, saTakerPays, & saTakerGets.
STAmount saOfferFunds;
STAmount saTakerPays;
STAmount saTakerGets;
};
typedef std::shared_ptr<PathState> pointer;
typedef const std::shared_ptr<PathState>& ref;
PathState* setIndex (const int iIndex)
{
mIndex = iIndex;
return this;
}
int getIndex ()
{
return mIndex;
};
PathState (
const STAmount& saSend,
const STAmount& saSendMax)
PathState (const STAmount& saSend, const STAmount& saSendMax)
: saInReq (saSendMax)
, saOutReq (saSend)
, bConsumed (false)
, allLiquidityConsumed_ (false)
{
}
PathState (const PathState& psSrc,
bool bUnused)
PathState (const PathState& psSrc, bool bUnused)
: saInReq (psSrc.saInReq)
, saOutReq (psSrc.saOutReq)
, bConsumed (false)
, allLiquidityConsumed_ (false)
{
}
void clear();
void setExpanded (
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
@@ -131,23 +55,53 @@ public:
const uint160& uSenderID
);
void checkNoRipple (uint160 const& destinationAccountID, uint160 const& sourceAccountID);
void checkNoRipple (uint160 const&, uint160 const&, uint160 const&, uint160 const&);
path::Node::List& nodes() { return nodes_; }
void setCanonical (const PathState& psExpanded);
STAmount& inPass() { return saInPass; }
STAmount& outPass() { return saOutPass; }
const STAmount& outReq() const { return saOutReq; }
STAmount& inAct() { return saInAct; }
STAmount& outAct() { return saOutAct; }
const STAmount& inReq() const { return saInReq; }
AccountCurrencyIssuerToNodeIndex& forward() { return umForward; }
AccountCurrencyIssuerToNodeIndex& reverse() { return umReverse; }
Json::Value getJson () const;
static char const* getCountedObjectName () { return "PathState"; }
OfferIndexList& becameUnfunded() { return vUnfundedBecame; }
void setStatus(TER status) { terStatus = status; }
TER status() const { return terStatus; }
std::uint64_t quality() const { return uQuality; }
void setQuality (std::uint64_t q) { uQuality = q; }
bool allLiquidityConsumed() const { return allLiquidityConsumed_; }
void consumeAllLiqudity () { allLiquidityConsumed_ = true; }
void setIndex (int i) { mIndex = i; }
int index() const { return mIndex; }
void checkNoRipple (uint160 const& destinationAccountID,
uint160 const& sourceAccountID);
static bool lessPriority (PathState& lhs, PathState& rhs);
TER terStatus;
std::vector<Node> vpnNodes;
LedgerEntrySet& ledgerEntries() { return lesEntries; }
private:
void checkNoRipple (uint160 const&, uint160 const&, uint160 const&, uint160 const&);
TER terStatus;
path::Node::List nodes_;
// When processing, don't want to complicate directory walking with deletion.
std::vector<uint256> vUnfundedBecame; // Offers that became unfunded or were completely consumed.
// Offers that became unfunded or were completely consumed.
OfferIndexList vUnfundedBecame;
// First time scanning foward, as part of path contruction, a funding source was mentioned for accounts. Source may only be
// used there.
// First time scanning foward, as part of path construction, a funding
// source was mentioned for accounts. Source may only be used there.
AccountCurrencyIssuerToNodeIndex umForward;
// First time working in reverse a funding source was used.
@@ -164,11 +118,17 @@ public:
const STAmount& saOutReq; // --> Amount to send.
STAmount saOutAct; // --> Amount actually sent so far.
STAmount saOutPass; // <-- Amount actually sent.
bool bConsumed; // If true, use consumes full liquidity. False, may or may not.
private:
TER pushNode (const int iType, const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
TER pushImply (const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
// If true, all liquidity on this path has been consumed.
bool allLiquidityConsumed_;
TER pushNode (
const int iType, const uint160& uAccountID, const uint160& uCurrencyID,
const uint160& uIssuerID);
TER pushImply (
const uint160& uAccountID, const uint160& uCurrencyID,
const uint160& uIssuerID);
};
} // ripple

View File

@@ -1,4 +1,3 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
@@ -243,14 +242,14 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath)
try
{
STAmount saMaxAmountAct, saDstAmountAct;
std::vector<PathState::pointer> vpsExpanded;
PathState::List pathStateList;
LedgerEntrySet lesSandbox (mLedger, tapNONE);
TER result = rippleCalculate (
TER result = path::rippleCalculate (
lesSandbox,
saMaxAmountAct,
saDstAmountAct,
vpsExpanded,
pathStateList,
mSrcAmount,
mDstAmount,
mDstAccountID,
@@ -286,23 +285,23 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath)
{
STAmount saMaxAmountAct;
STAmount saDstAmountAct;
std::vector<PathState::pointer> vpsExpanded;
PathState::List pathStateList;
STPathSet spsPaths;
STPath& spCurrent = mCompletePaths[i];
spsPaths.addPath (spCurrent); // Just checking the current path.
TER errorCode;
TER resultCode;
try
{
LedgerEntrySet lesSandbox (mLedger, tapNONE);
errorCode = rippleCalculate (
resultCode = path::rippleCalculate (
lesSandbox,
saMaxAmountAct, // --> computed input
saDstAmountAct, // --> computed output
vpsExpanded,
pathStateList,
mSrcAmount, // --> amount to send max.
mDstAmount, // --> amount to deliver.
mDstAccountID,
@@ -317,13 +316,13 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath)
{
WriteLog (lsINFO, Pathfinder) << "findPaths: Caught throw: " << e.what ();
errorCode = tefEXCEPTION;
resultCode = tefEXCEPTION;
}
if (errorCode != tesSUCCESS)
if (resultCode != tesSUCCESS)
{
WriteLog (lsDEBUG, Pathfinder) <<
"findPaths: dropping: " << transToken (errorCode) <<
"findPaths: dropping: " << transToken (resultCode) <<
": " << spCurrent.getJson (0);
}
else if (saDstAmountAct < saMinDstAmount)

View File

@@ -0,0 +1,31 @@
//------------------------------------------------------------------------------
/*
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_QUALITY_CONSTRAINT_H
#define RIPPLE_QUALITY_CONSTRAINT_H
namespace ripple {
namespace path {
enum class QualityConstraint { SAME_OR_BETTER, UNCONSTRAINED };
} // path
} // ripple
#endif

View File

@@ -27,12 +27,18 @@
#include <ripple/module/app/paths/CalcNodeDeliverRev.cpp>
#include <ripple/module/app/paths/CalcNodeOffer.cpp>
#include <ripple/module/app/paths/CalcNodeRipple.cpp>
#include <ripple/module/app/paths/Node.cpp>
#include <ripple/module/app/paths/PathNext.cpp>
namespace ripple {
SETUP_LOG (RippleCalc)
namespace path {
// OPTIMIZE: When calculating path increment, note if increment consumes all
// liquidity. No need to revisit path in the future if all liquidity is used.
// <-- TER: Only returns tepPATH_PARTIAL if !bPartialPayment.
TER rippleCalculate (
// Compute paths vs this ledger entry set. Up to caller to actually apply
@@ -42,7 +48,14 @@ TER rippleCalculate (
STAmount& saMaxAmountAct, // <-- The computed input amount.
STAmount& saDstAmountAct, // <-- The computed output amount.
std::vector<PathState::pointer>& vpsExpanded,
// Expanded path with all the actual nodes in it.
// TODO(tom): does it put in default paths?
// A path starts with the source account, ends with the destination account
// and goes through other acounts or order books.
PathState::List& pathStateList,
// Issuer:
// XRP: ACCOUNT_XRP
// non-XRP: uSrcAccountID (for any issuer) or another account with trust
@@ -57,11 +70,15 @@ TER rippleCalculate (
const uint160& uDstAccountID,
const uint160& uSrcAccountID,
// A set of paths that are included in the transaction that we'll explore
// for liquidity.
const STPathSet& spsPaths,
const bool bPartialPayment,
const bool bLimitQuality,
const bool bNoRippleDirect,
const bool bStandAlone,
// True, not to delete unfundeds.
const bool bOpenLedger)
{
@@ -73,7 +90,7 @@ TER rippleCalculate (
<< " saMaxAmountReq:" << saMaxAmountReq
<< " saDstAmountReq:" << saDstAmountReq;
TER errorCode = temUNCERTAIN;
TER resultCode = temUNCERTAIN;
// YYY Might do basic checks on src and dst validity as per doPayment.
@@ -96,7 +113,7 @@ TER rippleCalculate (
// nodes.
// XXX Might also make a XRP bridge by default.
PathState::pointer pspDirect = std::make_shared<PathState> (
auto pspDirect = std::make_shared<PathState> (
saDstAmountReq, saMaxAmountReq);
if (!pspDirect)
@@ -105,29 +122,27 @@ TER rippleCalculate (
pspDirect->setExpanded (
activeLedger, STPath (), uDstAccountID, uSrcAccountID);
if (tesSUCCESS == pspDirect->terStatus)
if (tesSUCCESS == pspDirect->status())
pspDirect->checkNoRipple (uDstAccountID, uSrcAccountID);
pspDirect->setIndex (vpsExpanded.size ());
pspDirect->setIndex (pathStateList.size ());
WriteLog (lsDEBUG, RippleCalc)
<< "rippleCalc: Build direct:"
<< " status: " << transToken (pspDirect->terStatus);
<< " status: " << transToken (pspDirect->status());
// Return if malformed.
if (isTemMalformed (pspDirect->terStatus))
return pspDirect->terStatus;
if (isTemMalformed (pspDirect->status()))
return pspDirect->status();
if (tesSUCCESS == pspDirect->terStatus)
if (tesSUCCESS == pspDirect->status())
{
// Had a success.
errorCode = tesSUCCESS;
vpsExpanded.push_back (pspDirect);
resultCode = tesSUCCESS;
pathStateList.push_back (pspDirect);
}
else if (terNO_LINE != pspDirect->terStatus)
else if (terNO_LINE != pspDirect->status())
{
errorCode = pspDirect->terStatus;
resultCode = pspDirect->status();
}
}
@@ -137,7 +152,7 @@ TER rippleCalculate (
int iIndex = 0;
for (auto const& spPath: spsPaths)
{
PathState::pointer pspExpanded = std::make_shared<PathState> (
auto pspExpanded = std::make_shared<PathState> (
saDstAmountReq, saMaxAmountReq);
if (!pspExpanded)
@@ -155,43 +170,38 @@ TER rippleCalculate (
pspExpanded->setExpanded (
activeLedger, spPath, uDstAccountID, uSrcAccountID);
if (tesSUCCESS == pspExpanded->terStatus)
if (tesSUCCESS == pspExpanded->status())
pspExpanded->checkNoRipple (uDstAccountID, uSrcAccountID);
WriteLog (lsDEBUG, RippleCalc)
<< "rippleCalc:"
<< " Build path:" << ++iIndex
<< " status: " << transToken (pspExpanded->terStatus);
<< " status: " << transToken (pspExpanded->status());
// Return, if the path specification was malformed.
if (isTemMalformed (pspExpanded->terStatus))
return pspExpanded->terStatus;
if (isTemMalformed (pspExpanded->status()))
return pspExpanded->status();
if (tesSUCCESS == pspExpanded->terStatus)
if (tesSUCCESS == pspExpanded->status())
{
errorCode = tesSUCCESS; // Had a success.
resultCode = tesSUCCESS; // Had a success.
pspExpanded->setIndex (vpsExpanded.size ());
vpsExpanded.push_back (pspExpanded);
pspExpanded->setIndex (pathStateList.size ());
pathStateList.push_back (pspExpanded);
}
else if (terNO_LINE != pspExpanded->terStatus)
else if (terNO_LINE != pspExpanded->status())
{
errorCode = pspExpanded->terStatus;
resultCode = pspExpanded->status();
}
}
if (errorCode != tesSUCCESS)
return errorCode == temUNCERTAIN ? terNO_LINE : errorCode;
if (resultCode != tesSUCCESS)
return resultCode == temUNCERTAIN ? terNO_LINE : resultCode;
else
errorCode = temUNCERTAIN;
resultCode = temUNCERTAIN;
saMaxAmountAct = STAmount (
saMaxAmountReq.getCurrency (), saMaxAmountReq.getIssuer ());
saDstAmountAct = STAmount (
saDstAmountReq.getCurrency (), saDstAmountReq.getIssuer ());
// Checkpoint with just fees paid.
const LedgerEntrySet lesBase = activeLedger;
saMaxAmountAct = zeroed(saMaxAmountReq);
saDstAmountAct = zeroed(saDstAmountReq);
// When processing, we don't want to complicate directory walking with
// deletion.
@@ -203,7 +213,7 @@ TER rippleCalculate (
int iPass = 0;
while (errorCode == temUNCERTAIN)
while (resultCode == temUNCERTAIN)
{
int iBest = -1;
const LedgerEntrySet lesCheckpoint = activeLedger;
@@ -213,47 +223,47 @@ TER rippleCalculate (
bool bMultiQuality = false;
// Find the best path.
for (auto pspCur: vpsExpanded)
for (auto pspCur: pathStateList)
{
if (pspCur->uQuality)
if (pspCur->quality())
// Only do active paths.
{
bMultiQuality = 1 == vpsExpanded.size () - iDry;
bMultiQuality = 1 == pathStateList.size () - iDry;
// Computing the only non-dry path, compute multi-quality.
pspCur->saInAct = saMaxAmountAct;
pspCur->inAct() = saMaxAmountAct;
// Update to current amount processed.
pspCur->saOutAct = saDstAmountAct;
pspCur->outAct() = saDstAmountAct;
CondLog (pspCur->saInReq > zero
&& pspCur->saInAct >= pspCur->saInReq,
CondLog (pspCur->inReq() > zero
&& pspCur->inAct() >= pspCur->inReq(),
lsWARNING, RippleCalc)
<< "rippleCalc: DONE:"
<< " saInAct=" << pspCur->saInAct
<< " saInReq=" << pspCur->saInReq;
<< " inAct()=" << pspCur->inAct()
<< " inReq()=" << pspCur->inReq();
assert (pspCur->saInReq < zero ||
pspCur->saInAct < pspCur->saInReq); // Error if done.
assert (pspCur->inReq() < zero ||
pspCur->inAct() < pspCur->inReq()); // Error if done.
CondLog (pspCur->saOutAct >= pspCur->saOutReq,
CondLog (pspCur->outAct() >= pspCur->outReq(),
lsWARNING, RippleCalc)
<< "rippleCalc: ALREADY DONE:"
<< " saOutAct=" << pspCur->saOutAct
<< " saOutReq=%s" << pspCur->saOutReq;
<< " saOutAct=" << pspCur->outAct()
<< " saOutReq=%s" << pspCur->outReq();
assert (pspCur->saOutAct < pspCur->saOutReq);
assert (pspCur->outAct() < pspCur->outReq());
// Error if done, output met.
pathNext (rc, *pspCur, bMultiQuality, lesCheckpoint, rc.mActiveLedger);
// Compute increment.
WriteLog (lsDEBUG, RippleCalc)
<< "rippleCalc: AFTER:"
<< " mIndex=" << pspCur->mIndex
<< " uQuality=" << pspCur->uQuality
<< " rate=%s" << STAmount::saFromRate (pspCur->uQuality);
<< " mIndex=" << pspCur->index()
<< " uQuality=" << pspCur->quality()
<< " rate=%s" << STAmount::saFromRate (pspCur->quality());
if (!pspCur->uQuality)
if (!pspCur->quality())
{
// Path was dry.
@@ -261,40 +271,40 @@ TER rippleCalculate (
}
else
{
CondLog (!pspCur->saInPass || !pspCur->saOutPass,
CondLog (!pspCur->inPass() || !pspCur->outPass(),
lsDEBUG, RippleCalc)
<< "rippleCalc: better:"
<< " uQuality="
<< STAmount::saFromRate (pspCur->uQuality)
<< " saInPass=" << pspCur->saInPass
<< " saOutPass=" << pspCur->saOutPass;
<< STAmount::saFromRate (pspCur->quality())
<< " inPass()=" << pspCur->inPass()
<< " saOutPass=" << pspCur->outPass();
assert (!!pspCur->saInPass && !!pspCur->saOutPass);
assert (!!pspCur->inPass() && !!pspCur->outPass());
if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit)
if ((!bLimitQuality || pspCur->quality() <= uQualityLimit)
// Quality is not limited or increment has allowed
// quality.
&& (iBest < 0
// Best is not yet set.
|| PathState::lessPriority (*vpsExpanded[iBest],
|| PathState::lessPriority (*pathStateList[iBest],
*pspCur)))
// Current is better than set.
{
WriteLog (lsDEBUG, RippleCalc)
<< "rippleCalc: better:"
<< " mIndex=" << pspCur->mIndex
<< " uQuality=" << pspCur->uQuality
<< " mIndex=" << pspCur->index()
<< " uQuality=" << pspCur->quality()
<< " rate="
<< STAmount::saFromRate (pspCur->uQuality)
<< " saInPass=" << pspCur->saInPass
<< " saOutPass=" << pspCur->saOutPass;
<< STAmount::saFromRate (pspCur->quality())
<< " inPass()=" << pspCur->inPass()
<< " saOutPass=" << pspCur->outPass();
assert (activeLedger.isValid ());
activeLedger.swapWith (pspCur->lesEntries);
activeLedger.swapWith (pspCur->ledgerEntries());
// For the path, save ledger state.
activeLedger.invalidate ();
iBest = pspCur->getIndex ();
iBest = pspCur->index ();
}
}
}
@@ -306,58 +316,58 @@ TER rippleCalculate (
<< "rippleCalc: Summary:"
<< " Pass: " << ++iPass
<< " Dry: " << iDry
<< " Paths: " << vpsExpanded.size ();
for (auto pspCur: vpsExpanded)
<< " Paths: " << pathStateList.size ();
for (auto pspCur: pathStateList)
{
WriteLog (lsDEBUG, RippleCalc)
<< "rippleCalc: "
<< "Summary: " << pspCur->mIndex
<< "Summary: " << pspCur->index()
<< " rate: "
<< STAmount::saFromRate (pspCur->uQuality)
<< " quality:" << pspCur->uQuality
<< " best: " << (iBest == pspCur->getIndex ());
<< STAmount::saFromRate (pspCur->quality())
<< " quality:" << pspCur->quality()
<< " best: " << (iBest == pspCur->index ());
}
}
if (iBest >= 0)
{
// Apply best path.
PathState::pointer pspBest = vpsExpanded[iBest];
auto pspBest = pathStateList[iBest];
WriteLog (lsDEBUG, RippleCalc)
<< "rippleCalc: best:"
<< " uQuality="
<< STAmount::saFromRate (pspBest->uQuality)
<< " saInPass=" << pspBest->saInPass
<< " saOutPass=" << pspBest->saOutPass;
<< STAmount::saFromRate (pspBest->quality())
<< " inPass()=" << pspBest->inPass()
<< " saOutPass=" << pspBest->outPass();
// Record best pass' offers that became unfunded for deletion on
// success.
vuUnfundedBecame.insert (
vuUnfundedBecame.end (),
pspBest->vUnfundedBecame.begin (),
pspBest->vUnfundedBecame.end ());
pspBest->becameUnfunded().begin (),
pspBest->becameUnfunded().end ());
// Record best pass' LedgerEntrySet to build off of and potentially
// return.
assert (pspBest->lesEntries.isValid ());
activeLedger.swapWith (pspBest->lesEntries);
pspBest->lesEntries.invalidate ();
assert (pspBest->ledgerEntries().isValid ());
activeLedger.swapWith (pspBest->ledgerEntries());
pspBest->ledgerEntries().invalidate ();
saMaxAmountAct += pspBest->saInPass;
saDstAmountAct += pspBest->saOutPass;
saMaxAmountAct += pspBest->inPass();
saDstAmountAct += pspBest->outPass();
if (pspBest->bConsumed || bMultiQuality)
if (pspBest->allLiquidityConsumed() || bMultiQuality)
{
++iDry;
pspBest->uQuality = 0;
pspBest->setQuality(0);
}
if (saDstAmountAct == saDstAmountReq)
{
// Done. Delivered requested amount.
errorCode = tesSUCCESS;
resultCode = tesSUCCESS;
}
else if (saDstAmountAct > saDstAmountReq)
{
@@ -370,59 +380,59 @@ TER rippleCalculate (
assert (false);
}
else if (saMaxAmountAct != saMaxAmountReq &&
iDry != vpsExpanded.size ())
iDry != pathStateList.size ())
{
// Have not met requested amount or max send, try to do
// more. Prepare for next pass.
//
// Merge best pass' umReverse.
rc.mumSource.insert (
pspBest->umReverse.begin (), pspBest->umReverse.end ());
pspBest->reverse().begin (), pspBest->reverse().end ());
}
else if (!bPartialPayment)
{
// Have sent maximum allowed. Partial payment not allowed.
errorCode = tecPATH_PARTIAL;
resultCode = tecPATH_PARTIAL;
}
else
{
// Have sent maximum allowed. Partial payment allowed. Success.
errorCode = tesSUCCESS;
resultCode = tesSUCCESS;
}
}
// Not done and ran out of paths.
else if (!bPartialPayment)
{
// Partial payment not allowed.
errorCode = tecPATH_PARTIAL;
resultCode = tecPATH_PARTIAL;
}
// Partial payment ok.
else if (!saDstAmountAct)
{
// No payment at all.
errorCode = tecPATH_DRY;
resultCode = tecPATH_DRY;
}
else
{
errorCode = tesSUCCESS;
resultCode = tesSUCCESS;
}
}
if (!bStandAlone)
{
if (errorCode == tesSUCCESS)
if (resultCode == tesSUCCESS)
{
// Delete became unfunded offers.
for (auto const& uOfferIndex: vuUnfundedBecame)
{
if (errorCode == tesSUCCESS)
if (resultCode == tesSUCCESS)
{
WriteLog (lsDEBUG, RippleCalc)
<< "Became unfunded " << to_string (uOfferIndex);
errorCode = activeLedger.offerDelete (uOfferIndex);
resultCode = activeLedger.offerDelete (uOfferIndex);
}
}
}
@@ -430,16 +440,17 @@ TER rippleCalculate (
// Delete found unfunded offers.
for (auto const& uOfferIndex: rc.mUnfundedOffers)
{
if (errorCode == tesSUCCESS)
if (resultCode == tesSUCCESS)
{
WriteLog (lsDEBUG, RippleCalc)
<< "Delete unfunded " << to_string (uOfferIndex);
errorCode = activeLedger.offerDelete (uOfferIndex);
resultCode = activeLedger.offerDelete (uOfferIndex);
}
}
}
return errorCode;
return resultCode;
}
} // path
} // ripple

View File

@@ -21,6 +21,7 @@
#define RIPPLE_RIPPLECALC_H
namespace ripple {
namespace path {
/** Calculate the quality of a payment path.
@@ -32,7 +33,7 @@ TER rippleCalculate (
LedgerEntrySet& lesActive,
STAmount& saMaxAmountAct,
STAmount& saDstAmountAct,
std::vector<PathState::pointer>& vpsExpanded,
PathState::List& pathStateList,
const STAmount& saDstAmountReq,
const STAmount& saMaxAmountReq,
const uint160& uDstAccountID,
@@ -47,6 +48,7 @@ TER rippleCalculate (
const bool bOpenLedger = true
);
} // path
} // ripple
#endif

View File

@@ -0,0 +1,43 @@
//------------------------------------------------------------------------------
/*
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_TYPES_H
#define RIPPLE_TYPES_H
namespace ripple {
// account id, currency id, issuer id.
typedef std::tuple <uint160, uint160, uint160> AccountCurrencyIssuer;
std::size_t hash_value (const AccountCurrencyIssuer& asValue);
// Map of account, currency, issuer to node index.
typedef ripple::unordered_map <AccountCurrencyIssuer, unsigned int>
AccountCurrencyIssuerToNodeIndex;
/** Returns a copy of an STAmount with the same currency and issuer but the
amount set to zero. */
inline STAmount zeroed(const STAmount& a)
{
return STAmount(a.getCurrency(), a.getIssuer());
}
} // ripple
#endif

View File

@@ -232,7 +232,7 @@ TER Payment::doApply ()
// Copy paths into an editable class.
STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths);
std::vector<PathState::pointer> vpsExpanded;
PathState::List pathStateList;
STAmount maxSourceAmountAct;
STAmount saDstAmountAct;
@@ -243,11 +243,11 @@ TER Payment::doApply ()
terResult = openLedger && tooManyPaths
? telBAD_PATH_COUNT // Too many paths for proposed ledger.
: rippleCalculate (
: path::rippleCalculate (
mEngine->view (),
maxSourceAmountAct,
saDstAmountAct,
vpsExpanded, // Vector for saving expanded path.
pathStateList, // Vector for saving expanded path.
maxSourceAmount,
saDstAmount,
uDstAccountID,

View File

@@ -203,10 +203,10 @@ Json::Value RPCHandler::doRipplePathFind (
}
else
{
std::vector<PathState::pointer> vpsExpanded;
STAmount saMaxAmountAct;
STAmount saDstAmountAct;
STAmount saMaxAmount (
PathState::List pathStateList;
STAmount saMaxAmountAct;
STAmount saDstAmountAct;
STAmount saMaxAmount (
uSrcCurrencyID,
!!uSrcIssuerID
? uSrcIssuerID // Use specifed issuer.
@@ -219,11 +219,11 @@ Json::Value RPCHandler::doRipplePathFind (
LedgerEntrySet lesSandbox (lpLedger, tapNONE);
TER terResult =
rippleCalculate (
path::rippleCalculate (
lesSandbox,
saMaxAmountAct, // <--
saDstAmountAct, // <--
vpsExpanded, // <--
pathStateList, // <--
saMaxAmount, // --> Amount to send is unlimited to get an estimate.
saDstAmount, // --> Amount to deliver.
raDst.getAccountID (), // --> Account to deliver to.
@@ -236,7 +236,7 @@ Json::Value RPCHandler::doRipplePathFind (
true); // --> Stand alone mode, no point in deleting unfundeds.
// WriteLog (lsDEBUG, RPCHandler) << "ripple_path_find: PATHS IN: " << spsComputed.size() << " : " << spsComputed.getJson(0);
// WriteLog (lsDEBUG, RPCHandler) << "ripple_path_find: PATHS EXP: " << vpsExpanded.size();
// WriteLog (lsDEBUG, RPCHandler) << "ripple_path_find: PATHS EXP: " << pathStateList.size();
WriteLog (lsWARNING, RPCHandler)
<< boost::str (boost::format ("ripple_path_find: saMaxAmount=%s saDstAmount=%s saMaxAmountAct=%s saDstAmountAct=%s")
@@ -249,10 +249,10 @@ Json::Value RPCHandler::doRipplePathFind (
{
WriteLog (lsDEBUG, PathRequest) << "Trying with an extra path element";
spsComputed.addPath(extraPath);
vpsExpanded.clear ();
pathStateList.clear ();
lesSandbox.clear ();
terResult = rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
vpsExpanded, saMaxAmount, saDstAmount,
terResult = path::rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
pathStateList, saMaxAmount, saDstAmount,
raDst.getAccountID (), raSrc.getAccountID (),
spsComputed, false, false, false, true);
WriteLog (lsDEBUG, PathRequest) << "Extra path element gives " << transHuman (terResult);
@@ -266,10 +266,9 @@ Json::Value RPCHandler::doRipplePathFind (
// Reuse the expanded as it would need to be calcuated anyway to produce the canonical.
// (At least unless we make a direct canonical.)
// RippleCalc::setCanonical(spsCanonical, vpsExpanded, false);
jvEntry["source_amount"] = saMaxAmountAct.getJson (0);
// jvEntry["paths_expanded"] = vpsExpanded.getJson(0);
// jvEntry["paths_expanded"] = pathStateList.getJson(0);
jvEntry["paths_canonical"] = Json::arrayValue; // spsCanonical.getJson(0);
jvEntry["paths_computed"] = spsComputed.getJson (0);