Rough cut at ripple_path_find.

This commit is contained in:
Arthur Britto
2012-11-22 14:03:32 -08:00
parent b7e18f367b
commit 5c1605ab35
6 changed files with 194 additions and 68 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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