Work on path finder integration.

This commit is contained in:
Arthur Britto
2012-11-30 17:14:25 -08:00
parent 07a3eafd8a
commit 6d8afeca47
5 changed files with 93 additions and 56 deletions

View File

@@ -75,6 +75,14 @@ PathOption::PathOption(PathOption::pointer other)
}
#endif
// Return true, if path is a default path with an element.
// XXX Could be determined via STAmount
bool Pathfinder::bDefaultPath(const STPath& spPath)
{
return false;
// return spPath.size() == 3 && spPath.mPath[1].mType;
}
//
// XXX Optionally, specifying a source and destination issuer might be nice. Especially, to convert between issuers. However, this
// functionality is left to the future.
@@ -88,11 +96,24 @@ Pathfinder::Pathfinder(const RippleAddress& srcAccountID, const RippleAddress& d
// If possible, returns a single path.
// --> maxSearchSteps: unused XXX
// --> maxPay: unused XXX
// <-- retPathSet: founds paths not including default paths.
// Returns true if found paths.
//
// When generating a path set blindly, don't allow the empty path, it is implied by default.
// When generating a path set for estimates, allow an empty path instead of no paths to indicate a path exists. The caller will
// need to strip the empty path when submitting the transaction.
bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet, bool bAllowEmpty)
bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet)
{
bool bFound = false;
cLog(lsDEBUG) << boost::str(boost::format("findPaths> mSrcAccountID=%s mDstAccountID=%s mDstAmount=%s mSrcCurrencyID=%s mSrcIssuerID=%s")
% RippleAddress::createHumanAccountID(mSrcAccountID)
% RippleAddress::createHumanAccountID(mDstAccountID)
% mDstAmount.getFullText()
% STAmount::createHumanCurrency(mSrcCurrencyID)
% RippleAddress::createHumanAccountID(mSrcIssuerID)
);
if (mLedger) {
std::queue<STPath> pqueue;
STPathElement ele(mSrcAccountID,
@@ -112,30 +133,45 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet
ele = path.mPath.back(); // Get the last node from the path.
// Done, if dest wants XRP and last element produces XRP.
if (!ele.mCurrencyID // Tail output is XRP
&& !mDstAmount.getCurrency()) {
if (!ele.mCurrencyID // Tail output is XRP.
&& !mDstAmount.getCurrency()) { // Which is dst currency.
// Remove implied first.
path.mPath.erase(path.mPath.begin());
// Return the path.
retPathSet.addPath(path);
if (path.size())
{
// There is an actual path element.
cLog(lsDEBUG) << "findPaths: adding: " << path.getJson(0);
retPathSet.addPath(path); // Return the path.
cLog(lsDEBUG) << "findPaths: adding: " << path.getJson(0);
}
else
{
cLog(lsDEBUG) << "findPaths: empty path: XRP->XRP";
}
return true;
}
// 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
if (ele.mAccountID == mDstAccountID // Tail is destination account.
&& ele.mCurrencyID == mDstAmount.getCurrency()) { // With correct output currency.
// Found a path to the destination.
if (!bAllowEmpty && 2 == path.mPath.size()) {
// Empty path is default. Drop it.
cLog(lsDEBUG) << "findPaths: dropping empty path.";
continue;
if (2 == path.mPath.size()) {
// Empty path is a default. Don't need to add it to return set.
cLog(lsDEBUG) << "findPaths: empty path: direct";
return true;
}
else if (bDefaultPath(path)) {
// Path is a default (implied). Don't need to add it to return set.
cLog(lsDEBUG) << "findPaths: default path: indirect: " << path.getJson(0);
return true;
}
// Remove implied first and last nodes.
@@ -246,7 +282,7 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet
cLog(lsWARNING) << boost::str(boost::format("findPaths: no ledger"));
}
return false;
return bFound;
}
#if 0

View File

@@ -52,7 +52,8 @@ class Pathfinder
public:
Pathfinder(const RippleAddress& srcAccountID, const RippleAddress& dstAccountID, const uint160& srcCurrencyID, const uint160& srcIssuerID, const STAmount& dstAmount);
// returns false if there is no path. otherwise fills out retPath
bool findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet, bool bAllowEmpty);
bool findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet);
bool bDefaultPath(const STPath& spPath);
};
// vim:ts=4

View File

@@ -54,6 +54,7 @@ Json::Value RPCHandler::rpcError(int iError)
{ rpcNO_EVENTS, "noEvents", "Current transport does not support events." },
{ rpcNO_GEN_DECRPYT, "noGenDectypt", "Password failed to decrypt master public generator." },
{ rpcNO_NETWORK, "noNetwork", "Network not available." },
{ rpcNO_PATH, "noPath", "Unable to find a ripple path." },
{ rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." },
{ rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." },
{ rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." },
@@ -784,9 +785,7 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
Pathfinder pf(raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount);
pf.findPaths(5, 1, spsComputed, true);
if (spsComputed.isEmpty())
if (!pf.findPaths(5, 1, spsComputed))
{
cLog(lsDEBUG) << "ripple_path_find: No paths found.";
}
@@ -804,11 +803,7 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
1);
saMaxAmount.negate();
// Strip empty/default path.
if (1 == spsComputed.size() && !spsComputed.begin()->size())
{
spsComputed.clear();
}
cLog(lsDEBUG) << "ripple_path_find: PATHS: " << spsComputed.size();
TER terResult =
RippleCalc::rippleCalc(
@@ -955,51 +950,54 @@ Json::Value RPCHandler::handleJSONSubmit(const Json::Value& jvRequest)
txJSON["Fee"] = (int) theConfig.FEE_ACCOUNT_CREATE;
}
if (!txJSON.isMember("Paths") && jvRequest.isMember("build_path"))
if (!txJSON.isMember("Paths") && txJSON.isMember("Amount") && jvRequest.isMember("build_path"))
{
if (txJSON["Amount"].isObject() || txJSON.isMember("SendMax"))
{ // we need a ripple path
STPathSet spsPaths;
uint160 uSrcCurrencyID;
uint160 uSrcIssuerID;
// Need a ripple path.
STPathSet spsPaths;
uint160 uSrcCurrencyID;
uint160 uSrcIssuerID;
if (txJSON.isMember("SendMax") && txJSON["SendMax"].isMember("currency"))
{
STAmount::currencyFromString(uSrcCurrencyID, txJSON["SendMax"]["currency"].asString());
}
else
{
uSrcCurrencyID = CURRENCY_XRP;
}
STAmount saSendMax;
STAmount saSend;
if (!!uSrcCurrencyID)
{
uSrcIssuerID = raSrcAddressID.getAccountID();
}
if (!txJSON.isMember("Amount") // Amount required.
|| !saSend.bSetJson(txJSON["Amount"])) // Must be valid.
return rpcError(rpcDST_AMT_MALFORMED);
STAmount dstAmount;
if (txJSON.isMember("SendMax"))
{
if (!saSendMax.bSetJson(txJSON["SendMax"]))
return rpcError(rpcINVALID_PARAMS);
}
else
{
// If no SendMax, default to Amount with sender as issuer.
saSendMax = saSend;
saSendMax.setIssuer(raSrcAddressID.getAccountID());
}
if (!dstAmount.bSetJson(txJSON["Amount"]))
{
return rpcError(rpcDST_AMT_MALFORMED);
}
Pathfinder pf(raSrcAddressID, dstAccountID, saSendMax.getCurrency(), saSendMax.getIssuer(), saSend);
Pathfinder pf(raSrcAddressID, dstAccountID, uSrcCurrencyID, uSrcIssuerID, dstAmount);
if (!pf.findPaths(5, 1, spsPaths))
{
cLog(lsDEBUG) << "payment: build_path: No paths found.";
pf.findPaths(5, 1, spsPaths, false);
return rpcError(rpcNO_PATH);
}
else
{
cLog(lsDEBUG) << "payment: build_path: " << spsPaths.getJson(0);
}
if (!spsPaths.isEmpty())
{
txJSON["Paths"]=spsPaths.getJson(0);
if(txJSON.isMember("Flags")) txJSON["Flags"]=txJSON["Flags"].asUInt() | 2;
else txJSON["Flags"]=2;
}
if (!spsPaths.isEmpty())
{
txJSON["Paths"]=spsPaths.getJson(0);
}
}
}
if(!txJSON.isMember("Sequence")) txJSON["Sequence"]=asSrc->getSeq();
if(!txJSON.isMember("Flags")) txJSON["Flags"]=0;
if (!txJSON.isMember("Sequence")) txJSON["Sequence"]=asSrc->getSeq();
if (!txJSON.isMember("Flags")) txJSON["Flags"]=0;
Ledger::pointer lpCurrent = mNetOps->getCurrentLedger();
SLE::pointer sleAccountRoot = mNetOps->getSLE(lpCurrent, Ledger::getAccountRootIndex(raSrcAddressID.getAccountID()));

View File

@@ -121,6 +121,7 @@ public:
rpcLGR_NOT_FOUND,
rpcNICKNAME_MISSING,
rpcNO_ACCOUNT,
rpcNO_PATH,
rpcPASSWD_CHANGED,
rpcSRC_MISSING,
rpcSRC_UNCLAIMED,

View File

@@ -2328,9 +2328,10 @@ TER RippleCalc::rippleCalc(
// Incrementally search paths.
// bNoRippleDirect is a slight misnomer, it really means make no ripple default path.
if (!bNoRippleDirect)
{
// Direct path.
// Build a default path. Use saDstAmountReq and saMaxAmountReq to imply nodes.
// XXX Might also make a XRP bridge by default.
PathState::pointer pspDirect = boost::make_shared<PathState>(saDstAmountReq, saMaxAmountReq, lesActive.getLedgerRef());