diff --git a/newcoin.vcxproj b/newcoin.vcxproj
index 42abebc8cd..a3cc152b6d 100644
--- a/newcoin.vcxproj
+++ b/newcoin.vcxproj
@@ -157,6 +157,7 @@
+
diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters
index 49dc813897..e1d94cc8fd 100644
--- a/newcoin.vcxproj.filters
+++ b/newcoin.vcxproj.filters
@@ -174,6 +174,9 @@
Source Files
+
+ Source Files
+
Source Files
diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj
index e044141116..08e43f0c50 100644
--- a/ripple2010.vcxproj
+++ b/ripple2010.vcxproj
@@ -145,6 +145,7 @@
+
diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters
index dd67f928d2..6aacf1bf82 100644
--- a/ripple2010.vcxproj.filters
+++ b/ripple2010.vcxproj.filters
@@ -171,6 +171,9 @@
Source Files
+
+ Source Files
+
Source Files
diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp
index ee851aa7e3..2d0094475f 100644
--- a/src/cpp/ripple/Amount.cpp
+++ b/src/cpp/ripple/Amount.cpp
@@ -281,7 +281,7 @@ bool STAmount::setValue(const std::string& sAmount)
try
{
- if ((smMatch[2].length() + smMatch[4].length()) > 25)
+ if ((smMatch[2].length() + smMatch[4].length()) > 32)
{
cLog(lsWARNING) << "Overlong number: " << sAmount;
return false;
diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp
index 5fba686ace..268cda5725 100644
--- a/src/cpp/ripple/CallRPC.cpp
+++ b/src/cpp/ripple/CallRPC.cpp
@@ -736,6 +736,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
#endif
// Evented methods
+ { "path_find", &RPCParser::parseEvented, -1, -1 },
{ "subscribe", &RPCParser::parseEvented, -1, -1 },
{ "unsubscribe", &RPCParser::parseEvented, -1, -1 },
};
diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp
index e89b2184da..f6ce0bcf47 100644
--- a/src/cpp/ripple/JobQueue.cpp
+++ b/src/cpp/ripple/JobQueue.cpp
@@ -43,6 +43,7 @@ const char* Job::toString(JobType t)
case jtPROOFWORK: return "proofOfWork";
case jtPROPOSAL_ut: return "untrustedProposal";
case jtLEDGER_DATA: return "ledgerData";
+ case jtUPDATE_PF: return "updatePaths";
case jtCLIENT: return "clientCommand";
case jtTRANSACTION: return "transaction";
case jtPUBLEDGER: return "publishNewLedger";
diff --git a/src/cpp/ripple/JobQueue.h b/src/cpp/ripple/JobQueue.h
index 14710d95f9..76d444b92c 100644
--- a/src/cpp/ripple/JobQueue.h
+++ b/src/cpp/ripple/JobQueue.h
@@ -28,16 +28,17 @@ enum JobType
jtPROOFWORK = 4, // A proof of work demand from another server
jtPROPOSAL_ut = 5, // A proposal from an untrusted source
jtLEDGER_DATA = 6, // Received data for a ledger we're acquiring
- jtCLIENT = 7, // A websocket command from the client
- jtTRANSACTION = 8, // A transaction received from the network
- jtPUBLEDGER = 9, // Publish a fully-accepted ledger
- jtWAL = 10, // Write-ahead logging
- jtVALIDATION_t = 11, // A validation from a trusted source
- jtWRITE = 12, // Write out hashed objects
- jtTRANSACTION_l = 13, // A local transaction
- jtPROPOSAL_t = 14, // A proposal from a trusted source
- jtADMIN = 15, // An administrative operation
- jtDEATH = 16, // job of death, used internally
+ jtUPDATE_PF = 7, // Update pathfinding requests
+ jtCLIENT = 8, // A websocket command from the client
+ jtTRANSACTION = 9, // A transaction received from the network
+ jtPUBLEDGER = 10, // Publish a fully-accepted ledger
+ jtWAL = 11, // Write-ahead logging
+ jtVALIDATION_t = 12, // A validation from a trusted source
+ jtWRITE = 13, // Write out hashed objects
+ jtTRANSACTION_l = 14, // A local transaction
+ jtPROPOSAL_t = 15, // A proposal from a trusted source
+ jtADMIN = 16, // An administrative operation
+ jtDEATH = 17, // job of death, used internally
// special types not dispatched by the job pool
jtPEER = 24,
diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp
index 377650973a..97f04e1518 100644
--- a/src/cpp/ripple/LedgerMaster.cpp
+++ b/src/cpp/ripple/LedgerMaster.cpp
@@ -6,6 +6,7 @@
#include "Application.h"
#include "RippleAddress.h"
#include "Log.h"
+#include "PFRequest.h"
SETUP_LOG();
@@ -627,12 +628,20 @@ void LedgerMaster::tryPublish()
mPubThread = true;
theApp->getJobQueue().addJob(jtPUBLEDGER, "Ledger::pubThread",
BIND_TYPE(&LedgerMaster::pubThread, this));
+ mPathFindNewLedger = true;
+ if (!mPathFindThread)
+ {
+ mPathFindThread = true;
+ theApp->getJobQueue().addJob(jtUPDATE_PF, "updatePaths",
+ BIND_TYPE(&LedgerMaster::updatePaths, this));
+ }
}
}
void LedgerMaster::pubThread()
{
std::list ledgers;
+ bool published = false;
while (1)
{
@@ -644,6 +653,12 @@ void LedgerMaster::pubThread()
if (ledgers.empty())
{
mPubThread = false;
+ if (published && !mPathFindThread)
+ {
+ mPathFindThread = true;
+ theApp->getJobQueue().addJob(jtUPDATE_PF, "updatePaths",
+ BIND_TYPE(&LedgerMaster::updatePaths, this));
+ }
return;
}
}
@@ -653,8 +668,52 @@ void LedgerMaster::pubThread()
cLog(lsDEBUG) << "Publishing ledger " << l->getLedgerSeq();
setFullLedger(l); // OPTIMIZEME: This is actually more work than we need to do
theApp->getOPs().pubLedger(l);
+ published = true;
}
}
}
+void LedgerMaster::updatePaths()
+{
+ Ledger::pointer lastLedger;
+ do
+ {
+ bool newOnly = false;
+
+ {
+ boost::recursive_mutex::scoped_lock ml(mLock);
+ if (mPathFindNewLedger || (lastLedger && (lastLedger.get() != mPubLedger.get())))
+ lastLedger = mPubLedger;
+ else if (mPathFindNewRequest)
+ {
+ newOnly = true;
+ lastLedger = boost::make_shared(*mCurrentLedger, false);
+ }
+ else
+ {
+ mPathFindThread = false;
+ return;
+ }
+ lastLedger = mPubLedger;
+ mPathFindNewLedger = false;
+ mPathFindNewRequest = false;
+ }
+
+ PFRequest::updateAll(lastLedger, newOnly);
+
+ } while(1);
+}
+
+void LedgerMaster::newPFRequest()
+{
+ boost::recursive_mutex::scoped_lock ml(mLock);
+ mPathFindNewRequest = true;
+ if (!mPathFindThread)
+ {
+ mPathFindThread = true;
+ theApp->getJobQueue().addJob(jtUPDATE_PF, "updatePaths",
+ BIND_TYPE(&LedgerMaster::updatePaths, this));
+ }
+}
+
// vim:ts=4
diff --git a/src/cpp/ripple/LedgerMaster.h b/src/cpp/ripple/LedgerMaster.h
index 6c9d87122b..2d872bbdca 100644
--- a/src/cpp/ripple/LedgerMaster.h
+++ b/src/cpp/ripple/LedgerMaster.h
@@ -46,6 +46,10 @@ protected:
std::list mPubLedgers; // List of ledgers to publish
bool mPubThread; // Publish thread is running
+ bool mPathFindThread; // Pathfind thread is running
+ bool mPathFindNewLedger;
+ bool mPathFindNewRequest;
+
void applyFutureTransactions(uint32 ledgerIndex);
bool isValidTransaction(Transaction::ref trans);
bool isTransactionOnFutureList(Transaction::ref trans);
@@ -54,11 +58,13 @@ protected:
void asyncAccept(Ledger::pointer);
void missingAcquireComplete(LedgerAcquire::pointer);
void pubThread();
+ void updatePaths();
public:
LedgerMaster() : mHeldTransactions(uint256()), mMissingSeq(0),
- mMinValidations(0), mLastValidateSeq(0), mPubThread(false)
+ mMinValidations(0), mLastValidateSeq(0), mPubThread(false),
+ mPathFindThread(false), mPathFindNewLedger(false), mPathFindNewRequest(false)
{ ; }
uint32 getCurrentLedgerIndex();
@@ -155,6 +161,7 @@ public:
void checkAccept(const uint256& hash);
void checkAccept(const uint256& hash, uint32 seq);
void tryPublish();
+ void newPFRequest();
static bool shouldAcquire(uint32 currentLedgerID, uint32 ledgerHistory, uint32 targetLedger);
};
diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h
index 6469d8ff5e..6db090981f 100644
--- a/src/cpp/ripple/NetworkOPs.h
+++ b/src/cpp/ripple/NetworkOPs.h
@@ -21,6 +21,7 @@
class Peer;
class LedgerConsensus;
+class PFRequest;
DEFINE_INSTANCE(InfoSub);
@@ -29,6 +30,7 @@ class InfoSub : public IS_INSTANCE(InfoSub)
protected:
boost::unordered_set mSubAccountInfo;
boost::unordered_set mSubAccountTransaction;
+ boost::shared_ptr mPFRequest;
boost::mutex mLockInfo;
@@ -64,6 +66,21 @@ public:
mSubAccountInfo.insert(addr);
}
+
+ void clearPFRequest()
+ {
+ mPFRequest.reset();
+ }
+
+ void setPFRequest(const boost::shared_ptr& req)
+ {
+ mPFRequest = req;
+ }
+
+ const boost::shared_ptr& getPFRequest()
+ {
+ return mPFRequest;
+ }
};
class NetworkOPs
diff --git a/src/cpp/ripple/PFRequest.cpp b/src/cpp/ripple/PFRequest.cpp
new file mode 100644
index 0000000000..cc1b827ee1
--- /dev/null
+++ b/src/cpp/ripple/PFRequest.cpp
@@ -0,0 +1,328 @@
+#include "PFRequest.h"
+
+#include "NetworkOPs.h"
+#include "RPCErr.h"
+#include "Ledger.h"
+#include "Application.h"
+#include "Pathfinder.h"
+#include "RippleCalc.h"
+#include "LedgerFormats.h"
+
+SETUP_LOG();
+
+boost::recursive_mutex PFRequest::sLock;
+std::set PFRequest::sRequests;
+
+PFRequest::PFRequest(const boost::shared_ptr& subscriber) :
+ wpSubscriber(subscriber), jvStatus(Json::objectValue), bValid(false), bNew(true)
+{
+ ;
+}
+
+bool PFRequest::isValid()
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ return bValid;
+}
+
+bool PFRequest::isNew()
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ return bNew;
+}
+
+bool PFRequest::isValid(Ledger::ref lrLedger)
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ bValid = raSrcAccount.isSet() && raDstAccount.isSet() && saDstAmount.isPositive();
+ if (bValid)
+ {
+ AccountState::pointer asSrc = theApp->getOPs().getAccountState(lrLedger, raSrcAccount);
+ if (!asSrc)
+ { // no source account
+ bValid = false;
+ jvStatus = rpcError(rpcSRC_ACT_NOT_FOUND);
+ }
+ else
+ {
+ AccountState::pointer asDst = theApp->getOPs().getAccountState(lrLedger, raDstAccount);
+ Json::Value jvDestCur;
+ if (!asDst)
+ { // no destination account
+ jvDestCur.append(Json::Value("XRP"));
+ if(!saDstAmount.isNative())
+ { // only XRP can be send to a non-existent account
+ bValid = false;
+ jvStatus = rpcError(rpcACT_NOT_FOUND);
+ }
+ else if (saDstAmount < STAmount(lrLedger->getReserve(0)))
+ { // payment must meet reserve
+ bValid = false;
+ jvStatus = rpcError(rpcDST_AMT_MALFORMED);
+ }
+ }
+ else
+ {
+ boost::unordered_set usDestCurrID = usAccountDestCurrencies(raDstAccount, lrLedger, true);
+ BOOST_FOREACH(const uint160& uCurrency, usDestCurrID)
+ jvDestCur.append(STAmount::createHumanCurrency(uCurrency));
+ jvStatus["destination_tag"] = (asDst->peekSLE().getFlags() & lsfRequireDestTag) != 0;
+ }
+ jvStatus["destination_currencies"] = jvDestCur;
+ }
+ }
+ jvStatus["ledger_hash"] = lrLedger->getHash().GetHex();
+ jvStatus["ledger_index"] = lrLedger->getLedgerSeq();
+ return bValid;
+}
+
+Json::Value PFRequest::doCreate(Ledger::ref lrLedger, const Json::Value& value)
+{
+ assert(lrLedger->isClosed());
+
+ Json::Value status;
+ bool mValid;
+
+ {
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ if (parseJson(value, true) != PFR_PJ_INVALID)
+ {
+ mValid = isValid(lrLedger);
+ if (mValid)
+ {
+ RLCache::pointer cache = boost::make_shared(lrLedger);
+ doUpdate(cache, true);
+ }
+ }
+ else
+ mValid = false;
+ }
+
+ if (mValid)
+ {
+ cLog(lsINFO) << "Request created: " << raSrcAccount.humanAccountID() <<
+ " -> " << raDstAccount.humanAccountID();
+ cLog(lsINFO) << "Deliver: " << saDstAmount.getFullText();
+
+ boost::recursive_mutex::scoped_lock sl(sLock);
+ sRequests.insert(shared_from_this());
+ }
+
+ return jvStatus;
+}
+
+int PFRequest::parseJson(const Json::Value& jvParams, bool complete)
+{
+ int ret = PFR_PJ_NOCHANGE;
+
+ if (jvParams.isMember("source_account"))
+ {
+ if (!raSrcAccount.setAccountID(jvParams["source_account"].asString()))
+ {
+ jvStatus = rpcError(rpcSRC_ACT_MALFORMED);
+ return PFR_PJ_INVALID;
+ }
+ }
+ else if (complete)
+ {
+ jvStatus = rpcError(rpcSRC_ACT_MISSING);
+ return PFR_PJ_INVALID;
+ }
+
+ if (jvParams.isMember("destination_account"))
+ {
+ if (!raDstAccount.setAccountID(jvParams["destination_account"].asString()))
+ {
+ jvStatus = rpcError(rpcDST_ACT_MALFORMED);
+ return PFR_PJ_INVALID;
+ }
+ }
+ else if (complete)
+ {
+ jvStatus = rpcError(rpcDST_ACT_MISSING);
+ return PFR_PJ_INVALID;
+ }
+
+ if (jvParams.isMember("destination_amount"))
+ {
+ if (!saDstAmount.bSetJson(jvParams["destination_amount"]) ||
+ (saDstAmount.getCurrency().isZero() && saDstAmount.getIssuer().isNonZero()) ||
+ (saDstAmount.getCurrency() == CURRENCY_BAD) ||
+ !saDstAmount.isPositive())
+ {
+ jvStatus = rpcError(rpcDST_AMT_MALFORMED);
+ return PFR_PJ_INVALID;
+ }
+ }
+ else if (complete)
+ {
+ jvStatus = rpcError(rpcDST_ACT_MISSING);
+ return PFR_PJ_INVALID;
+ }
+
+ if (jvParams.isMember("source_currencies"))
+ {
+ const Json::Value& jvSrcCur = jvParams["source_currencies"];
+ if (!jvSrcCur.isArray())
+ {
+ jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
+ return PFR_PJ_INVALID;
+ }
+ sciSourceCurrencies.clear();
+ for (unsigned i = 0; i < jvSrcCur.size(); ++i)
+ {
+ const Json::Value& jvCur = jvSrcCur[i];
+ uint160 uCur, uIss;
+ if (!jvCur.isMember("currency") || !STAmount::currencyFromString(uCur, jvCur["currency"].asString()))
+ {
+ jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
+ return PFR_PJ_INVALID;
+ }
+ if (jvCur.isMember("issuer") && !STAmount::issuerFromString(uIss, jvCur["issuer"].asString()))
+ {
+ jvStatus = rpcError(rpcSRC_ISR_MALFORMED);
+ }
+ if (uCur.isZero() && uIss.isNonZero())
+ {
+ jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
+ return PFR_PJ_INVALID;
+ }
+ sciSourceCurrencies.insert(currIssuer_t(uCur, uIss));
+ }
+ }
+
+ return ret;
+}
+Json::Value PFRequest::doClose(const Json::Value&)
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ return jvStatus;
+}
+
+Json::Value PFRequest::doStatus(const Json::Value&)
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ return jvStatus;
+}
+
+bool PFRequest::doUpdate(RLCache::ref cache, bool fast)
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ jvStatus = Json::objectValue;
+ if (!isValid(cache->getLedger()))
+ return false;
+ if (!fast)
+ bNew = false;
+
+ std::set sourceCurrencies(sciSourceCurrencies);
+ if (sourceCurrencies.empty())
+ {
+ boost::unordered_set usCurrencies =
+ usAccountSourceCurrencies(raSrcAccount, cache->getLedger(), true);
+ bool sameAccount = raSrcAccount == raDstAccount;
+ BOOST_FOREACH(const uint160& c, usCurrencies)
+ {
+ if (!sameAccount || (c != saDstAmount.getCurrency()))
+ {
+ if (c.isZero())
+ sourceCurrencies.insert(std::make_pair(c, ACCOUNT_XRP));
+ else
+ sourceCurrencies.insert(std::make_pair(c, raSrcAccount.getAccountID()));
+ }
+ }
+ }
+
+ jvStatus["source_account"] = raSrcAccount.humanAccountID();
+ jvStatus["destination_account"] = raDstAccount.humanAccountID();
+
+ Json::Value jvArray = Json::arrayValue;
+
+ BOOST_FOREACH(const currIssuer_t& currIssuer, sourceCurrencies)
+ {
+ {
+ STAmount test(currIssuer.first, currIssuer.second, 1);
+ cLog(lsDEBUG) << "Trying to find paths: " << test.getFullText();
+ }
+ bool valid;
+ STPathSet spsPaths;
+ Pathfinder pf(cache, raSrcAccount, raDstAccount,
+ currIssuer.first, currIssuer.second, saDstAmount, valid);
+ tLog(!valid, lsINFO) << "PF request not valid";
+ if (valid && pf.findPaths(theConfig.PATH_SEARCH_SIZE - (fast ? 0 : 1), 3, spsPaths))
+ {
+ LedgerEntrySet lesSandbox(cache->getLedger(), tapNONE);
+ std::vector vpsExpanded;
+ STAmount saMaxAmountAct;
+ STAmount saDstAmountAct;
+ STAmount saMaxAmount(currIssuer.first,
+ currIssuer.second.isNonZero() ? currIssuer.second :
+ (currIssuer.first.isZero() ? ACCOUNT_XRP : raSrcAccount.getAccountID()), 1);
+ saMaxAmount.negate();
+ cLog(lsDEBUG) << "Paths found, calling rippleCalc";
+ TER terResult = RippleCalc::rippleCalc(lesSandbox, saMaxAmountAct, saDstAmountAct,
+ vpsExpanded, saMaxAmount, saDstAmount, raDstAccount.getAccountID(), raSrcAccount.getAccountID(),
+ spsPaths, false, false, false, true);
+ if (terResult == tesSUCCESS)
+ {
+ Json::Value jvEntry(Json::objectValue);
+ jvEntry["source_amount"] = saMaxAmountAct.getJson(0);
+ jvEntry["paths_computed"] = spsPaths.getJson(0);
+ jvArray.append(jvEntry);
+ }
+ else
+ {
+ cLog(lsINFO) << "rippleCalc returns " << transHuman(terResult);
+ }
+ }
+ else
+ {
+ cLog(lsINFO) << "No paths found";
+ }
+ }
+ jvStatus["alternatives"] = jvArray;
+ return true;
+}
+
+void PFRequest::updateAll(Ledger::ref ledger, bool newOnly)
+{
+ std::set requests;
+
+ {
+ boost::recursive_mutex::scoped_lock sl(sLock);
+ requests = sRequests;
+ }
+
+ if (requests.empty())
+ return;
+
+ RLCache::pointer cache = boost::make_shared(ledger);
+
+ BOOST_FOREACH(wref wRequest, requests)
+ {
+ bool remove = true;
+ PFRequest::pointer pRequest = wRequest.lock();
+ if (pRequest && (!newOnly || pRequest->isNew()))
+ {
+ InfoSub::pointer ipSub = pRequest->wpSubscriber.lock();
+ if (ipSub)
+ {
+ Json::Value update;
+ {
+ boost::recursive_mutex::scoped_lock sl(pRequest->mLock);
+ pRequest->doUpdate(cache, false);
+ update = pRequest->jvStatus;
+ }
+ update["type"] = "path_find";
+ ipSub->send(update, false);
+ remove = false;
+ }
+ }
+ if (remove)
+ {
+ boost::recursive_mutex::scoped_lock sl(sLock);
+ sRequests.erase(wRequest);
+ }
+ }
+}
+
+// vim:ts=4
diff --git a/src/cpp/ripple/PFRequest.h b/src/cpp/ripple/PFRequest.h
new file mode 100644
index 0000000000..402bb02cc0
--- /dev/null
+++ b/src/cpp/ripple/PFRequest.h
@@ -0,0 +1,84 @@
+#ifndef _PFREQUEST__H
+#define _PFREQUEST__H
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "../json/value.h"
+
+#include "uint256.h"
+#include "RippleAddress.h"
+#include "SerializedTypes.h"
+#include "Pathfinder.h"
+
+// A pathfinding request submitted by a client
+// The request issuer must maintain a strong pointer
+
+class Ledger;
+class InfoSub;
+class STAmount;
+class RLCache;
+
+// Return values from parseJson <0 = invalid, >0 = valid
+#define PFR_PJ_INVALID -1
+#define PFR_PJ_NOCHANGE 0
+#define PFR_PJ_CHANGE 1
+
+class PFRequest : public boost::enable_shared_from_this
+{
+public:
+ typedef boost::weak_ptr wptr;
+ typedef boost::shared_ptr pointer;
+ typedef const pointer& ref;
+ typedef const wptr& wref;
+ typedef std::pair currIssuer_t;
+
+
+protected:
+ boost::recursive_mutex mLock;
+ boost::weak_ptr wpSubscriber; // Who this request came from
+ Json::Value jvStatus; // Last result
+
+ // Client request parameters
+ RippleAddress raSrcAccount;
+ RippleAddress raDstAccount;
+ STAmount saDstAmount;
+ std::set sciSourceCurrencies;
+ std::vector vjvBridges;
+
+ bool bValid;
+ bool bNew;
+
+ // Track all requests
+ static std::set sRequests;
+ static boost::recursive_mutex sLock;
+
+ void setValid();
+ int parseJson(const Json::Value&, bool complete);
+
+public:
+
+ PFRequest(const boost::shared_ptr& subscriber);
+
+ bool isValid(const boost::shared_ptr&);
+ bool isValid();
+ bool isNew();
+ Json::Value getStatus();
+
+ Json::Value doCreate(const boost::shared_ptr&, const Json::Value&);
+ Json::Value doClose(const Json::Value&);
+ Json::Value doStatus(const Json::Value&);
+
+ bool doUpdate(const boost::shared_ptr&, bool fast); // update jvStatus
+
+ static void updateAll(const boost::shared_ptr& ledger, bool newOnly);
+};
+
+#endif
+
+// vim:ts=4
diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp
index 9f3c604a8d..7101f9458a 100644
--- a/src/cpp/ripple/Pathfinder.cpp
+++ b/src/cpp/ripple/Pathfinder.cpp
@@ -138,7 +138,7 @@ static int getEffectiveLength(const STPath& spPath)
return length;
}
-Pathfinder::Pathfinder(Ledger::ref ledger,
+Pathfinder::Pathfinder(RLCache::ref cache,
const RippleAddress& uSrcAccountID, const RippleAddress& uDstAccountID,
const uint160& uSrcCurrencyID, const uint160& uSrcIssuerID, const STAmount& saDstAmount, bool& bValid)
: mSrcAccountID(uSrcAccountID.getAccountID()),
@@ -147,7 +147,7 @@ Pathfinder::Pathfinder(Ledger::ref ledger,
mSrcCurrencyID(uSrcCurrencyID),
mSrcIssuerID(uSrcIssuerID),
mSrcAmount(uSrcCurrencyID, uSrcIssuerID, 1u, 0, true),
- mLedger(ledger)
+ mLedger(cache->getLedger()), mRLCache(cache)
{
if (((mSrcAccountID == mDstAccountID) && (mSrcCurrencyID == mDstAmount.getCurrency())) || mDstAmount.isZero())
@@ -443,7 +443,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
bool bRequireAuth = isSetBit(sleEnd->getFieldU32(sfFlags), lsfRequireAuth);
bool dstCurrency = speEnd.mCurrencyID == mDstAmount.getCurrency();
- AccountItems& rippleLines(getRippleLines(speEnd.mAccountID));
+ AccountItems& rippleLines(mRLCache->getRippleLines(speEnd.mAccountID));
std::vector< std::pair > candidates;
candidates.reserve(rippleLines.getItems().size());
@@ -776,8 +776,9 @@ boost::unordered_set usAccountDestCurrencies(const RippleAddress& raAcc
return usCurrencies;
}
-AccountItems& Pathfinder::getRippleLines(const uint160& accountID)
+AccountItems& RLCache::getRippleLines(const uint160& accountID)
{
+ boost::mutex::scoped_lock sl(mLock);
boost::unordered_map::iterator it = mRLMap.find(accountID);
if (it == mRLMap.end())
it = mRLMap.insert(std::make_pair(accountID, boost::make_shared
@@ -799,7 +800,7 @@ int Pathfinder::getPathsOut(const uint160& currencyID, const uint160& accountID,
return it->second;
int count = 0;
- AccountItems& rippleLines(getRippleLines(accountID));
+ AccountItems& rippleLines(mRLCache->getRippleLines(accountID));
BOOST_FOREACH(AccountItem::ref item, rippleLines.getItems())
{
RippleState* rspEntry = (RippleState*) item.get();
diff --git a/src/cpp/ripple/Pathfinder.h b/src/cpp/ripple/Pathfinder.h
index acc5e5a7d9..3ddbb486b1 100644
--- a/src/cpp/ripple/Pathfinder.h
+++ b/src/cpp/ripple/Pathfinder.h
@@ -35,6 +35,23 @@ public:
};
#endif
+class RLCache
+{
+protected:
+ boost::mutex mLock;
+ Ledger::pointer mLedger;
+ boost::unordered_map mRLMap;
+
+public:
+ typedef boost::shared_ptr pointer;
+ typedef const pointer& ref;
+
+ RLCache(Ledger::ref l) : mLedger(l) { ; }
+ Ledger::ref getLedger() { return mLedger; }
+
+ AccountItems& getRippleLines(const uint160& accountID);
+};
+
class Pathfinder
{
uint160 mSrcAccountID;
@@ -47,6 +64,7 @@ class Pathfinder
Ledger::pointer mLedger;
PathState::pointer mPsDefault;
LoadEvent::pointer mLoadMonitor;
+ RLCache::pointer mRLCache;
boost::unordered_map mRLMap;
boost::unordered_map, int> mPOMap;
@@ -63,13 +81,11 @@ class Pathfinder
bool matchesOrigin(const uint160& currency, const uint160& issuer);
- AccountItems& getRippleLines(const uint160& accountID);
-
int getPathsOut(const uint160& currency, const uint160& accountID,
bool isAuthRequired, bool isDestCurrency, const uint160& dest);
public:
- Pathfinder(Ledger::ref ledger,
+ Pathfinder(RLCache::ref cache,
const RippleAddress& srcAccountID, const RippleAddress& dstAccountID,
const uint160& srcCurrencyID, const uint160& srcIssuerID, const STAmount& dstAmount, bool& bValid);
diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp
index 01a6d5a026..566b420ce8 100644
--- a/src/cpp/ripple/RPCErr.cpp
+++ b/src/cpp/ripple/RPCErr.cpp
@@ -54,6 +54,7 @@ Json::Value rpcError(int iError, Json::Value jvResult)
{ 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." },
+ { rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress." },
{ rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." },
{ rpcNOT_SUPPORTED, "notSupported", "Operation not supported." },
{ rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." },
diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h
index 39a2747ae3..fbfc9549fe 100644
--- a/src/cpp/ripple/RPCErr.h
+++ b/src/cpp/ripple/RPCErr.h
@@ -42,6 +42,7 @@ enum {
// Malformed command
rpcINVALID_PARAMS,
rpcUNKNOWN_COMMAND,
+ rpcNO_PF_REQUEST,
// Bad parameter
rpcACT_BITCOIN,
diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp
index a24e49cc3f..6988ffa602 100644
--- a/src/cpp/ripple/RPCHandler.cpp
+++ b/src/cpp/ripple/RPCHandler.cpp
@@ -22,6 +22,7 @@
#include "NicknameState.h"
#include "InstanceCounter.h"
#include "Offer.h"
+#include "PFRequest.h"
SETUP_LOG();
@@ -180,7 +181,8 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit)
{
ScopedUnlock su(theApp->getMasterLock());
bool bValid;
- Pathfinder pf(lSnapshot, raSrcAddressID, dstAccountID,
+ RLCache::pointer cache = boost::make_shared(lSnapshot);
+ Pathfinder pf(cache, raSrcAddressID, dstAccountID,
saSendMax.getCurrency(), saSendMax.getIssuer(), saSend, bValid);
if (!bValid || !pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsPaths))
@@ -1167,6 +1169,49 @@ Json::Value RPCHandler::doRandom(Json::Value jvRequest, int& cost, ScopedLock& M
}
}
+Json::Value RPCHandler::doPathFind(Json::Value jvRequest, int& cost, ScopedLock& MasterLockHolder)
+{
+ if (!jvRequest.isMember("subcommand") || !jvRequest["subcommand"].isString())
+ return rpcError(rpcINVALID_PARAMS);
+
+ if (!mInfoSub)
+ return rpcError(rpcNO_EVENTS);
+
+ std::string sSubCommand = jvRequest["subcommand"].asString();
+
+ if (sSubCommand == "create")
+ {
+ mInfoSub->clearPFRequest();
+ PFRequest::pointer request = boost::make_shared(mInfoSub);
+ Json::Value result = request->doCreate(mNetOps->getClosedLedger(), jvRequest);
+ if (request->isValid())
+ {
+ mInfoSub->setPFRequest(request);
+ theApp->getLedgerMaster().newPFRequest();
+ }
+ return result;
+ }
+
+ if (sSubCommand == "close")
+ {
+ PFRequest::pointer request = mInfoSub->getPFRequest();
+ if (!request)
+ return rpcError(rpcNO_PF_REQUEST);
+ mInfoSub->clearPFRequest();
+ return request->doClose(jvRequest);
+ }
+
+ if (sSubCommand == "status")
+ {
+ PFRequest::pointer request = mInfoSub->getPFRequest();
+ if (!request)
+ return rpcNO_PF_REQUEST;
+ return request->doStatus(jvRequest);
+ }
+
+ return rpcError(rpcINVALID_PARAMS);
+}
+
// TODO:
// - Add support for specifying non-endpoint issuer.
// - Return fully expanded path with proof.
@@ -1268,6 +1313,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost, Scope
jvResult["destination_account"] = raDst.humanAccountID();
Json::Value jvArray(Json::arrayValue);
+ RLCache::pointer cache = boost::make_shared(lSnapShot);
for (unsigned int i=0; i != jvSrcCurrencies.size(); ++i) {
Json::Value jvSource = jvSrcCurrencies[i];
@@ -1303,7 +1349,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost, Scope
STPathSet spsComputed;
bool bValid;
- Pathfinder pf(lSnapShot, raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount, bValid);
+ Pathfinder pf(cache, raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount, bValid);
if (!bValid || !pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsComputed))
{
@@ -3293,6 +3339,7 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole, int &
// { "nickname_info", &RPCHandler::doNicknameInfo, false, optCurrent },
{ "owner_info", &RPCHandler::doOwnerInfo, false, optCurrent },
{ "peers", &RPCHandler::doPeers, true, optNone },
+ { "path_find", &RPCHandler::doPathFind, false, optCurrent },
{ "ping", &RPCHandler::doPing, false, optNone },
// { "profile", &RPCHandler::doProfile, false, optCurrent },
{ "random", &RPCHandler::doRandom, false, optNone },
diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h
index 710ed12127..f5b73c39be 100644
--- a/src/cpp/ripple/RPCHandler.h
+++ b/src/cpp/ripple/RPCHandler.h
@@ -69,6 +69,7 @@ class RPCHandler
Json::Value doNicknameInfo(Json::Value params, int& cost, ScopedLock& mlh);
Json::Value doOwnerInfo(Json::Value params, int& cost, ScopedLock& mlh);
Json::Value doPeers(Json::Value params, int& cost, ScopedLock& mlh);
+ Json::Value doPathFind(Json::Value params, int& cost, ScopedLock& mlh);
Json::Value doPing(Json::Value params, int& cost, ScopedLock& mlh);
Json::Value doProfile(Json::Value params, int& cost, ScopedLock& mlh);
Json::Value doRandom(Json::Value jvRequest, int& cost, ScopedLock& mlh);