Work toward ripple_path_find.

This commit is contained in:
Arthur Britto
2012-11-28 19:19:39 -08:00
parent 11d0e93cd8
commit 2268989609
5 changed files with 381 additions and 228 deletions

View File

@@ -115,6 +115,7 @@ TER PaymentTransactor::doApply()
// Ripple payment // Ripple payment
STPathSet spsPaths = mTxn.getFieldPathSet(sfPaths); STPathSet spsPaths = mTxn.getFieldPathSet(sfPaths);
std::vector<PathState::pointer> vpsExpanded;
STAmount saMaxAmountAct; STAmount saMaxAmountAct;
STAmount saDstAmountAct; STAmount saDstAmountAct;
@@ -124,6 +125,7 @@ TER PaymentTransactor::doApply()
mEngine->getNodes(), mEngine->getNodes(),
saMaxAmountAct, saMaxAmountAct,
saDstAmountAct, saDstAmountAct,
vpsExpanded,
saMaxAmount, saMaxAmount,
saDstAmount, saDstAmount,
uDstAccountID, uDstAccountID,

View File

@@ -779,13 +779,14 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
return rpcError(rpcINVALID_PARAMS); return rpcError(rpcINVALID_PARAMS);
} }
STPathSet spsPaths; STPathSet spsComputed;
std::vector<PathState::pointer> vpsExpanded;
Pathfinder pf(raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount); Pathfinder pf(raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount);
pf.findPaths(5, 1, spsPaths, true); pf.findPaths(5, 1, spsComputed, true);
if (spsPaths.isEmpty()) if (spsComputed.isEmpty())
{ {
cLog(lsDEBUG) << "ripple_path_find: No paths found."; cLog(lsDEBUG) << "ripple_path_find: No paths found.";
} }
@@ -804,9 +805,9 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
saMaxAmount.negate(); saMaxAmount.negate();
// Strip empty/default path. // Strip empty/default path.
if (1 == spsPaths.size() && !spsPaths.begin()->size()) if (1 == spsComputed.size() && !spsComputed.begin()->size())
{ {
spsPaths.clear(); spsComputed.clear();
} }
TER terResult = TER terResult =
@@ -814,11 +815,12 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
lesSnapshot, lesSnapshot,
saMaxAmountAct, saMaxAmountAct,
saDstAmountAct, saDstAmountAct,
vpsExpanded,
saMaxAmount, // --> Amount to send is unlimited to get an estimate. saMaxAmount, // --> Amount to send is unlimited to get an estimate.
saDstAmount, // --> Amount to deliver. saDstAmount, // --> Amount to deliver.
raDst.getAccountID(), // --> Account to deliver to. raDst.getAccountID(), // --> Account to deliver to.
raSrc.getAccountID(), // --> Account sending from. raSrc.getAccountID(), // --> Account sending from.
spsPaths, // --> Path set. spsComputed, // --> Path set.
false, // --> Don't allow partial payment. This is for normal fill or kill payments. false, // --> Don't allow partial payment. This is for normal fill or kill payments.
// Must achive delivery goal. // Must achive delivery goal.
false, // --> Don't limit quality. Average quality is wanted for normal payments. false, // --> Don't limit quality. Average quality is wanted for normal payments.
@@ -836,8 +838,13 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
{ {
Json::Value jvEntry(Json::objectValue); Json::Value jvEntry(Json::objectValue);
STPathSet spsCanonical;
RippleCalc::setCanonical(spsCanonical, vpsExpanded);
jvEntry["source_amount"] = saMaxAmountAct.getJson(0); jvEntry["source_amount"] = saMaxAmountAct.getJson(0);
jvEntry["paths_expanded"] = spsPaths.getJson(0); // jvEntry["paths_expanded"] = spsExpanded.getJson(0);
jvEntry["paths_canonical"] = spsCanonical.getJson(0);
jvArray.append(jvEntry); jvArray.append(jvEntry);
} }
@@ -852,7 +859,7 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
<< boost::str(boost::format("ripple_path_find: %s %s %s") << boost::str(boost::format("ripple_path_find: %s %s %s")
% strToken % strToken
% strHuman % strHuman
% spsPaths.getJson(0)); % spsComputed.getJson(0));
} }
} }
} }
@@ -860,6 +867,10 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
jvResult["alternatives"] = jvArray; jvResult["alternatives"] = jvArray;
} }
cLog(lsDEBUG)
<< boost::str(boost::format("ripple_path_find< %s")
% jvResult);
return jvResult; return jvResult;
} }

View File

@@ -32,17 +32,17 @@ std::size_t hash_value(const aciSource& asValue)
// //
// Return true, iff lhs has less priority than rhs. // Return true, iff lhs has less priority than rhs.
bool PathState::lessPriority(PathState::ref lhs, PathState::ref rhs) bool PathState::lessPriority(PathState& lhs, PathState& rhs)
{ {
if (lhs->uQuality != rhs->uQuality) if (lhs.uQuality != rhs.uQuality)
return lhs->uQuality > rhs->uQuality; // Bigger is worse. return lhs.uQuality > rhs.uQuality; // Bigger is worse.
// Best quanity is second rank. // Best quanity is second rank.
if (lhs->saOutPass != rhs->saOutPass) if (lhs.saOutPass != rhs.saOutPass)
return lhs->saOutPass < rhs->saOutPass; // Smaller is worse. return lhs.saOutPass < rhs.saOutPass; // Smaller is worse.
// Path index is third rank. // Path index is third rank.
return lhs->mIndex > rhs->mIndex; // Bigger is worse. return lhs.mIndex > rhs.mIndex; // Bigger is worse.
} }
// Make sure last path node delivers to uAccountID: uCurrencyID from uIssuerID. // Make sure last path node delivers to uAccountID: uCurrencyID from uIssuerID.
@@ -420,7 +420,7 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=
} }
} }
cLog(lsINFO) << boost::str(boost::format("PathState: in=%s/%s out=%s/%s %s") cLog(lsDEBUG) << boost::str(boost::format("PathState: in=%s/%s out=%s/%s %s")
% STAmount::createHumanCurrency(uMaxCurrencyID) % STAmount::createHumanCurrency(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uMaxIssuerID) % RippleAddress::createHumanAccountID(uMaxIssuerID)
% STAmount::createHumanCurrency(uOutCurrencyID) % STAmount::createHumanCurrency(uOutCurrencyID)
@@ -429,10 +429,159 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=
} }
void PathState::setCanonical( void PathState::setCanonical(
PathState::ref pspExpanded const PathState& psExpanded
) )
{ {
; 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 uNode = 0;
unsigned int uEnd = psExpanded.vpnNodes.size(); // The node, indexed by 0, not to include.
uint160 uDstAccountID = psExpanded.vpnNodes[uEnd].uAccountID;
uint160 uAccountID = psExpanded.vpnNodes[0].uAccountID;
uint160 uCurrencyID = uMaxCurrencyID;
uint160 uIssuerID = uMaxIssuerID;
// Node 0 is a composit of the sending account and saInAct.
++uNode; // 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 (uMaxIssuerID != uAccountID);
{
// saInAct issuer is not the sender. This forces an implied node.
// skip node 1
uIssuerID = psExpanded.vpnNodes[uNode].uIssuerID;
++uNode;
}
if (uNode != uEnd && !!uOutCurrencyID && uOutIssuerID != uDstAccountID)
{
// 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 PaymentNode& pnEnd = psExpanded.vpnNodes[uEnd];
if (uNode != 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.
--uEnd;
}
// Do not include uEnd.
for (; uNode != uEnd; ++uNode)
{
const PaymentNode& pnPrv = psExpanded.vpnNodes[uNode-1];
const PaymentNode& pnCur = psExpanded.vpnNodes[uNode];
const PaymentNode& pnNxt = psExpanded.vpnNodes[uNode+1];
const bool bPrvAccount = isSetBit(pnPrv.uFlags, STPathElement::typeAccount);
const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount);
const bool bNxtAccount = isSetBit(pnNxt.uFlags, STPathElement::typeAccount);
bool bSkip = false;
if (bCurAccount)
{
// Currently at an account.
if (!!pnCur.uCurrencyID && pnCur.uIssuerID == pnCur.uAccountID)
{
// Account issues itself.
bSkip = true;
}
}
else
{
// Currently at an offer.
if (bPrvAccount && bNxtAccount // Offer surrounded by accounts.
&& pnPrv.uCurrencyID != pnNxt.uCurrencyID)
{
// Offer can be implied by currenct change.
bSkip = true;
}
}
if (!bSkip)
{
// Copy node
PaymentNode pnNew;
bool bSetAccount = bCurAccount;
bool bSetCurrency = uCurrencyID != pnCur.uCurrencyID;
bool bSetIssuer = !uCurrencyID && uIssuerID != pnCur.uIssuerID;
pnNew.uFlags = (bSetAccount ? STPathElement::typeAccount : 0)
| (bSetCurrency ? STPathElement::typeCurrency : 0)
| (bSetIssuer ? STPathElement::typeIssuer : 0);
if (bSetAccount)
pnNew.uAccountID = pnCur.uAccountID;
if (bSetCurrency)
{
pnNew.uCurrencyID = pnCur.uCurrencyID;
uCurrencyID = pnNew.uCurrencyID;
}
if (bSetIssuer)
pnNew.uIssuerID = pnCur.uIssuerID;
if (bSetCurrency && !uCurrencyID)
uIssuerID.zero();
vpnNodes.push_back(pnNew);
}
}
cLog(lsDEBUG) << boost::str(boost::format("setCanonical: in=%s/%s out=%s/%s %s")
% STAmount::createHumanCurrency(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uMaxIssuerID)
% STAmount::createHumanCurrency(uOutCurrencyID)
% RippleAddress::createHumanAccountID(uOutIssuerID)
% getJson());
}
void RippleCalc::setCanonical(STPathSet& spsDst, const std::vector<PathState::pointer>& vpsExpanded)
{
BOOST_FOREACH(PathState::ref pspExpanded, vpsExpanded)
{
PathState psCanonical(*pspExpanded, false);
psCanonical.setCanonical(*pspExpanded);
STPath spCanonical;
BOOST_FOREACH(const PaymentNode& pnElem, psCanonical.vpnNodes)
{
STPathElement speElem(pnElem.uFlags, pnElem.uAccountID, pnElem.uCurrencyID, pnElem.uIssuerID);
spCanonical.addElement(speElem);
}
spsDst.addPath(spCanonical);
}
} }
Json::Value PathState::getJson() const Json::Value PathState::getJson() const
@@ -460,22 +609,22 @@ Json::Value PathState::getJson() const
if (!!pnNode.uIssuerID) if (!!pnNode.uIssuerID)
jvNode["issuer"] = RippleAddress::createHumanAccountID(pnNode.uIssuerID); jvNode["issuer"] = RippleAddress::createHumanAccountID(pnNode.uIssuerID);
// if (pnNode.saRevRedeem) if (pnNode.saRevRedeem)
jvNode["rev_redeem"] = pnNode.saRevRedeem.getFullText(); jvNode["rev_redeem"] = pnNode.saRevRedeem.getFullText();
// if (pnNode.saRevIssue) if (pnNode.saRevIssue)
jvNode["rev_issue"] = pnNode.saRevIssue.getFullText(); jvNode["rev_issue"] = pnNode.saRevIssue.getFullText();
// if (pnNode.saRevDeliver) if (pnNode.saRevDeliver)
jvNode["rev_deliver"] = pnNode.saRevDeliver.getFullText(); jvNode["rev_deliver"] = pnNode.saRevDeliver.getFullText();
// if (pnNode.saFwdRedeem) if (pnNode.saFwdRedeem)
jvNode["fwd_redeem"] = pnNode.saFwdRedeem.getFullText(); jvNode["fwd_redeem"] = pnNode.saFwdRedeem.getFullText();
// if (pnNode.saFwdIssue) if (pnNode.saFwdIssue)
jvNode["fwd_issue"] = pnNode.saFwdIssue.getFullText(); jvNode["fwd_issue"] = pnNode.saFwdIssue.getFullText();
// if (pnNode.saFwdDeliver) if (pnNode.saFwdDeliver)
jvNode["fwd_deliver"] = pnNode.saFwdDeliver.getFullText(); jvNode["fwd_deliver"] = pnNode.saFwdDeliver.getFullText();
jvNodes.append(jvNode); jvNodes.append(jvNode);
@@ -519,12 +668,12 @@ Json::Value PathState::getJson() const
// <-- uOfferIndex : 0=end of list. // <-- uOfferIndex : 0=end of list.
TER RippleCalc::calcNodeAdvance( TER RippleCalc::calcNodeAdvance(
const unsigned int uNode, // 0 < uNode < uLast const unsigned int uNode, // 0 < uNode < uLast
PathState::ref pspCur, PathState& psCur,
const bool bMultiQuality, const bool bMultiQuality,
const bool bReverse) const bool bReverse)
{ {
PaymentNode& pnPrv = pspCur->vpnNodes[uNode-1]; PaymentNode& pnPrv = psCur.vpnNodes[uNode-1];
PaymentNode& pnCur = pspCur->vpnNodes[uNode]; PaymentNode& pnCur = psCur.vpnNodes[uNode];
const uint160& uPrvCurrencyID = pnPrv.uCurrencyID; const uint160& uPrvCurrencyID = pnPrv.uCurrencyID;
const uint160& uPrvIssuerID = pnPrv.uIssuerID; const uint160& uPrvIssuerID = pnPrv.uIssuerID;
@@ -668,8 +817,8 @@ TER RippleCalc::calcNodeAdvance(
// XXX This can get called multiple times for same source in a row, caching result would be nice. // XXX This can get called multiple times for same source in a row, caching result would be nice.
// XXX Going forward could we fund something with a worse quality which was previously skipped? Might need to check // XXX Going forward could we fund something with a worse quality which was previously skipped? Might need to check
// quality. // quality.
curIssuerNodeConstIterator itForward = pspCur->umForward.find(asLine); curIssuerNodeConstIterator itForward = psCur.umForward.find(asLine);
const bool bFoundForward = itForward != pspCur->umForward.end(); const bool bFoundForward = itForward != psCur.umForward.end();
if (bFoundForward && itForward->second != uNode) if (bFoundForward && itForward->second != uNode)
{ {
@@ -692,8 +841,8 @@ TER RippleCalc::calcNodeAdvance(
continue; continue;
} }
curIssuerNodeConstIterator itReverse = pspCur->umReverse.find(asLine); curIssuerNodeConstIterator itReverse = psCur.umReverse.find(asLine);
bool bFoundReverse = itReverse != pspCur->umReverse.end(); bool bFoundReverse = itReverse != psCur.umReverse.end();
if (bFoundReverse && itReverse->second != uNode) if (bFoundReverse && itReverse->second != uNode)
{ {
@@ -735,7 +884,7 @@ TER RippleCalc::calcNodeAdvance(
% STAmount::createHumanCurrency(uCurCurrencyID) % STAmount::createHumanCurrency(uCurCurrencyID)
% RippleAddress::createHumanAccountID(uCurIssuerID)); % RippleAddress::createHumanAccountID(uCurIssuerID));
pspCur->umReverse.insert(std::make_pair(asLine, uNode)); psCur.umReverse.insert(std::make_pair(asLine, uNode));
} }
bFundsDirty = false; bFundsDirty = false;
@@ -761,7 +910,7 @@ TER RippleCalc::calcNodeAdvance(
// till request is satisified while we the rate does not increase past the initial rate. // till request is satisified while we the rate does not increase past the initial rate.
TER RippleCalc::calcNodeDeliverRev( TER RippleCalc::calcNodeDeliverRev(
const unsigned int uNode, // 0 < uNode < uLast const unsigned int uNode, // 0 < uNode < uLast
PathState::ref pspCur, PathState& psCur,
const bool bMultiQuality, const bool bMultiQuality,
const uint160& uOutAccountID, // --> Output owner's account. const uint160& uOutAccountID, // --> Output owner's account.
const STAmount& saOutReq, // --> Funds wanted. const STAmount& saOutReq, // --> Funds wanted.
@@ -769,8 +918,8 @@ TER RippleCalc::calcNodeDeliverRev(
{ {
TER terResult = tesSUCCESS; TER terResult = tesSUCCESS;
PaymentNode& pnPrv = pspCur->vpnNodes[uNode-1]; PaymentNode& pnPrv = psCur.vpnNodes[uNode-1];
PaymentNode& pnCur = pspCur->vpnNodes[uNode]; PaymentNode& pnCur = psCur.vpnNodes[uNode];
const uint160& uCurIssuerID = pnCur.uIssuerID; const uint160& uCurIssuerID = pnCur.uIssuerID;
const uint160& uPrvAccountID = pnPrv.uAccountID; const uint160& uPrvAccountID = pnPrv.uAccountID;
@@ -802,7 +951,7 @@ TER RippleCalc::calcNodeDeliverRev(
STAmount& saTakerGets = pnCur.saTakerGets; STAmount& saTakerGets = pnCur.saTakerGets;
STAmount& saRateMax = pnCur.saRateMax; STAmount& saRateMax = pnCur.saRateMax;
terResult = calcNodeAdvance(uNode, pspCur, bMultiQuality, true); // If needed, advance to next funded offer. terResult = calcNodeAdvance(uNode, psCur, bMultiQuality, true); // If needed, advance to next funded offer.
if (tesSUCCESS != terResult || !uOfferIndex) if (tesSUCCESS != terResult || !uOfferIndex)
{ {
@@ -908,7 +1057,7 @@ TER RippleCalc::calcNodeDeliverRev(
terResult = calcNodeDeliverRev( terResult = calcNodeDeliverRev(
uNode-1, uNode-1,
pspCur, psCur,
bMultiQuality, bMultiQuality,
uOfrOwnerID, uOfrOwnerID,
saInPassReq, saInPassReq,
@@ -969,7 +1118,7 @@ TER RippleCalc::calcNodeDeliverRev(
// <-- pnCur.saFwdDeliver: For calcNodeAccountFwd to know how much went through // <-- pnCur.saFwdDeliver: For calcNodeAccountFwd to know how much went through
TER RippleCalc::calcNodeDeliverFwd( TER RippleCalc::calcNodeDeliverFwd(
const unsigned int uNode, // 0 < uNode < uLast const unsigned int uNode, // 0 < uNode < uLast
PathState::ref pspCur, PathState& psCur,
const bool bMultiQuality, const bool bMultiQuality,
const uint160& uInAccountID, // --> Input owner's account. const uint160& uInAccountID, // --> Input owner's account.
const STAmount& saInReq, // --> Amount to deliver. const STAmount& saInReq, // --> Amount to deliver.
@@ -978,9 +1127,9 @@ TER RippleCalc::calcNodeDeliverFwd(
{ {
TER terResult = tesSUCCESS; TER terResult = tesSUCCESS;
PaymentNode& pnPrv = pspCur->vpnNodes[uNode-1]; PaymentNode& pnPrv = psCur.vpnNodes[uNode-1];
PaymentNode& pnCur = pspCur->vpnNodes[uNode]; PaymentNode& pnCur = psCur.vpnNodes[uNode];
PaymentNode& pnNxt = pspCur->vpnNodes[uNode+1]; PaymentNode& pnNxt = psCur.vpnNodes[uNode+1];
const uint160& uNxtAccountID = pnNxt.uAccountID; const uint160& uNxtAccountID = pnNxt.uAccountID;
const uint160& uCurCurrencyID = pnCur.uCurrencyID; const uint160& uCurCurrencyID = pnCur.uCurrencyID;
@@ -1002,7 +1151,7 @@ TER RippleCalc::calcNodeDeliverFwd(
&& saInAct + saInFees != saInReq) // Did not deliver all funds. && saInAct + saInFees != saInReq) // Did not deliver all funds.
{ {
// Determine values for pass to adjust saInAct, saInFees, and saCurDeliverAct // Determine values for pass to adjust saInAct, saInFees, and saCurDeliverAct
terResult = calcNodeAdvance(uNode, pspCur, bMultiQuality, false); // If needed, advance to next funded offer. terResult = calcNodeAdvance(uNode, psCur, bMultiQuality, false); // If needed, advance to next funded offer.
if (tesSUCCESS == terResult) if (tesSUCCESS == terResult)
{ {
@@ -1079,7 +1228,7 @@ TER RippleCalc::calcNodeDeliverFwd(
// Therefore, immediately push through output for current offer. // Therefore, immediately push through output for current offer.
terResult = RippleCalc::calcNodeDeliverFwd( terResult = RippleCalc::calcNodeDeliverFwd(
uNode+1, uNode+1,
pspCur, psCur,
bMultiQuality, bMultiQuality,
uOfrOwnerID, // --> Current holder. uOfrOwnerID, // --> Current holder.
saOutPassMax, // --> Amount available. saOutPassMax, // --> Amount available.
@@ -1138,7 +1287,7 @@ TER RippleCalc::calcNodeDeliverFwd(
if (saOutPassAct == saOutFunded) if (saOutPassAct == saOutFunded)
{ {
// Offer became unfunded. // Offer became unfunded.
pspCur->vUnfundedBecame.push_back(uOfferIndex); psCur.vUnfundedBecame.push_back(uOfferIndex);
bEntryAdvance = true; bEntryAdvance = true;
} }
@@ -1161,13 +1310,13 @@ TER RippleCalc::calcNodeDeliverFwd(
// Called to drive from the last offer node in a chain. // Called to drive from the last offer node in a chain.
TER RippleCalc::calcNodeOfferRev( TER RippleCalc::calcNodeOfferRev(
const unsigned int uNode, // 0 < uNode < uLast const unsigned int uNode, // 0 < uNode < uLast
PathState::ref pspCur, PathState& psCur,
const bool bMultiQuality) const bool bMultiQuality)
{ {
TER terResult; TER terResult;
PaymentNode& pnCur = pspCur->vpnNodes[uNode]; PaymentNode& pnCur = psCur.vpnNodes[uNode];
PaymentNode& pnNxt = pspCur->vpnNodes[uNode+1]; PaymentNode& pnNxt = psCur.vpnNodes[uNode+1];
if (!!pnNxt.uAccountID) if (!!pnNxt.uAccountID)
{ {
@@ -1176,7 +1325,7 @@ TER RippleCalc::calcNodeOfferRev(
terResult = calcNodeDeliverRev( terResult = calcNodeDeliverRev(
uNode, uNode,
pspCur, psCur,
bMultiQuality, bMultiQuality,
pnNxt.uAccountID, pnNxt.uAccountID,
@@ -1201,12 +1350,12 @@ TER RippleCalc::calcNodeOfferRev(
// - Deliver is set without transfer fees. // - Deliver is set without transfer fees.
TER RippleCalc::calcNodeOfferFwd( TER RippleCalc::calcNodeOfferFwd(
const unsigned int uNode, // 0 < uNode < uLast const unsigned int uNode, // 0 < uNode < uLast
PathState::ref pspCur, PathState& psCur,
const bool bMultiQuality const bool bMultiQuality
) )
{ {
TER terResult; TER terResult;
PaymentNode& pnPrv = pspCur->vpnNodes[uNode-1]; PaymentNode& pnPrv = psCur.vpnNodes[uNode-1];
if (!!pnPrv.uAccountID) if (!!pnPrv.uAccountID)
{ {
@@ -1216,7 +1365,7 @@ TER RippleCalc::calcNodeOfferFwd(
terResult = calcNodeDeliverFwd( terResult = calcNodeDeliverFwd(
uNode, uNode,
pspCur, psCur,
bMultiQuality, bMultiQuality,
pnPrv.uAccountID, pnPrv.uAccountID,
pnPrv.saFwdDeliver, // Previous is sending this much. pnPrv.saFwdDeliver, // Previous is sending this much.
@@ -1353,16 +1502,16 @@ void RippleCalc::calcNodeRipple(
// Issues are limited based on credit limits and amount owed. // Issues are limited based on credit limits and amount owed.
// No account balance adjustments as we don't know how much is going to actually be pushed through yet. // No account balance adjustments as we don't know how much is going to actually be pushed through yet.
// <-- tesSUCCESS or tepPATH_DRY // <-- tesSUCCESS or tepPATH_DRY
TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality) TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
{ {
TER terResult = tesSUCCESS; TER terResult = tesSUCCESS;
const unsigned int uLast = pspCur->vpnNodes.size() - 1; const unsigned int uLast = psCur.vpnNodes.size() - 1;
uint64 uRateMax = 0; uint64 uRateMax = 0;
PaymentNode& pnPrv = pspCur->vpnNodes[uNode ? uNode-1 : 0]; PaymentNode& pnPrv = psCur.vpnNodes[uNode ? uNode-1 : 0];
PaymentNode& pnCur = pspCur->vpnNodes[uNode]; PaymentNode& pnCur = psCur.vpnNodes[uNode];
PaymentNode& pnNxt = pspCur->vpnNodes[uNode == uLast ? uLast : uNode+1]; PaymentNode& pnNxt = psCur.vpnNodes[uNode == uLast ? uLast : uNode+1];
// Current is allowed to redeem to next. // Current is allowed to redeem to next.
const bool bPrvAccount = !uNode || isSetBit(pnPrv.uFlags, STPathElement::typeAccount); const bool bPrvAccount = !uNode || isSetBit(pnPrv.uFlags, STPathElement::typeAccount);
@@ -1433,7 +1582,7 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
% saCurIssueReq.getFullText() % saCurIssueReq.getFullText()
% saNxtOwed.getFullText()); % saNxtOwed.getFullText());
cLog(lsINFO) << pspCur->getJson(); cLog(lsINFO) << psCur.getJson();
assert(!saCurRedeemReq || (-saNxtOwed) >= saCurRedeemReq); // Current redeem req can't be more than IOUs on hand. assert(!saCurRedeemReq || (-saNxtOwed) >= saCurRedeemReq); // Current redeem req can't be more than IOUs on hand.
assert(!saCurIssueReq // If not issuing, fine. assert(!saCurIssueReq // If not issuing, fine.
@@ -1454,8 +1603,8 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
// account --> ACCOUNT --> $ // account --> ACCOUNT --> $
// Overall deliverable. // Overall deliverable.
const STAmount& saCurWantedReq = bPrvAccount const STAmount& saCurWantedReq = bPrvAccount
? std::min(pspCur->saOutReq-pspCur->saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit. ? std::min(psCur.saOutReq-psCur.saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit.
: pspCur->saOutReq-pspCur->saOutAct; // Previous is an offer, no limit: redeem own IOUs. : psCur.saOutReq-psCur.saOutAct; // Previous is an offer, no limit: redeem own IOUs.
STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer()); STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer());
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> $ : saCurWantedReq=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> $ : saCurWantedReq=%s")
@@ -1618,8 +1767,8 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
{ {
// offer --> ACCOUNT --> $ // offer --> ACCOUNT --> $
const STAmount& saCurWantedReq = bPrvAccount const STAmount& saCurWantedReq = bPrvAccount
? std::min(pspCur->saOutReq-pspCur->saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit. ? std::min(psCur.saOutReq-psCur.saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit.
: pspCur->saOutReq-pspCur->saOutAct; // Previous is an offer, no limit: redeem own IOUs. : psCur.saOutReq-psCur.saOutAct; // Previous is an offer, no limit: redeem own IOUs.
STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer()); STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer());
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> $ : saCurWantedReq=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> $ : saCurWantedReq=%s")
@@ -1702,17 +1851,17 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
// - If next node is an offer and output is XRP then we need to deliver funds to limbo. // - If next node is an offer and output is XRP then we need to deliver funds to limbo.
TER RippleCalc::calcNodeAccountFwd( TER RippleCalc::calcNodeAccountFwd(
const unsigned int uNode, // 0 <= uNode <= uLast const unsigned int uNode, // 0 <= uNode <= uLast
PathState::ref pspCur, PathState& psCur,
const bool bMultiQuality) const bool bMultiQuality)
{ {
TER terResult = tesSUCCESS; TER terResult = tesSUCCESS;
const unsigned int uLast = pspCur->vpnNodes.size() - 1; const unsigned int uLast = psCur.vpnNodes.size() - 1;
uint64 uRateMax = 0; uint64 uRateMax = 0;
PaymentNode& pnPrv = pspCur->vpnNodes[uNode ? uNode-1 : 0]; PaymentNode& pnPrv = psCur.vpnNodes[uNode ? uNode-1 : 0];
PaymentNode& pnCur = pspCur->vpnNodes[uNode]; PaymentNode& pnCur = psCur.vpnNodes[uNode];
PaymentNode& pnNxt = pspCur->vpnNodes[uNode == uLast ? uLast : uNode+1]; PaymentNode& pnNxt = psCur.vpnNodes[uNode == uLast ? uLast : uNode+1];
const bool bPrvAccount = isSetBit(pnPrv.uFlags, STPathElement::typeAccount); const bool bPrvAccount = isSetBit(pnPrv.uFlags, STPathElement::typeAccount);
const bool bNxtAccount = isSetBit(pnNxt.uFlags, STPathElement::typeAccount); const bool bNxtAccount = isSetBit(pnNxt.uFlags, STPathElement::typeAccount);
@@ -1754,7 +1903,7 @@ TER RippleCalc::calcNodeAccountFwd(
STAmount& saCurDeliverAct = pnCur.saFwdDeliver; STAmount& saCurDeliverAct = pnCur.saFwdDeliver;
// For !uNode // For !uNode
STAmount& saCurSendMaxPass = pspCur->saInPass; // Report how much pass sends. STAmount& saCurSendMaxPass = psCur.saInPass; // Report how much pass sends.
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd> uNode=%d/%d saPrvRedeemReq=%s saPrvIssueReq=%s saPrvDeliverReq=%s saCurRedeemReq=%s saCurIssueReq=%s saCurDeliverReq=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd> uNode=%d/%d saPrvRedeemReq=%s saPrvIssueReq=%s saPrvDeliverReq=%s saCurRedeemReq=%s saCurIssueReq=%s saCurDeliverReq=%s")
% uNode % uNode
@@ -1780,10 +1929,10 @@ TER RippleCalc::calcNodeAccountFwd(
saCurRedeemAct = saCurRedeemReq; saCurRedeemAct = saCurRedeemReq;
if (!pspCur->saInReq.isNegative()) if (!psCur.saInReq.isNegative())
{ {
// Limit by send max. // Limit by send max.
saCurRedeemAct = std::min(saCurRedeemAct, pspCur->saInReq-pspCur->saInAct); saCurRedeemAct = std::min(saCurRedeemAct, psCur.saInReq-psCur.saInAct);
} }
saCurSendMaxPass = saCurRedeemAct; saCurSendMaxPass = saCurRedeemAct;
@@ -1792,17 +1941,17 @@ TER RippleCalc::calcNodeAccountFwd(
? saCurIssueReq ? saCurIssueReq
: STAmount(saCurIssueReq); : STAmount(saCurIssueReq);
if (!!saCurIssueAct && !pspCur->saInReq.isNegative()) if (!!saCurIssueAct && !psCur.saInReq.isNegative())
{ {
// Limit by send max. // Limit by send max.
saCurIssueAct = std::min(saCurIssueAct, pspCur->saInReq-pspCur->saInAct-saCurRedeemAct); saCurIssueAct = std::min(saCurIssueAct, psCur.saInReq-psCur.saInAct-saCurRedeemAct);
} }
saCurSendMaxPass += saCurIssueAct; saCurSendMaxPass += saCurIssueAct;
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saInReq=%s saInAct=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s saCurSendMaxPass=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saInReq=%s saInAct=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s saCurSendMaxPass=%s")
% pspCur->saInReq.getFullText() % psCur.saInReq.getFullText()
% pspCur->saInAct.getFullText() % psCur.saInAct.getFullText()
% saCurRedeemAct.getFullText() % saCurRedeemAct.getFullText()
% saCurIssueReq.getFullText() % saCurIssueReq.getFullText()
% saCurIssueAct.getFullText() % saCurIssueAct.getFullText()
@@ -1819,7 +1968,7 @@ TER RippleCalc::calcNodeAccountFwd(
// Last node. Accept all funds. Calculate amount actually to credit. // Last node. Accept all funds. Calculate amount actually to credit.
STAmount& saCurReceive = pspCur->saOutPass; STAmount& saCurReceive = psCur.saOutPass;
STAmount saIssueCrd = uQualityIn >= QUALITY_ONE STAmount saIssueCrd = uQualityIn >= QUALITY_ONE
? saPrvIssueReq // No fee. ? saPrvIssueReq // No fee.
@@ -1916,10 +2065,10 @@ TER RippleCalc::calcNodeAccountFwd(
saCurDeliverAct = saCurDeliverReq; saCurDeliverAct = saCurDeliverReq;
// If limited, then limit by send max and available. // If limited, then limit by send max and available.
if (!pspCur->saInReq.isNegative()) if (!psCur.saInReq.isNegative())
{ {
// Limit by send max. // Limit by send max.
saCurDeliverAct = std::min(saCurDeliverAct, pspCur->saInReq-pspCur->saInAct); saCurDeliverAct = std::min(saCurDeliverAct, psCur.saInReq-psCur.saInAct);
// Limit XRP by available. No limit for non-XRP as issuer. // Limit XRP by available. No limit for non-XRP as issuer.
if (!uCurAccountID) if (!uCurAccountID)
@@ -1956,7 +2105,7 @@ TER RippleCalc::calcNodeAccountFwd(
// offer --> ACCOUNT --> $ // offer --> ACCOUNT --> $
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> $ : %s") % saPrvDeliverReq.getFullText()); cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> $ : %s") % saPrvDeliverReq.getFullText());
STAmount& saCurReceive = pspCur->saOutPass; STAmount& saCurReceive = psCur.saOutPass;
// Amount to credit. // Amount to credit.
saCurReceive = saPrvDeliverReq; saCurReceive = saPrvDeliverReq;
@@ -2012,20 +2161,20 @@ TER RippleCalc::calcNodeAccountFwd(
return terResult; return terResult;
} }
TER RippleCalc::calcNodeFwd(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality) TER RippleCalc::calcNodeFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
{ {
const PaymentNode& pnCur = pspCur->vpnNodes[uNode]; const PaymentNode& pnCur = psCur.vpnNodes[uNode];
const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount); const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount);
cLog(lsINFO) << boost::str(boost::format("calcNodeFwd> uNode=%d") % uNode); cLog(lsINFO) << boost::str(boost::format("calcNodeFwd> uNode=%d") % uNode);
TER terResult = bCurAccount TER terResult = bCurAccount
? calcNodeAccountFwd(uNode, pspCur, bMultiQuality) ? calcNodeAccountFwd(uNode, psCur, bMultiQuality)
: calcNodeOfferFwd(uNode, pspCur, bMultiQuality); : calcNodeOfferFwd(uNode, psCur, bMultiQuality);
if (tesSUCCESS == terResult && uNode + 1 != pspCur->vpnNodes.size()) if (tesSUCCESS == terResult && uNode + 1 != psCur.vpnNodes.size())
{ {
terResult = calcNodeFwd(uNode+1, pspCur, bMultiQuality); terResult = calcNodeFwd(uNode+1, psCur, bMultiQuality);
} }
cLog(lsINFO) << boost::str(boost::format("calcNodeFwd< uNode=%d terResult=%d") % uNode % terResult); cLog(lsINFO) << boost::str(boost::format("calcNodeFwd< uNode=%d terResult=%d") % uNode % terResult);
@@ -2042,9 +2191,9 @@ TER RippleCalc::calcNodeFwd(const unsigned int uNode, PathState::ref pspCur, con
// --> [all]saWanted.mCurrency // --> [all]saWanted.mCurrency
// --> [all]saAccount // --> [all]saAccount
// <-> [0]saWanted.mAmount : --> limit, <-- actual // <-> [0]saWanted.mAmount : --> limit, <-- actual
TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality) TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
{ {
PaymentNode& pnCur = pspCur->vpnNodes[uNode]; PaymentNode& pnCur = psCur.vpnNodes[uNode];
const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount); const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount);
TER terResult; TER terResult;
@@ -2060,8 +2209,8 @@ TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState::ref pspCur, con
% saTransferRate.getFullText()); % saTransferRate.getFullText());
terResult = bCurAccount terResult = bCurAccount
? calcNodeAccountRev(uNode, pspCur, bMultiQuality) ? calcNodeAccountRev(uNode, psCur, bMultiQuality)
: calcNodeOfferRev(uNode, pspCur, bMultiQuality); : calcNodeOfferRev(uNode, psCur, bMultiQuality);
// Do previous. // Do previous.
if (tesSUCCESS != terResult) if (tesSUCCESS != terResult)
@@ -2073,7 +2222,7 @@ TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState::ref pspCur, con
{ {
// Continue in reverse. // Continue in reverse.
terResult = calcNodeRev(uNode-1, pspCur, bMultiQuality); terResult = calcNodeRev(uNode-1, psCur, bMultiQuality);
} }
cLog(lsINFO) << boost::str(boost::format("calcNodeRev< uNode=%d terResult=%s/%d") % uNode % transToken(terResult) % terResult); cLog(lsINFO) << boost::str(boost::format("calcNodeRev< uNode=%d terResult=%s/%d") % uNode % transToken(terResult) % terResult);
@@ -2083,59 +2232,59 @@ TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState::ref pspCur, con
// Calculate the next increment of a path. // Calculate the next increment of a path.
// The increment is what can satisfy a portion or all of the requested output at the best quality. // The increment is what can satisfy a portion or all of the requested output at the best quality.
// <-- pspCur->uQuality // <-- psCur.uQuality
void RippleCalc::pathNext(PathState::ref pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent) void RippleCalc::pathNext(PathState& psCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent)
{ {
// The next state is what is available in preference order. // The next state is what is available in preference order.
// This is calculated when referenced accounts changed. // This is calculated when referenced accounts changed.
const bool bMultiQuality = iPaths == 1; const bool bMultiQuality = iPaths == 1;
const unsigned int uLast = pspCur->vpnNodes.size() - 1; const unsigned int uLast = psCur.vpnNodes.size() - 1;
pspCur->bConsumed = false; psCur.bConsumed = false;
// YYY This clearing should only be needed for nice logging. // YYY This clearing should only be needed for nice logging.
pspCur->saInPass = STAmount(pspCur->saInReq.getCurrency(), pspCur->saInReq.getIssuer()); psCur.saInPass = STAmount(psCur.saInReq.getCurrency(), psCur.saInReq.getIssuer());
pspCur->saOutPass = STAmount(pspCur->saOutReq.getCurrency(), pspCur->saOutReq.getIssuer()); psCur.saOutPass = STAmount(psCur.saOutReq.getCurrency(), psCur.saOutReq.getIssuer());
pspCur->vUnfundedBecame.clear(); psCur.vUnfundedBecame.clear();
pspCur->umReverse.clear(); psCur.umReverse.clear();
cLog(lsINFO) << "Path In: " << pspCur->getJson(); cLog(lsINFO) << "Path In: " << psCur.getJson();
assert(pspCur->vpnNodes.size() >= 2); assert(psCur.vpnNodes.size() >= 2);
lesCurrent = lesCheckpoint; // Restore from checkpoint. lesCurrent = lesCheckpoint; // Restore from checkpoint.
lesCurrent.bumpSeq(); // Begin ledger varance. lesCurrent.bumpSeq(); // Begin ledger varance.
pspCur->terStatus = calcNodeRev(uLast, pspCur, bMultiQuality); psCur.terStatus = calcNodeRev(uLast, psCur, bMultiQuality);
cLog(lsINFO) << "Path after reverse: " << pspCur->getJson(); cLog(lsINFO) << "Path after reverse: " << psCur.getJson();
if (tesSUCCESS == pspCur->terStatus) if (tesSUCCESS == psCur.terStatus)
{ {
// Do forward. // Do forward.
lesCurrent = lesCheckpoint; // Restore from checkpoint. lesCurrent = lesCheckpoint; // Restore from checkpoint.
lesCurrent.bumpSeq(); // Begin ledger varance. lesCurrent.bumpSeq(); // Begin ledger varance.
pspCur->terStatus = calcNodeFwd(0, pspCur, bMultiQuality); psCur.terStatus = calcNodeFwd(0, psCur, bMultiQuality);
} }
if (tesSUCCESS == pspCur->terStatus) if (tesSUCCESS == psCur.terStatus)
{ {
tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG) tLog(!psCur.saInPass || !psCur.saOutPass, lsDEBUG)
<< boost::str(boost::format("saOutPass=%s saInPass=%s") << boost::str(boost::format("saOutPass=%s saInPass=%s")
% pspCur->saOutPass.getFullText() % psCur.saOutPass.getFullText()
% pspCur->saInPass.getFullText()); % psCur.saInPass.getFullText());
assert(!!pspCur->saOutPass && !!pspCur->saInPass); assert(!!psCur.saOutPass && !!psCur.saInPass);
pspCur->uQuality = STAmount::getRate(pspCur->saOutPass, pspCur->saInPass); // Calculate relative quality. psCur.uQuality = STAmount::getRate(psCur.saOutPass, psCur.saInPass); // Calculate relative quality.
cLog(lsINFO) << "Path after forward: " << pspCur->getJson(); cLog(lsINFO) << "Path after forward: " << psCur.getJson();
} }
else else
{ {
pspCur->uQuality = 0; psCur.uQuality = 0;
} }
} }
@@ -2144,7 +2293,7 @@ TER RippleCalc::rippleCalc(
LedgerEntrySet& lesActive, // <-> --> = Fee already applied to src balance. LedgerEntrySet& lesActive, // <-> --> = Fee already applied to src balance.
STAmount& saMaxAmountAct, // <-- The computed input amount. STAmount& saMaxAmountAct, // <-- The computed input amount.
STAmount& saDstAmountAct, // <-- The computed output amount. STAmount& saDstAmountAct, // <-- The computed output amount.
std::vector<PathState::pointer>& vpsExpanded,
// Issuer: // Issuer:
// XRP: ACCOUNT_XRP // XRP: ACCOUNT_XRP
// non-XRP: uSrcAccountID (for any issuer) or another account with trust node. // non-XRP: uSrcAccountID (for any issuer) or another account with trust node.
@@ -2178,28 +2327,23 @@ TER RippleCalc::rippleCalc(
} }
// Incrementally search paths. // Incrementally search paths.
std::vector<PathState::pointer> vpsPaths;
if (!bNoRippleDirect) if (!bNoRippleDirect)
{ {
// Direct path. // Direct path.
// XXX Might also make a XRP bridge by default. // XXX Might also make a XRP bridge by default.
PathState::pointer pspDirect = PathState::createExpanded( PathState::pointer pspDirect = boost::make_shared<PathState>(saDstAmountReq, saMaxAmountReq, lesActive.getLedgerRef());
lesActive,
STPath(),
uDstAccountID,
uSrcAccountID,
saDstAmountReq,
saMaxAmountReq);
pspDirect->setIndex(vpsPaths.size()); if (!pspDirect)
return temUNKNOWN;
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build direct: add: %d status: %s") pspDirect->setExpanded(lesActive, STPath(), uDstAccountID, uSrcAccountID);
% !!pspDirect
% transToken(pspDirect ? pspDirect->terStatus : temUNKNOWN)); pspDirect->setIndex(vpsExpanded.size());
if (pspDirect)
{ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build direct: status: %s")
% transToken(pspDirect->terStatus));
// Return if malformed. // Return if malformed.
if (isTemMalformed(pspDirect->terStatus)) if (isTemMalformed(pspDirect->terStatus))
return pspDirect->terStatus; return pspDirect->terStatus;
@@ -2209,35 +2353,30 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build direct: add: %d sta
// Had a success. // Had a success.
terResult = tesSUCCESS; terResult = tesSUCCESS;
vpsPaths.push_back(pspDirect); vpsExpanded.push_back(pspDirect);
} }
else if (terNO_LINE != pspDirect->terStatus) else if (terNO_LINE != pspDirect->terStatus)
{ {
terResult = pspDirect->terStatus; terResult = pspDirect->terStatus;
} }
} }
}
cLog(lsINFO) << "rippleCalc: Paths in set: " << spsPaths.size(); cLog(lsINFO) << "rippleCalc: Paths in set: " << spsPaths.size();
int iIndex = 0; int iIndex = 0;
BOOST_FOREACH(const STPath& spPath, spsPaths) BOOST_FOREACH(const STPath& spPath, spsPaths)
{ {
PathState::pointer pspExpanded = PathState::createExpanded( PathState::pointer pspExpanded = boost::make_shared<PathState>(saDstAmountReq, saMaxAmountReq, lesActive.getLedgerRef());
lesActive,
spPath,
uDstAccountID,
uSrcAccountID,
saDstAmountReq,
saMaxAmountReq);
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: add: %d status: %s") if (!pspExpanded)
return temUNKNOWN;
pspExpanded->setExpanded(lesActive, spPath, uDstAccountID, uSrcAccountID);
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s")
% ++iIndex % ++iIndex
% !!pspExpanded % transToken(pspExpanded->terStatus));
% transToken(pspExpanded ? pspExpanded->terStatus : temUNKNOWN));
if (pspExpanded)
{
// Return, if the path specification was malformed. // Return, if the path specification was malformed.
if (isTemMalformed(pspExpanded->terStatus)) if (isTemMalformed(pspExpanded->terStatus))
return pspExpanded->terStatus; return pspExpanded->terStatus;
@@ -2245,16 +2384,15 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: add: %d s
if (tesSUCCESS == pspExpanded->terStatus) { if (tesSUCCESS == pspExpanded->terStatus) {
terResult = tesSUCCESS; // Had a success. terResult = tesSUCCESS; // Had a success.
pspExpanded->setIndex(vpsPaths.size()); pspExpanded->setIndex(vpsExpanded.size());
vpsPaths.push_back(pspExpanded); vpsExpanded.push_back(pspExpanded);
} }
else if (terNO_LINE != pspExpanded->terStatus) else if (terNO_LINE != pspExpanded->terStatus)
{ {
terResult = pspExpanded->terStatus; terResult = pspExpanded->terStatus;
} }
} }
}
if (tesSUCCESS != terResult) if (tesSUCCESS != terResult)
{ {
@@ -2276,19 +2414,19 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: add: %d s
int iPass = 0; int iPass = 0;
while (temUNCERTAIN == terResult) while (temUNCERTAIN == terResult)
{ {
PathState::pointer pspBest; int iBest = -1;
const LedgerEntrySet lesCheckpoint = lesActive; const LedgerEntrySet lesCheckpoint = lesActive;
int iDry = 0; int iDry = 0;
// Find the best path. // Find the best path.
BOOST_FOREACH(PathState::pointer& pspCur, vpsPaths) BOOST_FOREACH(PathState::pointer pspCur, vpsExpanded)
{ {
if (pspCur->uQuality) if (pspCur->uQuality)
{ {
pspCur->saInAct = saMaxAmountAct; // Update to current amount processed. pspCur->saInAct = saMaxAmountAct; // Update to current amount processed.
pspCur->saOutAct = saDstAmountAct; pspCur->saOutAct = saDstAmountAct;
rc.pathNext(pspCur, vpsPaths.size(), lesCheckpoint, lesActive); // Compute increment. rc.pathNext(*pspCur, vpsExpanded.size(), lesCheckpoint, lesActive); // Compute increment.
if (!pspCur->uQuality) { if (!pspCur->uQuality) {
// Path was dry. // Path was dry.
@@ -2305,8 +2443,8 @@ int iPass = 0;
assert(!!pspCur->saInPass && !!pspCur->saOutPass); assert(!!pspCur->saInPass && !!pspCur->saOutPass);
if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality. if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality.
&& (!pspBest // Best is not yet set. && (iBest < 0 // Best is not yet set.
|| PathState::lessPriority(pspBest, pspCur))) // Current is better than set. || PathState::lessPriority(*vpsExpanded[iBest], *pspCur))) // Current is better than set.
{ {
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s") cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s")
% STAmount::saFromRate(pspCur->uQuality) % STAmount::saFromRate(pspCur->uQuality)
@@ -2314,20 +2452,21 @@ int iPass = 0;
% pspCur->saOutPass.getFullText()); % pspCur->saOutPass.getFullText());
lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state. lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state.
pspBest = pspCur; iBest = pspCur->getIndex();
} }
} }
} }
} }
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsPaths.size()); cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size());
BOOST_FOREACH(PathState::pointer& pspCur, vpsPaths) BOOST_FOREACH(PathState::ref pspCur, vpsExpanded)
{ {
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d best: %d consumed: %d") % pspCur->mIndex % pspCur->uQuality % (pspBest == pspCur) % pspCur->bConsumed); cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d best: %d consumed: %d") % pspCur->mIndex % pspCur->uQuality % (iBest == pspCur->getIndex()) % pspCur->bConsumed);
} }
if (pspBest) if (iBest >= 0)
{ {
// Apply best path. // Apply best path.
PathState::pointer pspBest = vpsExpanded[iBest];
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: best: uQuality=%s saInPass=%s saOutPass=%s") cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: best: uQuality=%s saInPass=%s saOutPass=%s")
% STAmount::saFromRate(pspBest->uQuality) % STAmount::saFromRate(pspBest->uQuality)
@@ -2355,7 +2494,7 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d be
terResult = tesSUCCESS; terResult = tesSUCCESS;
} }
else if (saMaxAmountAct != saMaxAmountReq && iDry != vpsPaths.size()) else if (saMaxAmountAct != saMaxAmountReq && iDry != vpsExpanded.size())
{ {
// Have not met requested amount or max send, try to do more. Prepare for next pass. // Have not met requested amount or max send, try to do more. Prepare for next pass.

View File

@@ -107,12 +107,17 @@ public:
return this; return this;
} }
int getIndex() { return mIndex; };
PathState( PathState(
const STAmount& saSend, const STAmount& saSend,
const STAmount& saSendMax, const STAmount& saSendMax,
const Ledger::ref lrLedger = Ledger::pointer() const Ledger::ref lrLedger = Ledger::pointer()
) : mLedger(lrLedger), saInReq(saSendMax), saOutReq(saSend) { ; } ) : mLedger(lrLedger), saInReq(saSendMax), saOutReq(saSend) { ; }
PathState(const PathState& psSrc, bool bUnsed)
: mLedger(psSrc.mLedger), saInReq(psSrc.saInReq), saOutReq(psSrc.saOutReq) { ; }
void setExpanded( void setExpanded(
const LedgerEntrySet& lesSource, const LedgerEntrySet& lesSource,
const STPath& spSourcePath, const STPath& spSourcePath,
@@ -121,29 +126,14 @@ public:
); );
void setCanonical( void setCanonical(
PathState::ref pspExpanded const PathState& psExpanded
); );
Json::Value getJson() const; Json::Value getJson() const;
static PathState::pointer createExpanded( #if 0
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
const uint160& uSenderID,
const STAmount& saSend,
const STAmount& saSendMax
)
{
PathState::pointer pspNew = boost::make_shared<PathState>(saSend, saSendMax, lesSource.getLedgerRef());
pspNew->setExpanded(lesSource, spSourcePath, uReceiverID, uSenderID);
return pspNew;
}
static PathState::pointer createCanonical( static PathState::pointer createCanonical(
PathState::ref pspExpanded PathState&ref pspExpanded
) )
{ {
PathState::pointer pspNew = boost::make_shared<PathState>(pspExpanded->saOutAct, pspExpanded->saInAct); PathState::pointer pspNew = boost::make_shared<PathState>(pspExpanded->saOutAct, pspExpanded->saInAct);
@@ -152,8 +142,8 @@ public:
return pspNew; return pspNew;
} }
#endif
static bool lessPriority(PathState::ref lhs, PathState::ref rhs); static bool lessPriority(PathState& lhs, PathState& rhs);
}; };
class RippleCalc class RippleCalc
@@ -168,18 +158,18 @@ public:
// If the transaction fails to meet some constraint, still need to delete unfunded offers. // If the transaction fails to meet some constraint, still need to delete unfunded offers.
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded. boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
void pathNext(PathState::ref pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent); void pathNext(PathState& psCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
TER calcNode(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality); TER calcNode(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
TER calcNodeRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality); TER calcNodeRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
TER calcNodeFwd(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality); TER calcNodeFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
TER calcNodeOfferRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality); TER calcNodeOfferRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
TER calcNodeOfferFwd(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality); TER calcNodeOfferFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
TER calcNodeAccountRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality); TER calcNodeAccountRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
TER calcNodeAccountFwd(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality); TER calcNodeAccountFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
TER calcNodeAdvance(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality, const bool bReverse); TER calcNodeAdvance(const unsigned int uNode, PathState& psCur, const bool bMultiQuality, const bool bReverse);
TER calcNodeDeliverRev( TER calcNodeDeliverRev(
const unsigned int uNode, const unsigned int uNode,
PathState::ref pspCur, PathState& psCur,
const bool bMultiQuality, const bool bMultiQuality,
const uint160& uOutAccountID, const uint160& uOutAccountID,
const STAmount& saOutReq, const STAmount& saOutReq,
@@ -187,7 +177,7 @@ public:
TER calcNodeDeliverFwd( TER calcNodeDeliverFwd(
const unsigned int uNode, const unsigned int uNode,
PathState::ref pspCur, PathState& psCur,
const bool bMultiQuality, const bool bMultiQuality,
const uint160& uInAccountID, const uint160& uInAccountID,
const STAmount& saInReq, const STAmount& saInReq,
@@ -205,6 +195,7 @@ public:
LedgerEntrySet& lesActive, LedgerEntrySet& lesActive,
STAmount& saMaxAmountAct, STAmount& saMaxAmountAct,
STAmount& saDstAmountAct, STAmount& saDstAmountAct,
std::vector<PathState::pointer>& vpsExpanded,
const STAmount& saDstAmountReq, const STAmount& saDstAmountReq,
const STAmount& saMaxAmountReq, const STAmount& saMaxAmountReq,
const uint160& uDstAccountID, const uint160& uDstAccountID,
@@ -215,6 +206,8 @@ public:
const bool bNoRippleDirect, const bool bNoRippleDirect,
const bool bStandAlone const bool bStandAlone
); );
static void setCanonical(STPathSet& spsDst, const std::vector<PathState::pointer>& vpsExpanded);
}; };
#endif #endif

View File

@@ -570,7 +570,7 @@ public:
}; };
protected: protected:
int mType; unsigned int mType;
uint160 mAccountID; uint160 mAccountID;
uint160 mCurrencyID; uint160 mCurrencyID;
uint160 mIssuerID; uint160 mIssuerID;
@@ -586,6 +586,12 @@ public:
| (uIssuerID.isZero() ? 0 : STPathElement::typeIssuer); | (uIssuerID.isZero() ? 0 : STPathElement::typeIssuer);
} }
STPathElement(unsigned int uType, const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
: mType(uType), mAccountID(uAccountID), mCurrencyID(uCurrencyID), mIssuerID(uIssuerID)
{
;
}
int getNodeType() const { return mType; } int getNodeType() const { return mType; }
bool isOffer() const { return mAccountID.isZero(); } bool isOffer() const { return mAccountID.isZero(); }
bool isAccount() const { return !isOffer(); } bool isAccount() const { return !isOffer(); }
@@ -634,6 +640,8 @@ public:
std::vector<STPathElement>::const_iterator end() const { return mPath.end(); } std::vector<STPathElement>::const_iterator end() const { return mPath.end(); }
bool operator==(const STPath& t) const { return mPath == t.mPath; } bool operator==(const STPath& t) const { return mPath == t.mPath; }
void setCanonical(const STPath& spExpanded);
}; };
inline std::vector<STPathElement>::iterator range_begin(STPath & x) inline std::vector<STPathElement>::iterator range_begin(STPath & x)