From 24bb7a2016258c2c53d5fd1179734f643e6f127c Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 29 Apr 2013 16:42:26 -0700 Subject: [PATCH 01/16] New pathfinder API work. --- src/cpp/ripple/PFRequest.h | 61 +++++++++++++++++++++++++++++++++++ src/cpp/ripple/RPCHandler.cpp | 25 ++++++++++++++ src/cpp/ripple/RPCHandler.h | 1 + 3 files changed, 87 insertions(+) create mode 100644 src/cpp/ripple/PFRequest.h diff --git a/src/cpp/ripple/PFRequest.h b/src/cpp/ripple/PFRequest.h new file mode 100644 index 0000000000..9c0f4d0b02 --- /dev/null +++ b/src/cpp/ripple/PFRequest.h @@ -0,0 +1,61 @@ +#ifndef _PFREQUEST__H +#define _PFREQUEST__H + +#include +#include + +#include +#include +#include + +#include "../json/value.h" + +#include "uint256.h" +#include "RippleAddress.h" +#include "SerializedTypes.h" + +// A pathfinding request submitted by a client +// The request issuer must maintain a strong pointer + +class Ledger; +class InfoSub; +class STAmount; + +class PFRequest +{ +public: + typedef boost::weak_ptr wptr; + typedef boost::shared_ptr pointer; + typedef const pointer& ref; + typedef std::pair currIssuer_t; + + +protected: + 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; + + // Track all requests + static std::set sRequests; + static boost::recursive_mutex sLock; + +public: + + PFRequest(const boost::shared_ptr& subscriber, Json::Value request); + ~PFRequest(); + + Json::Value create(const Json::Value&); + Json::Value close(const Json::Value&); + Json::Value status(const Json::Value&); + void update(); + + static void updateAll(const boost::shared_ptr &); +}; + +#endif diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index e5af79c75a..fdd87534d3 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1166,6 +1166,30 @@ 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); + std::string sSubCommand = jvRequest["subcommand"].asString(); + + if (sSubCommand == "create") + { + // WRITEME + } + + if (sSubCommand == "close") + { + // WRITEME + } + + if (sSubCommand == "status") + { + // WRITEME + } + + return rpcError(rpcINVALID_PARAMS); +} + // TODO: // - Add support for specifying non-endpoint issuer. // - Return fully expanded path with proof. @@ -3292,6 +3316,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); From 1e5437a719e9e4ed0dc2aa24c9e6c41612e48f1b Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 29 Apr 2013 16:42:42 -0700 Subject: [PATCH 02/16] Missing from previous commit. --- src/cpp/ripple/NetworkOPs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 6469d8ff5e..482bb48b87 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; From 50d40115e5f6415c7ebeb3ff524cbd58d3a2d80f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 29 Apr 2013 20:27:08 -0700 Subject: [PATCH 03/16] Updates. --- src/cpp/ripple/PFRequest.cpp | 21 +++++++++++++++++++++ src/cpp/ripple/PFRequest.h | 24 +++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 src/cpp/ripple/PFRequest.cpp diff --git a/src/cpp/ripple/PFRequest.cpp b/src/cpp/ripple/PFRequest.cpp new file mode 100644 index 0000000000..3ba35bda51 --- /dev/null +++ b/src/cpp/ripple/PFRequest.cpp @@ -0,0 +1,21 @@ +#include "PFRequest.h" + +#include "NetworkOPs.h" + +PFRequest::PFRequest(const boost::shared_ptr& subscriber, Json::Value request) : + wpSubscriber(subscriber), jvStatus(Json::objectValue), bValid(false) +{ + if (parseJson(request) == PFR_PJ_COMPLETE) + bValid = true; +} + +bool PFRequest::isValid() +{ + boost::recursive_mutex::scoped_lock sl(mLock); + return bValid; +} + +int PFRequest::parseJson(const Json::Value& jvParams) +{ + return 0; +} diff --git a/src/cpp/ripple/PFRequest.h b/src/cpp/ripple/PFRequest.h index 9c0f4d0b02..96a187b1c5 100644 --- a/src/cpp/ripple/PFRequest.h +++ b/src/cpp/ripple/PFRequest.h @@ -21,6 +21,12 @@ class Ledger; class InfoSub; class STAmount; +// Return values from parseJson +#define PFR_PJ_COMPLETE 0 +#define PFR_PJ_NOCHANGE 1 +#define PFR_PJ_CHANGE 2 +#define PFR_PJ_INVALID 3 + class PFRequest { public: @@ -31,6 +37,7 @@ public: protected: + boost::recursive_mutex mLock; boost::weak_ptr wpSubscriber; // Who this request came from Json::Value jvStatus; // Last result @@ -41,19 +48,26 @@ protected: std::set sciSourceCurrencies; std::vector vjvBridges; + bool bValid; + // Track all requests static std::set sRequests; static boost::recursive_mutex sLock; + int parseJson(const Json::Value&); + public: PFRequest(const boost::shared_ptr& subscriber, Json::Value request); - ~PFRequest(); - Json::Value create(const Json::Value&); - Json::Value close(const Json::Value&); - Json::Value status(const Json::Value&); - void update(); + bool isValid(); + Json::Value getStatus(); + + Json::Value doCreate(const Json::Value&); + Json::Value doClose(const Json::Value&); + Json::Value doStatus(const Json::Value&); + + void doUpdate(); static void updateAll(const boost::shared_ptr &); }; From 707f914d32ac6020369a8e13c9b2c7907c7bb936 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 29 Apr 2013 23:31:25 -0700 Subject: [PATCH 04/16] Some new PF API support. --- src/cpp/ripple/NetworkOPs.h | 15 +++++++ src/cpp/ripple/PFRequest.cpp | 82 ++++++++++++++++++++++++++++++++--- src/cpp/ripple/PFRequest.h | 22 ++++++---- src/cpp/ripple/RPCErr.cpp | 1 + src/cpp/ripple/RPCErr.h | 1 + src/cpp/ripple/RPCHandler.cpp | 19 ++++++-- 6 files changed, 123 insertions(+), 17 deletions(-) diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 482bb48b87..6db090981f 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -66,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 index 3ba35bda51..c86858b4aa 100644 --- a/src/cpp/ripple/PFRequest.cpp +++ b/src/cpp/ripple/PFRequest.cpp @@ -1,21 +1,93 @@ #include "PFRequest.h" #include "NetworkOPs.h" +#include "RPCErr.h" -PFRequest::PFRequest(const boost::shared_ptr& subscriber, Json::Value request) : +boost::recursive_mutex PFRequest::sLock; +std::set PFRequest::sRequests; + +PFRequest::PFRequest(const boost::shared_ptr& subscriber) : wpSubscriber(subscriber), jvStatus(Json::objectValue), bValid(false) { - if (parseJson(request) == PFR_PJ_COMPLETE) - bValid = true; + ; } bool PFRequest::isValid() { boost::recursive_mutex::scoped_lock sl(mLock); + bValid = raSrcAccount.isSet() && raDstAccount.isSet() && saDstAmount.isPositive(); return bValid; } -int PFRequest::parseJson(const Json::Value& jvParams) +Json::Value PFRequest::doCreate(const Json::Value& value) { - return 0; + Json::Value status; + bool mValid; + + { + boost::recursive_mutex::scoped_lock sl(mLock); + parseJson(value, true); + status = jvStatus; + mValid = isValid(); + } + + if (mValid) + { + 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")) + { + } + else if (complete) + { + jvStatus = rpcSRC_ACT_MISSING; + return PFR_PJ_INVALID; + } + + if (jvParams.isMember("destination_account")) + { + } + else if (complete) + { + jvStatus = rpcDST_ACT_MISSING; + return PFR_PJ_INVALID; + } + + if (jvParams.isMember("destination_amount")) + { + } + else if (complete) + { + } + + if (jvParams.isMember("source_currencies")) + { + } + + + + 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; +} + +// vim:ts=4 diff --git a/src/cpp/ripple/PFRequest.h b/src/cpp/ripple/PFRequest.h index 96a187b1c5..114fc5e343 100644 --- a/src/cpp/ripple/PFRequest.h +++ b/src/cpp/ripple/PFRequest.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -21,13 +22,12 @@ class Ledger; class InfoSub; class STAmount; -// Return values from parseJson -#define PFR_PJ_COMPLETE 0 -#define PFR_PJ_NOCHANGE 1 -#define PFR_PJ_CHANGE 2 -#define PFR_PJ_INVALID 3 +// 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 +class PFRequest : public boost::enable_shared_from_this { public: typedef boost::weak_ptr wptr; @@ -54,11 +54,12 @@ protected: static std::set sRequests; static boost::recursive_mutex sLock; - int parseJson(const Json::Value&); + void setValid(); + int parseJson(const Json::Value&, bool complete); public: - PFRequest(const boost::shared_ptr& subscriber, Json::Value request); + PFRequest(const boost::shared_ptr& subscriber); bool isValid(); Json::Value getStatus(); @@ -67,9 +68,12 @@ public: Json::Value doClose(const Json::Value&); Json::Value doStatus(const Json::Value&); - void doUpdate(); + void doUpdate(); // do an update + void trigger(); // schedule an update static void updateAll(const boost::shared_ptr &); }; #endif + +// vim:ts=4 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 fdd87534d3..64e29bdfb1 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -21,6 +21,7 @@ #include "NicknameState.h" #include "InstanceCounter.h" #include "Offer.h" +#include "PFRequest.h" SETUP_LOG(); @@ -1174,17 +1175,29 @@ Json::Value RPCHandler::doPathFind(Json::Value jvRequest, int& cost, ScopedLock& if (sSubCommand == "create") { - // WRITEME + mInfoSub->clearPFRequest(); + PFRequest::pointer request = boost::make_shared(mInfoSub); + Json::Value result = request->doCreate(jvRequest); + if (request->isValid()) + mInfoSub->setPFRequest(request); + return result; } if (sSubCommand == "close") { - // WRITEME + PFRequest::pointer request = mInfoSub->getPFRequest(); + if (!request) + return rpcNO_PF_REQUEST; + mInfoSub->clearPFRequest(); + return request->doClose(jvRequest); } if (sSubCommand == "status") { - // WRITEME + PFRequest::pointer request = mInfoSub->getPFRequest(); + if (!request) + return rpcNO_PF_REQUEST; + return request->doStatus(jvRequest); } return rpcError(rpcINVALID_PARAMS); From dac85af5e38c2535b35e764d0ad128a6430f5283 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 30 Apr 2013 15:04:47 -0700 Subject: [PATCH 05/16] New PF Json parser work. --- src/cpp/ripple/PFRequest.cpp | 49 ++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/cpp/ripple/PFRequest.cpp b/src/cpp/ripple/PFRequest.cpp index c86858b4aa..54f9371c8d 100644 --- a/src/cpp/ripple/PFRequest.cpp +++ b/src/cpp/ripple/PFRequest.cpp @@ -46,38 +46,77 @@ int PFRequest::parseJson(const Json::Value& jvParams, bool complete) 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 = rpcSRC_ACT_MISSING; + jvStatus = rpcError(rpcSRC_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember("destination_account")) { + if (!raDstAccount.setAccountID(jvParams["source_account"].asString())) + { + jvStatus = rpcError(rpcDST_ACT_MALFORMED); + return PFR_PJ_INVALID; + } } else if (complete) { - jvStatus = rpcDST_ACT_MISSING; + 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().isZero()) || + (saDstAmount.getCurrency() == CURRENCY_BAD)) + { + 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")) + { + // parse issuer WRITEME + } + // sanity check WRITEME + // insert in set WRIEME + } } - - return ret; } - Json::Value PFRequest::doClose(const Json::Value&) { boost::recursive_mutex::scoped_lock sl(mLock); From 297f68f371f6fa35521e3ba7dd7d66b1d7ab35cb Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 30 Apr 2013 15:35:28 -0700 Subject: [PATCH 06/16] Rework the way PF API request validity is checked against the ledger. --- src/cpp/ripple/PFRequest.cpp | 54 +++++++++++++++++++++++++++++------ src/cpp/ripple/PFRequest.h | 3 +- src/cpp/ripple/RPCHandler.cpp | 2 +- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/cpp/ripple/PFRequest.cpp b/src/cpp/ripple/PFRequest.cpp index 54f9371c8d..a613cf1c3a 100644 --- a/src/cpp/ripple/PFRequest.cpp +++ b/src/cpp/ripple/PFRequest.cpp @@ -2,6 +2,8 @@ #include "NetworkOPs.h" #include "RPCErr.h" +#include "Ledger.h" +#include "Application.h" boost::recursive_mutex PFRequest::sLock; std::set PFRequest::sRequests; @@ -15,11 +17,43 @@ PFRequest::PFRequest(const boost::shared_ptr& subscriber) : bool PFRequest::isValid() { boost::recursive_mutex::scoped_lock sl(mLock); - bValid = raSrcAccount.isSet() && raDstAccount.isSet() && saDstAmount.isPositive(); return bValid; } -Json::Value PFRequest::doCreate(const Json::Value& value) +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); + if (!asDst) + { // no destination account + 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); + } + } + } + } + return bValid; +} + +Json::Value PFRequest::doCreate(Ledger::ref lrLedger, const Json::Value& value) { Json::Value status; bool mValid; @@ -28,7 +62,7 @@ Json::Value PFRequest::doCreate(const Json::Value& value) boost::recursive_mutex::scoped_lock sl(mLock); parseJson(value, true); status = jvStatus; - mValid = isValid(); + mValid = isValid(lrLedger); } if (mValid) @@ -75,7 +109,7 @@ int PFRequest::parseJson(const Json::Value& jvParams, bool complete) if (jvParams.isMember("destination_amount")) { if (!saDstAmount.bSetJson(jvParams["destination_amount"]) || - (saDstAmount.getCurrency().isZero() != saDstAmount.getIssuer().isZero()) || + (saDstAmount.getCurrency().isZero() && saDstAmount.getIssuer().isNonZero()) || (saDstAmount.getCurrency() == CURRENCY_BAD)) { jvStatus = rpcError(rpcDST_AMT_MALFORMED); @@ -106,12 +140,16 @@ int PFRequest::parseJson(const Json::Value& jvParams, bool complete) jvStatus = rpcError(rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } - if (jvCur.isMember("issuer")) + if (jvCur.isMember("issuer") && !STAmount::issuerFromString(uIss, jvCur["issuer"].asString())) { - // parse issuer WRITEME + jvStatus = rpcError(rpcSRC_ISR_MALFORMED); } - // sanity check WRITEME - // insert in set WRIEME + if (uCur.isZero() && uIss.isNonZero()) + { + jvStatus = rpcError(rpcSRC_CUR_MALFORMED); + return PFR_PJ_INVALID; + } + sciSourceCurrencies.insert(currIssuer_t(uCur, uIss)); } } diff --git a/src/cpp/ripple/PFRequest.h b/src/cpp/ripple/PFRequest.h index 114fc5e343..30c40ff91b 100644 --- a/src/cpp/ripple/PFRequest.h +++ b/src/cpp/ripple/PFRequest.h @@ -61,10 +61,11 @@ public: PFRequest(const boost::shared_ptr& subscriber); + bool isValid(const boost::shared_ptr&); bool isValid(); Json::Value getStatus(); - Json::Value doCreate(const Json::Value&); + Json::Value doCreate(const boost::shared_ptr&, const Json::Value&); Json::Value doClose(const Json::Value&); Json::Value doStatus(const Json::Value&); diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 64e29bdfb1..b72ea21ddf 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1177,7 +1177,7 @@ Json::Value RPCHandler::doPathFind(Json::Value jvRequest, int& cost, ScopedLock& { mInfoSub->clearPFRequest(); PFRequest::pointer request = boost::make_shared(mInfoSub); - Json::Value result = request->doCreate(jvRequest); + Json::Value result = request->doCreate(mNetOps->getCurrentLedger(), jvRequest); if (request->isValid()) mInfoSub->setPFRequest(request); return result; From 9df93f4202b23054d625573816aead482f4ed1be Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 30 Apr 2013 15:57:11 -0700 Subject: [PATCH 07/16] Break the ripple line cache into its own structure --- src/cpp/ripple/Pathfinder.cpp | 11 ++++++----- src/cpp/ripple/Pathfinder.h | 20 +++++++++++++++++--- src/cpp/ripple/RPCHandler.cpp | 6 ++++-- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 9f3c604a8d..35a55da78c 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(Ledger::ref ledger, 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(ledger), 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..c1a9df966f 100644 --- a/src/cpp/ripple/Pathfinder.h +++ b/src/cpp/ripple/Pathfinder.h @@ -35,6 +35,21 @@ 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) { ; } + AccountItems& getRippleLines(const uint160& accountID); +}; + class Pathfinder { uint160 mSrcAccountID; @@ -47,6 +62,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 +79,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(Ledger::ref ledger, 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/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index b72ea21ddf..69e27236f5 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -180,7 +180,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(lSnapshot, cache, raSrcAddressID, dstAccountID, saSendMax.getCurrency(), saSendMax.getIssuer(), saSend, bValid); if (!bValid || !pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsPaths)) @@ -1304,6 +1305,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]; @@ -1339,7 +1341,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(lSnapShot, cache, raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount, bValid); if (!bValid || !pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsComputed)) { From ca46e97bc5c6e363ca6ac1ea7d8fd27483be2e4c Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 30 Apr 2013 17:19:45 -0700 Subject: [PATCH 08/16] Wrong error code returned. --- src/cpp/ripple/RPCHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 69e27236f5..e07c6c462a 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1188,7 +1188,7 @@ Json::Value RPCHandler::doPathFind(Json::Value jvRequest, int& cost, ScopedLock& { PFRequest::pointer request = mInfoSub->getPFRequest(); if (!request) - return rpcNO_PF_REQUEST; + return rpcError(rpcNO_PF_REQUEST); mInfoSub->clearPFRequest(); return request->doClose(jvRequest); } From d193608aa1929ea7e386b0648c2277022e60e69e Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 2 May 2013 01:04:16 -0700 Subject: [PATCH 09/16] More PFRequest work. Advanced features. --- src/cpp/ripple/PFRequest.cpp | 87 +++++++++++++++++++++++++++++++++-- src/cpp/ripple/PFRequest.h | 5 +- src/cpp/ripple/Pathfinder.cpp | 4 +- src/cpp/ripple/Pathfinder.h | 6 ++- src/cpp/ripple/RPCHandler.cpp | 10 ++-- 5 files changed, 100 insertions(+), 12 deletions(-) diff --git a/src/cpp/ripple/PFRequest.cpp b/src/cpp/ripple/PFRequest.cpp index a613cf1c3a..74ec431c8a 100644 --- a/src/cpp/ripple/PFRequest.cpp +++ b/src/cpp/ripple/PFRequest.cpp @@ -4,6 +4,9 @@ #include "RPCErr.h" #include "Ledger.h" #include "Application.h" +#include "Pathfinder.h" +#include "RippleCalc.h" +#include "LedgerFormats.h" boost::recursive_mutex PFRequest::sLock; std::set PFRequest::sRequests; @@ -35,8 +38,10 @@ bool PFRequest::isValid(Ledger::ref lrLedger) 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; @@ -48,21 +53,41 @@ bool PFRequest::isValid(Ledger::ref lrLedger) 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); - parseJson(value, true); - status = jvStatus; - mValid = isValid(lrLedger); + 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) @@ -167,4 +192,60 @@ Json::Value PFRequest::doStatus(const Json::Value&) 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; + + 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())) + sourceCurrencies.insert(std::make_pair(c, ACCOUNT_XRP)); + } + } + + jvStatus["source_account"] = raSrcAccount.humanAccountID(); + jvStatus["destination_account"] = raDstAccount.humanAccountID(); + + Json::Value jvArray = Json::arrayValue; + + BOOST_FOREACH(const currIssuer_t& currIssuer, sourceCurrencies) + { + bool valid; + STPathSet spsPaths; + Pathfinder pf(cache, raSrcAccount, raDstAccount, + currIssuer.first, currIssuer.second, saDstAmount, 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); + 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); + } + } + } + jvStatus["alternatives"] = jvArray; + return true; +} + // vim:ts=4 diff --git a/src/cpp/ripple/PFRequest.h b/src/cpp/ripple/PFRequest.h index 30c40ff91b..618bb5b9d5 100644 --- a/src/cpp/ripple/PFRequest.h +++ b/src/cpp/ripple/PFRequest.h @@ -14,6 +14,7 @@ #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 @@ -21,6 +22,7 @@ class Ledger; class InfoSub; class STAmount; +class RLCache; // Return values from parseJson <0 = invalid, >0 = valid #define PFR_PJ_INVALID -1 @@ -69,8 +71,7 @@ public: Json::Value doClose(const Json::Value&); Json::Value doStatus(const Json::Value&); - void doUpdate(); // do an update - void trigger(); // schedule an update + bool doUpdate(const boost::shared_ptr&, bool fast); // update jvStatus static void updateAll(const boost::shared_ptr &); }; diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp index 35a55da78c..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, RLCache::ref cache, +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, RLCache::ref cache, mSrcCurrencyID(uSrcCurrencyID), mSrcIssuerID(uSrcIssuerID), mSrcAmount(uSrcCurrencyID, uSrcIssuerID, 1u, 0, true), - mLedger(ledger), mRLCache(cache) + mLedger(cache->getLedger()), mRLCache(cache) { if (((mSrcAccountID == mDstAccountID) && (mSrcCurrencyID == mDstAmount.getCurrency())) || mDstAmount.isZero()) diff --git a/src/cpp/ripple/Pathfinder.h b/src/cpp/ripple/Pathfinder.h index c1a9df966f..3ddbb486b1 100644 --- a/src/cpp/ripple/Pathfinder.h +++ b/src/cpp/ripple/Pathfinder.h @@ -47,7 +47,9 @@ public: typedef const pointer& ref; RLCache(Ledger::ref l) : mLedger(l) { ; } - AccountItems& getRippleLines(const uint160& accountID); + Ledger::ref getLedger() { return mLedger; } + + AccountItems& getRippleLines(const uint160& accountID); }; class Pathfinder @@ -83,7 +85,7 @@ class Pathfinder bool isAuthRequired, bool isDestCurrency, const uint160& dest); public: - Pathfinder(Ledger::ref ledger, RLCache::ref cache, + 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/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 82abd03acb..28f9684611 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -182,7 +182,7 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit) ScopedUnlock su(theApp->getMasterLock()); bool bValid; RLCache::pointer cache = boost::make_shared(lSnapshot); - Pathfinder pf(lSnapshot, cache, raSrcAddressID, dstAccountID, + Pathfinder pf(cache, raSrcAddressID, dstAccountID, saSendMax.getCurrency(), saSendMax.getIssuer(), saSend, bValid); if (!bValid || !pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsPaths)) @@ -1173,13 +1173,17 @@ Json::Value RPCHandler::doPathFind(Json::Value jvRequest, int& cost, ScopedLock& { 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->getCurrentLedger(), jvRequest); + Json::Value result = request->doCreate(mNetOps->getClosedLedger(), jvRequest); if (request->isValid()) mInfoSub->setPFRequest(request); return result; @@ -1342,7 +1346,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost, Scope STPathSet spsComputed; bool bValid; - Pathfinder pf(lSnapShot, cache, 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)) { From f5579c0139b148532394d0aa29aabe69ac229fb4 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 2 May 2013 12:52:05 -0700 Subject: [PATCH 10/16] Handler to run all pathfinding requests. --- src/cpp/ripple/PFRequest.cpp | 43 ++++++++++++++++++++++++++++++++++++ src/cpp/ripple/PFRequest.h | 1 + 2 files changed, 44 insertions(+) diff --git a/src/cpp/ripple/PFRequest.cpp b/src/cpp/ripple/PFRequest.cpp index 74ec431c8a..743152d7c6 100644 --- a/src/cpp/ripple/PFRequest.cpp +++ b/src/cpp/ripple/PFRequest.cpp @@ -248,4 +248,47 @@ bool PFRequest::doUpdate(RLCache::ref cache, bool fast) return true; } +void PFRequest::updateAll(const boost::shared_ptr& ledger) +{ + assert(ledger->isImmutable()); + + 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) + { + InfoSub::pointer ipSub = pRequest->wpSubscriber.lock(); + if (ipSub) + { + Json::Value update; + { + boost::recursive_mutex::scoped_lock sl(pRequest->mLock); + pRequest->doUpdate(cache, true); + 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 index 618bb5b9d5..fbfa6ab182 100644 --- a/src/cpp/ripple/PFRequest.h +++ b/src/cpp/ripple/PFRequest.h @@ -35,6 +35,7 @@ public: typedef boost::weak_ptr wptr; typedef boost::shared_ptr pointer; typedef const pointer& ref; + typedef const wptr& wref; typedef std::pair currIssuer_t; From b6871cba8d0d3e1fc62f5b829f6167e1735955d1 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 2 May 2013 16:13:36 -0700 Subject: [PATCH 11/16] Dispatch new pathfinding requests. --- src/cpp/ripple/JobQueue.cpp | 1 + src/cpp/ripple/JobQueue.h | 21 +++++++++++---------- src/cpp/ripple/LedgerMaster.cpp | 30 ++++++++++++++++++++++++++++++ src/cpp/ripple/LedgerMaster.h | 4 +++- 4 files changed, 45 insertions(+), 11 deletions(-) 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..d9095ca6b0 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(); @@ -633,6 +634,7 @@ void LedgerMaster::tryPublish() void LedgerMaster::pubThread() { std::list ledgers; + bool published = false; while (1) { @@ -644,6 +646,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 +661,30 @@ 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 + { + { + boost::recursive_mutex::scoped_lock ml(mLock); + if (lastLedger.get() == mPubLedger.get()) + { + mPathFindThread = false; + return; + } + lastLedger = mPubLedger; + } + + PFRequest::updateAll(lastLedger); + + } while(1); +} + // vim:ts=4 diff --git a/src/cpp/ripple/LedgerMaster.h b/src/cpp/ripple/LedgerMaster.h index 6c9d87122b..45310a1a21 100644 --- a/src/cpp/ripple/LedgerMaster.h +++ b/src/cpp/ripple/LedgerMaster.h @@ -45,6 +45,7 @@ protected: std::list mPubLedgers; // List of ledgers to publish bool mPubThread; // Publish thread is running + bool mPathFindThread; // Pathfind thread is running void applyFutureTransactions(uint32 ledgerIndex); bool isValidTransaction(Transaction::ref trans); @@ -54,11 +55,12 @@ 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) { ; } uint32 getCurrentLedgerIndex(); From 0e9973d3a89ddfe86ee0d54b0a162344d1411afc Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 2 May 2013 19:20:21 -0700 Subject: [PATCH 12/16] We should accept numbers as long as we produce. --- src/cpp/ripple/Amount.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 9e4710725546b012948b051d1b3d049955f4161d Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 3 May 2013 09:33:58 -0700 Subject: [PATCH 13/16] Rework dispatch of new API PF requests. --- src/cpp/ripple/LedgerMaster.cpp | 35 ++++++++++++++++++++++++++++++--- src/cpp/ripple/LedgerMaster.h | 7 ++++++- src/cpp/ripple/PFRequest.cpp | 19 ++++++++++++------ src/cpp/ripple/PFRequest.h | 4 +++- src/cpp/ripple/RPCHandler.cpp | 3 +++ 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index d9095ca6b0..97f04e1518 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -628,6 +628,13 @@ 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)); + } } } @@ -669,22 +676,44 @@ void LedgerMaster::pubThread() void LedgerMaster::updatePaths() { Ledger::pointer lastLedger; - do { + bool newOnly = false; + { boost::recursive_mutex::scoped_lock ml(mLock); - if (lastLedger.get() == mPubLedger.get()) + 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); + 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 45310a1a21..2d872bbdca 100644 --- a/src/cpp/ripple/LedgerMaster.h +++ b/src/cpp/ripple/LedgerMaster.h @@ -45,7 +45,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); @@ -60,7 +63,8 @@ protected: public: LedgerMaster() : mHeldTransactions(uint256()), mMissingSeq(0), - mMinValidations(0), mLastValidateSeq(0), mPubThread(false), mPathFindThread(false) + mMinValidations(0), mLastValidateSeq(0), mPubThread(false), + mPathFindThread(false), mPathFindNewLedger(false), mPathFindNewRequest(false) { ; } uint32 getCurrentLedgerIndex(); @@ -157,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/PFRequest.cpp b/src/cpp/ripple/PFRequest.cpp index 743152d7c6..59f2c36683 100644 --- a/src/cpp/ripple/PFRequest.cpp +++ b/src/cpp/ripple/PFRequest.cpp @@ -12,7 +12,7 @@ boost::recursive_mutex PFRequest::sLock; std::set PFRequest::sRequests; PFRequest::PFRequest(const boost::shared_ptr& subscriber) : - wpSubscriber(subscriber), jvStatus(Json::objectValue), bValid(false) + wpSubscriber(subscriber), jvStatus(Json::objectValue), bValid(false), bNew(true) { ; } @@ -23,6 +23,12 @@ bool PFRequest::isValid() 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); @@ -198,6 +204,8 @@ bool PFRequest::doUpdate(RLCache::ref cache, bool fast) jvStatus = Json::objectValue; if (!isValid(cache->getLedger())) return false; + if (!fast) + bNew = false; std::set sourceCurrencies(sciSourceCurrencies); if (sourceCurrencies.empty()) @@ -248,11 +256,10 @@ bool PFRequest::doUpdate(RLCache::ref cache, bool fast) return true; } -void PFRequest::updateAll(const boost::shared_ptr& ledger) +void PFRequest::updateAll(Ledger::ref ledger, bool newOnly) { - assert(ledger->isImmutable()); - std::set requests; + { boost::recursive_mutex::scoped_lock sl(sLock); requests = sRequests; @@ -267,7 +274,7 @@ void PFRequest::updateAll(const boost::shared_ptr& ledger) { bool remove = true; PFRequest::pointer pRequest = wRequest.lock(); - if (pRequest) + if (pRequest && (!newOnly || pRequest->isNew())) { InfoSub::pointer ipSub = pRequest->wpSubscriber.lock(); if (ipSub) @@ -275,7 +282,7 @@ void PFRequest::updateAll(const boost::shared_ptr& ledger) Json::Value update; { boost::recursive_mutex::scoped_lock sl(pRequest->mLock); - pRequest->doUpdate(cache, true); + pRequest->doUpdate(cache, false); update = pRequest->jvStatus; } update["type"] = "path_find"; diff --git a/src/cpp/ripple/PFRequest.h b/src/cpp/ripple/PFRequest.h index fbfa6ab182..402bb02cc0 100644 --- a/src/cpp/ripple/PFRequest.h +++ b/src/cpp/ripple/PFRequest.h @@ -52,6 +52,7 @@ protected: std::vector vjvBridges; bool bValid; + bool bNew; // Track all requests static std::set sRequests; @@ -66,6 +67,7 @@ public: bool isValid(const boost::shared_ptr&); bool isValid(); + bool isNew(); Json::Value getStatus(); Json::Value doCreate(const boost::shared_ptr&, const Json::Value&); @@ -74,7 +76,7 @@ public: bool doUpdate(const boost::shared_ptr&, bool fast); // update jvStatus - static void updateAll(const boost::shared_ptr &); + static void updateAll(const boost::shared_ptr& ledger, bool newOnly); }; #endif diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 28f9684611..6988ffa602 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1185,7 +1185,10 @@ Json::Value RPCHandler::doPathFind(Json::Value jvRequest, int& cost, ScopedLock& 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; } From e7529d022144e1c204af617bf022a341be259665 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 3 May 2013 13:59:04 -0700 Subject: [PATCH 14/16] Report the correct error if someone tries to use the evented path_find through the RPC API. (Maybe we'll add limited support for this later.) --- src/cpp/ripple/CallRPC.cpp | 1 + 1 file changed, 1 insertion(+) 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 }, }; From 521d528d6a99555a63994caa85f8b5cdd783d622 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 3 May 2013 14:51:49 -0700 Subject: [PATCH 15/16] Bug fixes. --- src/cpp/ripple/PFRequest.cpp | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/PFRequest.cpp b/src/cpp/ripple/PFRequest.cpp index 59f2c36683..cc1b827ee1 100644 --- a/src/cpp/ripple/PFRequest.cpp +++ b/src/cpp/ripple/PFRequest.cpp @@ -8,6 +8,8 @@ #include "RippleCalc.h" #include "LedgerFormats.h" +SETUP_LOG(); + boost::recursive_mutex PFRequest::sLock; std::set PFRequest::sRequests; @@ -98,6 +100,10 @@ Json::Value PFRequest::doCreate(Ledger::ref lrLedger, const Json::Value& value) 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()); } @@ -125,7 +131,7 @@ int PFRequest::parseJson(const Json::Value& jvParams, bool complete) if (jvParams.isMember("destination_account")) { - if (!raDstAccount.setAccountID(jvParams["source_account"].asString())) + if (!raDstAccount.setAccountID(jvParams["destination_account"].asString())) { jvStatus = rpcError(rpcDST_ACT_MALFORMED); return PFR_PJ_INVALID; @@ -141,7 +147,8 @@ int PFRequest::parseJson(const Json::Value& jvParams, bool complete) { if (!saDstAmount.bSetJson(jvParams["destination_amount"]) || (saDstAmount.getCurrency().isZero() && saDstAmount.getIssuer().isNonZero()) || - (saDstAmount.getCurrency() == CURRENCY_BAD)) + (saDstAmount.getCurrency() == CURRENCY_BAD) || + !saDstAmount.isPositive()) { jvStatus = rpcError(rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; @@ -216,7 +223,12 @@ bool PFRequest::doUpdate(RLCache::ref cache, bool fast) BOOST_FOREACH(const uint160& c, usCurrencies) { if (!sameAccount || (c != saDstAmount.getCurrency())) - sourceCurrencies.insert(std::make_pair(c, ACCOUNT_XRP)); + { + if (c.isZero()) + sourceCurrencies.insert(std::make_pair(c, ACCOUNT_XRP)); + else + sourceCurrencies.insert(std::make_pair(c, raSrcAccount.getAccountID())); + } } } @@ -227,10 +239,15 @@ bool PFRequest::doUpdate(RLCache::ref cache, bool fast) 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); @@ -240,6 +257,8 @@ bool PFRequest::doUpdate(RLCache::ref cache, bool fast) 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); @@ -250,6 +269,14 @@ bool PFRequest::doUpdate(RLCache::ref cache, bool fast) 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; From 077b84846f842eeb4ef5a9f045fc05f05e713eb5 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 3 May 2013 14:57:31 -0700 Subject: [PATCH 16/16] Add PFRequest.cpp to project files. --- newcoin.vcxproj | 1 + newcoin.vcxproj.filters | 3 +++ ripple2010.vcxproj | 1 + ripple2010.vcxproj.filters | 3 +++ 4 files changed, 8 insertions(+) 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