diff --git a/src/ripple_app/paths/PathRequest.cpp b/src/ripple_app/paths/PathRequest.cpp index bb0775b42e..1c216f6c95 100644 --- a/src/ripple_app/paths/PathRequest.cpp +++ b/src/ripple_app/paths/PathRequest.cpp @@ -331,7 +331,8 @@ bool PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) currIssuer.first, currIssuer.second, saDstAmount, valid); CondLog (!valid, lsINFO, PathRequest) << "PF request not valid"; - if (valid && pf.findPaths (iLevel, 4, spsPaths)) + STPath extraPath; + if (valid && pf.findPaths (iLevel, 4, spsPaths, extraPath)) { LedgerEntrySet lesSandbox (cache->getLedger (), tapNONE); std::vector vpsExpanded; @@ -343,9 +344,23 @@ bool PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) saMaxAmount.negate (); WriteLog (lsDEBUG, PathRequest) << "Paths found, calling rippleCalc"; TER terResult = RippleCalc::rippleCalc (lesSandbox, saMaxAmountAct, saDstAmountAct, - vpsExpanded, saMaxAmount, saDstAmount, raDstAccount.getAccountID (), raSrcAccount.getAccountID (), + vpsExpanded, saMaxAmount, saDstAmount, + raDstAccount.getAccountID (), raSrcAccount.getAccountID (), spsPaths, false, false, false, true); + + if ((extraPath.size() > 0) && ((terResult == terNO_LINE) || (terResult == tecPATH_PARTIAL))) + { + WriteLog (lsDEBUG, PathRequest) << "Trying with an extra path element"; + spsPaths.addPath(extraPath); + vpsExpanded.clear (); + terResult = RippleCalc::rippleCalc (lesSandbox, saMaxAmountAct, saDstAmountAct, + vpsExpanded, saMaxAmount, saDstAmount, + raDstAccount.getAccountID (), raSrcAccount.getAccountID (), + spsPaths, false, false, false, true); + WriteLog (lsDEBUG, PathRequest) << "Extra path element gives " << transHuman (terResult); + } + if (terResult == tesSUCCESS) { Json::Value jvEntry (Json::objectValue); diff --git a/src/ripple_app/paths/Pathfinder.cpp b/src/ripple_app/paths/Pathfinder.cpp index 04be2df987..47effb7287 100644 --- a/src/ripple_app/paths/Pathfinder.cpp +++ b/src/ripple_app/paths/Pathfinder.cpp @@ -126,7 +126,7 @@ Pathfinder::Pathfinder (RippleLineCache::ref cache, } -bool Pathfinder::findPaths (int iLevel, const unsigned int iMaxPaths, STPathSet& pathsOut) +bool Pathfinder::findPaths (int iLevel, const unsigned int iMaxPaths, STPathSet& pathsOut, STPath& extraPath) { // pathsOut contains only non-default paths without source or destiation // On input, pathsOut contains any paths you want to ensure are included if still good @@ -221,14 +221,14 @@ bool Pathfinder::findPaths (int iLevel, const unsigned int iMaxPaths, STPathSet& WriteLog (lsDEBUG, Pathfinder) << mCompletePaths.size() << " paths to filter"; if (mCompletePaths.size() > iMaxPaths) - pathsOut = filterPaths(iMaxPaths); + pathsOut = filterPaths(iMaxPaths, extraPath); else pathsOut = mCompletePaths; return true; // Even if we find no paths, default paths may work, and we don't check them currently } -STPathSet Pathfinder::filterPaths(int iMaxPaths) +STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath) { if (mCompletePaths.size() <= iMaxPaths) return mCompletePaths; @@ -274,6 +274,9 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths) std::vector vMap; + // Ignore paths that move only very small amounts + STAmount saMinDstAmount = STAmount::divide(mDstAmount, STAmount(iMaxPaths + 2), mDstAmount); + // Build map of quality to entry. for (int i = mCompletePaths.size (); i--;) { @@ -293,8 +296,8 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths) terResult = RippleCalc::rippleCalc ( lesSandbox, - saMaxAmountAct, - saDstAmountAct, + saMaxAmountAct, // --> computed input + saDstAmountAct, // --> computed output vpsExpanded, mSrcAmount, // --> amount to send max. mDstAmount, // --> amount to deliver. @@ -313,7 +316,21 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths) terResult = tefEXCEPTION; } - if (tesSUCCESS == terResult) + if (tesSUCCESS != terResult) + { + WriteLog (lsDEBUG, Pathfinder) + << boost::str (boost::format ("findPaths: dropping: %s: %s") + % transToken (terResult) + % spCurrent.getJson (0)); + } + else if (saDstAmountAct < saMinDstAmount) + { + WriteLog (lsDEBUG, Pathfinder) + << boost::str (boost::format ("findPaths: dropping: outputs %s: %s") + % saDstAmountAct + % spCurrent.getJson (0)); + } + else { uint64 uQuality = STAmount::getRate (saDstAmountAct, saMaxAmountAct); @@ -324,13 +341,6 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths) vMap.push_back (path_LQ_t (uQuality, spCurrent.mPath.size (), saDstAmountAct, i)); } - else - { - WriteLog (lsDEBUG, Pathfinder) - << boost::str (boost::format ("findPaths: dropping: %s: %s") - % transToken (terResult) - % spCurrent.getJson (0)); - } } STPathSet spsDst; @@ -351,6 +361,12 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths) remaining -= lqt.get<2> (); spsDst.addPath (mCompletePaths[lqt.get<3> ()]); } + else if ((iPathsLeft == 0) && (lqt.get<2>() >= mDstAmount) && (extraPath.size() == 0)) + { + // found an extra path that can move the whole amount + extraPath = mCompletePaths[lqt.get<3>()]; + WriteLog (lsDEBUG, Pathfinder) << "Found extra full path: " << extraPath.getJson(0); + } else WriteLog (lsDEBUG, Pathfinder) << "Skipping a non-filling path: " << mCompletePaths[lqt.get<3> ()].getJson (0); } diff --git a/src/ripple_app/paths/Pathfinder.h b/src/ripple_app/paths/Pathfinder.h index 40bf6631bb..c926d09e64 100644 --- a/src/ripple_app/paths/Pathfinder.h +++ b/src/ripple_app/paths/Pathfinder.h @@ -62,7 +62,7 @@ public: const uint160& srcCurrencyID, const uint160& srcIssuerID, const STAmount& dstAmount, bool& bValid); static void initPathTable(); - bool findPaths (int iLevel, const unsigned int iMaxPaths, STPathSet& spsDst); + bool findPaths (int iLevel, const unsigned int iMaxPaths, STPathSet& spsDst, STPath& spExtraPath); private: @@ -102,7 +102,7 @@ private: void addLink(const STPath& currentPath, STPathSet& incompletePaths, int addFlags); void addLink(const STPathSet& currentPaths, STPathSet& incompletePaths, int addFlags); STPathSet& getPaths(const PathType_t& type, bool addComplete = true); - STPathSet filterPaths(int iMaxPaths); + STPathSet filterPaths(int iMaxPaths, STPath& extraPath); // Our main table of paths diff --git a/src/ripple_app/rpc/RPCHandler.cpp b/src/ripple_app/rpc/RPCHandler.cpp index 2e51fe29ff..858a81b557 100644 --- a/src/ripple_app/rpc/RPCHandler.cpp +++ b/src/ripple_app/rpc/RPCHandler.cpp @@ -172,7 +172,8 @@ Json::Value RPCHandler::transactionSign (Json::Value params, bool bSubmit, bool Pathfinder pf (cache, raSrcAddressID, dstAccountID, saSendMax.getCurrency (), saSendMax.getIssuer (), saSend, bValid); - if (!bValid || !pf.findPaths (getConfig ().PATH_SEARCH_OLD, 4, spsPaths)) + STPath extraPath; + if (!bValid || !pf.findPaths (getConfig ().PATH_SEARCH_OLD, 4, spsPaths, extraPath)) { WriteLog (lsDEBUG, RPCHandler) << "transactionSign: build_path: No paths found."; @@ -1560,7 +1561,8 @@ Json::Value RPCHandler::doRipplePathFind (Json::Value params, LoadType* loadType int level = getConfig().PATH_SEARCH_OLD; if ((getConfig().PATH_SEARCH_MAX > level) && !getApp().getFeeTrack().isLoadedLocal()) ++level; - if (!bValid || !pf.findPaths (level, 4, spsComputed)) + STPath extraPath; + if (!bValid || !pf.findPaths (level, 4, spsComputed, extraPath)) { WriteLog (lsWARNING, RPCHandler) << "ripple_path_find: No paths found."; } @@ -1608,6 +1610,18 @@ Json::Value RPCHandler::doRipplePathFind (Json::Value params, LoadType* loadType % saMaxAmountAct % saDstAmountAct); + if ((extraPath.size() > 0) && ((terResult == terNO_LINE) || (terResult == tecPATH_PARTIAL))) + { + WriteLog (lsDEBUG, PathRequest) << "Trying with an extra path element"; + spsComputed.addPath(extraPath); + vpsExpanded.clear (); + terResult = RippleCalc::rippleCalc (lesSandbox, saMaxAmountAct, saDstAmountAct, + vpsExpanded, saMaxAmount, saDstAmount, + raDst.getAccountID (), raSrc.getAccountID (), + spsComputed, false, false, false, true); + WriteLog (lsDEBUG, PathRequest) << "Extra path element gives " << transHuman (terResult); + } + if (tesSUCCESS == terResult) { Json::Value jvEntry (Json::objectValue);