mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Rough cut at ripple_path_find.
This commit is contained in:
@@ -84,7 +84,7 @@ Pathfinder::Pathfinder(RippleAddress& srcAccountID, RippleAddress& dstAccountID,
|
||||
// Returns a single path, if possible.
|
||||
// --> maxSearchSteps: unused
|
||||
// --> maxPay: unused
|
||||
bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet)
|
||||
bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet, bool bAllowEmpty)
|
||||
{
|
||||
if (mLedger) {
|
||||
std::queue<STPath> pqueue;
|
||||
@@ -107,16 +107,28 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet
|
||||
// Determine if path is solved.
|
||||
|
||||
// Done, if dest wants XRP and last element produces XRP.
|
||||
// Done, if dest wants non-XRP and last element is dest.
|
||||
if (!ele.mCurrencyID // Tail output is XRP
|
||||
&& !mDstAmount.getCurrency()) {
|
||||
|
||||
if (!ele.mCurrencyID) {
|
||||
// Remove implied first.
|
||||
path.mPath.erase(path.mPath.begin());
|
||||
|
||||
// Return the path.
|
||||
retPathSet.addPath(path);
|
||||
|
||||
cLog(lsDEBUG) << "findPaths: adding: " << path.getJson(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
if (ele.mAccountID == mDstAccountID) {
|
||||
|
||||
// Done, if dest wants non-XRP and last element is dest.
|
||||
// YYY Allows going through self. Is this wanted?
|
||||
if (ele.mAccountID == mDstAccountID // Tail is destination
|
||||
&& ele.mCurrencyID == mDstAmount.getCurrency()) { // With correct output currency.
|
||||
// Found a path to the destination.
|
||||
|
||||
if (2 == path.mPath.size()) {
|
||||
if (!bAllowEmpty && 2 == path.mPath.size()) {
|
||||
// Empty path is default. Drop it.
|
||||
// XXX Don't drop empty path - we still want an estimate.
|
||||
cLog(lsDEBUG) << "findPaths: dropping empty path.";
|
||||
continue;
|
||||
}
|
||||
@@ -128,14 +140,19 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet
|
||||
// Return the path.
|
||||
retPathSet.addPath(path);
|
||||
|
||||
cLog(lsDEBUG) << "findPaths: adding: " << path.getJson(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bContinued = false;
|
||||
|
||||
if (!ele.mCurrencyID) {
|
||||
// Last element is for XRP continue with qualifying books.
|
||||
|
||||
BOOST_FOREACH(OrderBook::pointer book, mOrderBook.getXRPInBooks())
|
||||
{
|
||||
// XXX Don't allow looping through same order books.
|
||||
|
||||
//if (!path.hasSeen(line->getAccountIDPeer().getAccountID()))
|
||||
{
|
||||
STPath new_path(path);
|
||||
@@ -145,10 +162,20 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet
|
||||
new_path.mCurrencyID = book->getCurrencyOut();
|
||||
new_path.mCurrentAccount = book->getCurrencyOut();
|
||||
|
||||
cLog(lsDEBUG) <<
|
||||
boost::str(boost::format("findPaths: XRP input - %s/%s")
|
||||
% STAmount::createHumanCurrency(new_path.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID(new_path.mCurrentAccount));
|
||||
|
||||
pqueue.push(new_path);
|
||||
|
||||
bContinued = true;
|
||||
}
|
||||
}
|
||||
|
||||
tLog(!bContinued, lsDEBUG)
|
||||
<< boost::str(boost::format("findPaths: XRP input - dead end"));
|
||||
|
||||
} else {
|
||||
// Last element is for non-XRP continue by adding ripple lines and order books.
|
||||
|
||||
@@ -164,8 +191,17 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet
|
||||
ele.mCurrencyID,
|
||||
uint160());
|
||||
|
||||
cLog(lsDEBUG) <<
|
||||
boost::str(boost::format("findPaths: %s/%s --> %s/%s")
|
||||
% RippleAddress::createHumanAccountID(ele.mAccountID)
|
||||
% STAmount::createHumanCurrency(ele.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID(line->getAccountIDPeer().getAccountID())
|
||||
% STAmount::createHumanCurrency(ele.mCurrencyID));
|
||||
|
||||
new_path.mPath.push_back(new_ele);
|
||||
pqueue.push(new_path);
|
||||
|
||||
bContinued = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,17 +215,31 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet
|
||||
STPath new_path(path);
|
||||
STPathElement new_ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
|
||||
|
||||
cLog(lsDEBUG) <<
|
||||
boost::str(boost::format("findPaths: %s/%s :: %s/%s")
|
||||
% STAmount::createHumanCurrency(ele.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID(ele.mAccountID)
|
||||
% STAmount::createHumanCurrency(book->getCurrencyOut())
|
||||
% RippleAddress::createHumanAccountID(book->getIssuerOut()));
|
||||
|
||||
new_path.mPath.push_back(new_ele);
|
||||
new_path.mCurrentAccount=book->getIssuerOut();
|
||||
new_path.mCurrencyID=book->getCurrencyOut();
|
||||
|
||||
pqueue.push(new_path);
|
||||
|
||||
bContinued = true;
|
||||
}
|
||||
}
|
||||
|
||||
// enumerate all adjacent nodes, construct a new path and push it into the queue
|
||||
} // While
|
||||
} // if there is a ledger
|
||||
tLog(!bContinued, lsDEBUG)
|
||||
<< boost::str(boost::format("findPaths: non-XRP input - dead end"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsWARNING) << boost::str(boost::format("findPaths: no ledger"));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -38,20 +38,20 @@ class Pathfinder
|
||||
OrderBookDB mOrderBook;
|
||||
Ledger::pointer mLedger;
|
||||
|
||||
std::list<PathOption::pointer> mBuildingPaths;
|
||||
std::list<PathOption::pointer> mCompletePaths;
|
||||
// std::list<PathOption::pointer> mBuildingPaths;
|
||||
// std::list<PathOption::pointer> mCompletePaths;
|
||||
|
||||
void addOptions(PathOption::pointer tail);
|
||||
// void addOptions(PathOption::pointer tail);
|
||||
|
||||
// returns true if any building paths are now complete?
|
||||
bool checkComplete(STPathSet& retPathSet);
|
||||
|
||||
void addPathOption(PathOption::pointer pathOption);
|
||||
// void addPathOption(PathOption::pointer pathOption);
|
||||
|
||||
public:
|
||||
Pathfinder(RippleAddress& srcAccountID, RippleAddress& dstAccountID, uint160& srcCurrencyID, STAmount dstAmount);
|
||||
|
||||
// returns false if there is no path. otherwise fills out retPath
|
||||
bool findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet);
|
||||
bool findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet, bool bAllowEmpty);
|
||||
};
|
||||
// vim:ts=4
|
||||
|
||||
@@ -119,7 +119,7 @@ TER PaymentTransactor::doApply()
|
||||
STAmount saDstAmountAct;
|
||||
|
||||
terResult = isSetBit(mParams, tapOPEN_LEDGER) && spsPaths.getPathCount() > RIPPLE_PATHS_MAX
|
||||
? telBAD_PATH_COUNT
|
||||
? telBAD_PATH_COUNT // Too many paths for proposed ledger.
|
||||
: RippleCalc::rippleCalc(
|
||||
mEngine->getNodes(),
|
||||
saMaxAmountAct,
|
||||
@@ -131,7 +131,8 @@ TER PaymentTransactor::doApply()
|
||||
spsPaths,
|
||||
bPartialPayment,
|
||||
bLimitQuality,
|
||||
bNoRippleDirect);
|
||||
bNoRippleDirect, // Always compute for finalizing ledger.
|
||||
false); // Not standalone, delete unfundeds.
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -175,3 +176,5 @@ TER PaymentTransactor::doApply()
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "RippleLines.h"
|
||||
#include "Wallet.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "AccountState.h"
|
||||
#include "NicknameState.h"
|
||||
#include "InstanceCounter.h"
|
||||
@@ -699,7 +700,7 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
Json::Value jvResult(Json::objectValue);
|
||||
RippleAddress raSrc;
|
||||
RippleAddress raDst;
|
||||
STAmount saDst;
|
||||
STAmount saDstAmount;
|
||||
|
||||
if (
|
||||
// Parse raSrc.
|
||||
@@ -720,9 +721,9 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
jvResult = rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
else if (
|
||||
// Parse saDst.
|
||||
// Parse saDstAmount.
|
||||
!jvRequest.isMember("destination_amount")
|
||||
|| !saDst.bSetJson(jvRequest["destination_amount"]))
|
||||
|| !saDstAmount.bSetJson(jvRequest["destination_amount"]))
|
||||
{
|
||||
cLog(lsINFO) << "Bad destination_amount.";
|
||||
jvResult = rpcError(rpcINVALID_PARAMS);
|
||||
@@ -739,9 +740,12 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
}
|
||||
else
|
||||
{
|
||||
Json::Value jvSrcCurrencies = jvRequest.isMember("source_currencies");
|
||||
Json::Value jvSrcCurrencies = jvRequest["source_currencies"];
|
||||
Json::Value jvArray(Json::arrayValue);
|
||||
|
||||
Ledger::pointer lpCurrent = mNetOps->getCurrentLedger();
|
||||
LedgerEntrySet lesSnapshot(lpCurrent);
|
||||
|
||||
for (unsigned int i=0; i != jvSrcCurrencies.size(); ++i) {
|
||||
Json::Value jvSource = jvSrcCurrencies[i];
|
||||
uint160 srcCurrencyID;
|
||||
@@ -760,16 +764,80 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
STPathSet spsPaths;
|
||||
|
||||
// XXX Need to add support for srcIssuerID.
|
||||
Pathfinder pf(raSrc, raDst, srcCurrencyID, saDst);
|
||||
Pathfinder pf(raSrc, raDst, srcCurrencyID, saDstAmount);
|
||||
|
||||
if (!spsPaths.isEmpty())
|
||||
pf.findPaths(5, 1, spsPaths, true);
|
||||
|
||||
if (spsPaths.isEmpty())
|
||||
{
|
||||
cLog(lsDEBUG) << "ripple_path_find: No paths found.";
|
||||
}
|
||||
else
|
||||
{
|
||||
// XXX Also need to check liquidity.
|
||||
jvSource.append(spsPaths.getJson(0));
|
||||
STAmount saMaxAmountAct;
|
||||
STAmount saDstAmountAct;
|
||||
STAmount saMaxAmount(srcCurrencyID,
|
||||
!!srcIssuerID
|
||||
? srcIssuerID
|
||||
: !!srcCurrencyID
|
||||
? raSrc.getAccountID()
|
||||
: ACCOUNT_XRP,
|
||||
1);
|
||||
saMaxAmount.negate();
|
||||
|
||||
TER terResult =
|
||||
RippleCalc::rippleCalc(
|
||||
lesSnapshot,
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
saMaxAmount, // --> -1/xxx/yyy unlimited
|
||||
saDstAmount, // --> Amount to deliver.
|
||||
raDst.getAccountID(), // --> Account to deliver to.
|
||||
raSrc.getAccountID(), // --> Account sending from.
|
||||
spsPaths, // --> Path set.
|
||||
false, // --> bPartialPayment - XXX might allow sometimes.
|
||||
// Must achive delivery goal.
|
||||
false, // --> bLimitQuality - XXX might allow sometimes.
|
||||
// Average quality is wanted for normal payments.
|
||||
// XXX TRUE till direct path representation resolved.
|
||||
true, // --> bNoRippleDirect - XXX might allow sometimes.
|
||||
// XXX No reason not to take the direct, unless set is merely direct.
|
||||
true); //--> Stand alone mode, don't delete unfundeds.
|
||||
|
||||
cLog(lsDEBUG)
|
||||
<< boost::str(boost::format("ripple_path_find: saMaxAmount=%s saDstAmount=%s saMaxAmountAct=%s saDstAmountAct=%s")
|
||||
% saMaxAmount
|
||||
% saDstAmount
|
||||
% saMaxAmountAct
|
||||
% saDstAmountAct);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
Json::Value jvEntry(Json::objectValue);
|
||||
|
||||
jvEntry["source_amount"] = saMaxAmountAct.getJson(0);
|
||||
jvEntry["paths"] = spsPaths.getJson(0);
|
||||
|
||||
jvArray.append(jvEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
transResultInfo(terResult, strToken, strHuman);
|
||||
|
||||
cLog(lsDEBUG)
|
||||
<< boost::str(boost::format("ripple_path_find: %s %s %s")
|
||||
% strToken
|
||||
% strHuman
|
||||
% spsPaths.getJson(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jvResult["results"] = jvArray;
|
||||
jvResult["alternatives"] = jvArray;
|
||||
}
|
||||
|
||||
return jvResult;
|
||||
@@ -872,7 +940,7 @@ Json::Value RPCHandler::handleJSONSubmit(const Json::Value& jvRequest)
|
||||
|
||||
Pathfinder pf(srcAddress, dstAccountID, srcCurrencyID, dstAmount);
|
||||
|
||||
pf.findPaths(5, 1, spsPaths);
|
||||
pf.findPaths(5, 1, spsPaths, false);
|
||||
|
||||
if (!spsPaths.isEmpty())
|
||||
{
|
||||
@@ -1067,7 +1135,7 @@ Json::Value RPCHandler::doTxHistory(const Json::Value& params)
|
||||
obj["index"]=startIndex;
|
||||
|
||||
std::string sql =
|
||||
str(boost::format("SELECT * FROM Transactions ORDER BY LedgerSeq desc LIMIT %u,20")
|
||||
boost::str(boost::format("SELECT * FROM Transactions ORDER BY LedgerSeq desc LIMIT %u,20")
|
||||
% startIndex);
|
||||
|
||||
{
|
||||
|
||||
@@ -2126,7 +2126,6 @@ void RippleCalc::pathNext(PathState::ref pspCur, const int iPaths, const LedgerE
|
||||
}
|
||||
}
|
||||
|
||||
// XXX Stand alone calculation not implemented, does not calculate required input.
|
||||
TER RippleCalc::rippleCalc(
|
||||
LedgerEntrySet& lesActive, // <-> --> = Fee applied to src balance.
|
||||
STAmount& saMaxAmountAct, // <-- The computed input amount.
|
||||
@@ -2138,7 +2137,8 @@ TER RippleCalc::rippleCalc(
|
||||
const STPathSet& spsPaths,
|
||||
const bool bPartialPayment,
|
||||
const bool bLimitQuality,
|
||||
const bool bNoRippleDirect
|
||||
const bool bNoRippleDirect,
|
||||
const bool bStandAlone // True, not to delete unfundeds.
|
||||
)
|
||||
{
|
||||
RippleCalc rc(lesActive);
|
||||
@@ -2240,8 +2240,9 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: add: %d s
|
||||
terResult = temUNCERTAIN;
|
||||
}
|
||||
|
||||
STAmount saInAct = STAmount(saMaxAmountReq.getCurrency(), saMaxAmountReq.getIssuer());
|
||||
STAmount saOutAct = STAmount(saDstAmountReq.getCurrency(), saDstAmountReq.getIssuer());
|
||||
saMaxAmountAct = STAmount(saMaxAmountReq.getCurrency(), saMaxAmountReq.getIssuer());
|
||||
saDstAmountAct = STAmount(saDstAmountReq.getCurrency(), saDstAmountReq.getIssuer());
|
||||
|
||||
const LedgerEntrySet lesBase = lesActive; // Checkpoint with just fees paid.
|
||||
const uint64 uQualityLimit = bLimitQuality ? STAmount::getRate(saDstAmountReq, saMaxAmountReq) : 0;
|
||||
// When processing, don't want to complicate directory walking with deletion.
|
||||
@@ -2259,8 +2260,8 @@ int iPass = 0;
|
||||
{
|
||||
if (pspCur->uQuality)
|
||||
{
|
||||
pspCur->saInAct = saInAct; // Update to current amount processed.
|
||||
pspCur->saOutAct = saOutAct;
|
||||
pspCur->saInAct = saMaxAmountAct; // Update to current amount processed.
|
||||
pspCur->saOutAct = saDstAmountAct;
|
||||
|
||||
rc.pathNext(pspCur, vpsPaths.size(), lesCheckpoint, lesActive); // Compute increment.
|
||||
|
||||
@@ -2314,8 +2315,8 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d be
|
||||
// Record best pass' LedgerEntrySet to build off of and potentially return.
|
||||
lesActive.swapWith(pspBest->lesEntries);
|
||||
|
||||
saInAct += pspBest->saInPass;
|
||||
saOutAct += pspBest->saOutPass;
|
||||
saMaxAmountAct += pspBest->saInPass;
|
||||
saDstAmountAct += pspBest->saOutPass;
|
||||
|
||||
if (pspBest->bConsumed)
|
||||
{
|
||||
@@ -2323,13 +2324,13 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d be
|
||||
pspBest->uQuality = 0;
|
||||
}
|
||||
|
||||
if (saOutAct == saDstAmountReq)
|
||||
if (saDstAmountAct == saDstAmountReq)
|
||||
{
|
||||
// Done. Delivered requested amount.
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
else if (saInAct != saMaxAmountReq && iDry != vpsPaths.size())
|
||||
else if (saMaxAmountAct != saMaxAmountReq && iDry != vpsPaths.size())
|
||||
{
|
||||
// Have not met requested amount or max send, try to do more. Prepare for next pass.
|
||||
|
||||
@@ -2359,7 +2360,7 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d be
|
||||
lesActive = lesBase; // Revert to just fees charged.
|
||||
}
|
||||
// Partial payment ok.
|
||||
else if (!saOutAct)
|
||||
else if (!saDstAmountAct)
|
||||
{
|
||||
// No payment at all.
|
||||
terResult = tepPATH_DRY;
|
||||
@@ -2371,6 +2372,8 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d be
|
||||
}
|
||||
}
|
||||
|
||||
if (!bStandAlone)
|
||||
{
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
// Delete became unfunded offers.
|
||||
@@ -2387,6 +2390,7 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d be
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = lesActive.offerDelete(uOfferIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ protected:
|
||||
|
||||
// For offers:
|
||||
|
||||
STAmount saRateMax; // XXX Should rate be sticky for forward too?
|
||||
STAmount saRateMax;
|
||||
|
||||
// Directory
|
||||
uint256 uDirectTip; // Current directory.
|
||||
@@ -186,7 +186,8 @@ public:
|
||||
const STPathSet& spsPaths,
|
||||
const bool bPartialPayment,
|
||||
const bool bLimitQuality,
|
||||
const bool bNoRippleDirect
|
||||
const bool bNoRippleDirect,
|
||||
const bool bStandAlone
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user