From 00bb9d99a746a85ff7a5544a37e52cbe652e3348 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Thu, 8 Nov 2012 20:26:09 -0800 Subject: [PATCH 1/5] Refactored WSConnection and RPCHandler to have one layer of commands. Evented RPC commands are now on the same level as all other RPC commands and are handled by the RPCHandler class. --- src/cpp/ripple/NetworkOPs.h | 10 ++ src/cpp/ripple/RPCHandler.cpp | 282 +++++++++++++++++++++++++++----- src/cpp/ripple/RPCHandler.h | 13 +- src/cpp/ripple/WSConnection.cpp | 276 +++---------------------------- src/cpp/ripple/WSConnection.h | 11 -- src/js/remote.js | 44 +++-- 6 files changed, 314 insertions(+), 322 deletions(-) diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index eab46a592..41515b003 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -28,6 +28,16 @@ public: virtual ~InfoSub() { ; } virtual void send(const Json::Value& jvObj) = 0; + +protected: + boost::unordered_set mSubAccountInfo; + boost::unordered_set mSubAccountTransaction; + +public: + void insertSubAccountInfo(RippleAddress addr) + { + mSubAccountInfo.insert(addr); + } }; class NetworkOPs diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index fbd24d555..9d9d1544e 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -51,6 +51,7 @@ Json::Value RPCHandler::rpcError(int iError) { rpcNO_ACCOUNT, "noAccount", "No such account." }, { rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable." }, { rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable." }, + { 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_PERMISSION, "noPermission", "You don't have permission for this command." }, @@ -1298,7 +1299,7 @@ Json::Value RPCHandler::doLogRotate(const Json::Value& params) return Log::rotateLog(); } -Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params, int role) +Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params, int role, InfoSub* sub) { cLog(lsTRACE) << "RPC:" << command; cLog(lsTRACE) << "RPC params:" << params; @@ -1309,53 +1310,58 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param int iMinParams; int iMaxParams; bool mAdminRequired; + bool mEvented; unsigned int iOptions; } commandsA[] = { + // Request-response methods { "accept_ledger", &RPCHandler::doAcceptLedger, 0, 0, true }, - { "account_info", &RPCHandler::doAccountInfo, 1, 2, false, optCurrent }, - { "account_tx", &RPCHandler::doAccountTransactions, 2, 3, false, optNetwork }, - { "connect", &RPCHandler::doConnect, 1, 2, true }, - { "data_delete", &RPCHandler::doDataDelete, 1, 1, true }, - { "data_fetch", &RPCHandler::doDataFetch, 1, 1, true }, - { "data_store", &RPCHandler::doDataStore, 2, 2, true }, - { "get_counts", &RPCHandler::doGetCounts, 0, 1, true }, - { "ledger", &RPCHandler::doLedger, 0, 2, false, optNetwork }, - { "ledger_accept", &RPCHandler::doLedgerAccept, 0, 0, true, optCurrent }, - { "ledger_closed", &RPCHandler::doLedgerClosed, 0, 0, false, optClosed }, - { "ledger_current", &RPCHandler::doLedgerCurrent, 0, 0, false, optCurrent }, - { "ledger_entry", &RPCHandler::doLedgerEntry, -1, -1, false, optCurrent }, - { "log_level", &RPCHandler::doLogLevel, 0, 2, true }, - { "logrotate", &RPCHandler::doLogRotate, 0, 0, true }, - { "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, optCurrent }, - { "owner_info", &RPCHandler::doOwnerInfo, 1, 2, false, optCurrent }, + { "account_info", &RPCHandler::doAccountInfo, 1, 2, false, false, optCurrent }, + { "account_tx", &RPCHandler::doAccountTransactions, 2, 3, false, false, optNetwork }, + { "connect", &RPCHandler::doConnect, 1, 2, true }, + { "data_delete", &RPCHandler::doDataDelete, 1, 1, true }, + { "data_fetch", &RPCHandler::doDataFetch, 1, 1, true }, + { "data_store", &RPCHandler::doDataStore, 2, 2, true }, + { "get_counts", &RPCHandler::doGetCounts, 0, 1, true }, + { "ledger", &RPCHandler::doLedger, 0, 2, false, false, optNetwork }, + { "ledger_accept", &RPCHandler::doLedgerAccept, 0, 0, true, false, optCurrent }, + { "ledger_closed", &RPCHandler::doLedgerClosed, 0, 0, false, false, optClosed }, + { "ledger_current", &RPCHandler::doLedgerCurrent, 0, 0, false, false, optCurrent }, + { "ledger_entry", &RPCHandler::doLedgerEntry, -1, -1, false, false, optCurrent }, + { "log_level", &RPCHandler::doLogLevel, 0, 2, true }, + { "logrotate", &RPCHandler::doLogRotate, 0, 0, true }, + { "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, false, optCurrent }, + { "owner_info", &RPCHandler::doOwnerInfo, 1, 2, false, false, optCurrent }, { "peers", &RPCHandler::doPeers, 0, 0, true }, - { "profile", &RPCHandler::doProfile, 1, 9, false, optCurrent }, - { "ripple_lines_get", &RPCHandler::doRippleLinesGet, 1, 2, false, optCurrent }, - { "submit", &RPCHandler::doSubmit, 2, 2, false, optCurrent }, - { "submit_json", &RPCHandler::doSubmitJson, -1, -1, false, optCurrent }, - { "server_info", &RPCHandler::doServerInfo, 0, 0, true }, - { "stop", &RPCHandler::doStop, 0, 0, true }, - { "transaction_entry", &RPCHandler::doTransactionEntry, -1, -1, false, optCurrent }, - { "tx", &RPCHandler::doTx, 1, 1, true }, - { "tx_history", &RPCHandler::doTxHistory, 1, 1, false, }, + { "profile", &RPCHandler::doProfile, 1, 9, false, false, optCurrent }, + { "ripple_lines_get", &RPCHandler::doRippleLinesGet, 1, 2, false, false, optCurrent }, + { "submit", &RPCHandler::doSubmit, 2, 2, false, false, optCurrent }, + { "submit_json", &RPCHandler::doSubmitJson, -1, -1, false, false, optCurrent }, + { "server_info", &RPCHandler::doServerInfo, 0, 0, true }, + { "stop", &RPCHandler::doStop, 0, 0, true }, + { "transaction_entry", &RPCHandler::doTransactionEntry, -1, -1, false, false, optCurrent }, + { "tx", &RPCHandler::doTx, 1, 1, true }, + { "tx_history", &RPCHandler::doTxHistory, 1, 1, false, }, - { "unl_add", &RPCHandler::doUnlAdd, 1, 2, true }, - { "unl_delete", &RPCHandler::doUnlDelete, 1, 1, true }, - { "unl_list", &RPCHandler::doUnlList, 0, 0, true }, - { "unl_load", &RPCHandler::doUnlLoad, 0, 0, true }, - { "unl_network", &RPCHandler::doUnlNetwork, 0, 0, true }, - { "unl_reset", &RPCHandler::doUnlReset, 0, 0, true }, - { "unl_score", &RPCHandler::doUnlScore, 0, 0, true }, + { "unl_add", &RPCHandler::doUnlAdd, 1, 2, true }, + { "unl_delete", &RPCHandler::doUnlDelete, 1, 1, true }, + { "unl_list", &RPCHandler::doUnlList, 0, 0, true }, + { "unl_load", &RPCHandler::doUnlLoad, 0, 0, true }, + { "unl_network", &RPCHandler::doUnlNetwork, 0, 0, true }, + { "unl_reset", &RPCHandler::doUnlReset, 0, 0, true }, + { "unl_score", &RPCHandler::doUnlScore, 0, 0, true }, - { "validation_create", &RPCHandler::doValidationCreate, 0, 1, false }, - { "validation_seed", &RPCHandler::doValidationSeed, 0, 1, false }, + { "validation_create", &RPCHandler::doValidationCreate, 0, 1, false }, + { "validation_seed", &RPCHandler::doValidationSeed, 0, 1, false }, - { "wallet_accounts", &RPCHandler::doWalletAccounts, 1, 1, false, optCurrent }, - { "wallet_propose", &RPCHandler::doWalletPropose, 0, 1, false, }, - { "wallet_seed", &RPCHandler::doWalletSeed, 0, 1, false, }, + { "wallet_accounts", &RPCHandler::doWalletAccounts, 1, 1, false, false, optCurrent }, + { "wallet_propose", &RPCHandler::doWalletPropose, 0, 1, false, }, + { "wallet_seed", &RPCHandler::doWalletSeed, 0, 1, false, }, - { "login", &RPCHandler::doLogin, 2, 2, true }, - }; + { "login", &RPCHandler::doLogin, 2, 2, true }, + + // Evented methods + { "subscribe", &RPCHandler::doSubscribe, -1, -1, false, true }, + { "unsubscribe", &RPCHandler::doUnsubscribe, -1, -1, false, true }, }; int i = NUMBER(commandsA); @@ -1370,6 +1376,10 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { return rpcError(rpcNO_PERMISSION); } + else if (commandsA[i].mEvented && sub == NULL) + { + return rpcError(rpcNO_EVENTS); + } else if (commandsA[i].iMinParams >= 0 ? commandsA[i].iMaxParams ? (params.size() < commandsA[i].iMinParams @@ -1396,6 +1406,11 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param } else { + if (sub != NULL) + { + isCurrent = sub; + } + try { return (this->*(commandsA[i].dfpFunc))(params); } @@ -1933,4 +1948,191 @@ Json::Value RPCHandler::doLedgerEntry(const Json::Value& jvRequest) return jvResult; } + +boost::unordered_set RPCHandler::parseAccountIds(const Json::Value& jvArray) +{ + boost::unordered_set usnaResult; + + for (Json::Value::const_iterator it = jvArray.begin(); it != jvArray.end(); it++) + { + RippleAddress naString; + + if (!(*it).isString() || !naString.setAccountID((*it).asString())) + { + usnaResult.clear(); + break; + } + else + { + (void) usnaResult.insert(naString); + } + } + + return usnaResult; +} + +/* +server : Sends a message anytime the server status changes such as network connectivity. +ledger : Sends a message at every ledger close. +transactions : Sends a message for every transaction that makes it into a ledger. +rt_transactions +accounts +rt_accounts +*/ +Json::Value RPCHandler::doSubscribe(const Json::Value& jvRequest) +{ + Json::Value jvResult(Json::objectValue); + + if (jvRequest.isMember("streams")) + { + for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) + { + if ((*it).isString()) + { + std::string streamName=(*it).asString(); + + if(streamName=="server") + { + mNetOps->subServer(isCurrent, jvResult); + }else if(streamName=="ledger") + { + mNetOps->subLedger(isCurrent, jvResult); + }else if(streamName=="transactions") + { + mNetOps->subTransactions(isCurrent); + }else if(streamName=="rt_transactions") + { + mNetOps->subRTTransactions(isCurrent); + }else + { + jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); + } + }else + { + jvResult["error"] = "malformedSteam"; + } + } + } + + if (jvRequest.isMember("rt_accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + isCurrent->insertSubAccountInfo(naAccountID); + } + + mNetOps->subAccount(isCurrent, usnaAccoundIds, true); + } + } + + if (jvRequest.isMember("accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + isCurrent->insertSubAccountInfo(naAccountID); + } + + mNetOps->subAccount(isCurrent, usnaAccoundIds, false); + } + } + + return jvResult; +} + +Json::Value RPCHandler::doUnsubscribe(const Json::Value& jvRequest) +{ + Json::Value jvResult(Json::objectValue); + + if (jvRequest.isMember("streams")) + { + for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) + { + if ((*it).isString() ) + { + std::string streamName=(*it).asString(); + + if(streamName=="server") + { + mNetOps->unsubServer(isCurrent); + }else if(streamName=="ledger") + { + mNetOps->unsubLedger(isCurrent); + }else if(streamName=="transactions") + { + mNetOps->unsubTransactions(isCurrent); + }else if(streamName=="rt_transactions") + { + mNetOps->unsubRTTransactions(isCurrent); + }else + { + jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); + } + }else + { + jvResult["error"] = "malformedSteam"; + } + } + } + + if (jvRequest.isMember("rt_accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + isCurrent->insertSubAccountInfo(naAccountID); + } + + mNetOps->unsubAccount(isCurrent, usnaAccoundIds,true); + } + } + + if (jvRequest.isMember("accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + isCurrent->insertSubAccountInfo(naAccountID); + } + + mNetOps->unsubAccount(isCurrent, usnaAccoundIds,false); + } + } + + return jvResult; +} + // vim:ts=4 diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index ca71d7bac..90cc0132a 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -6,7 +6,9 @@ class NetworkOPs; class RPCHandler { - NetworkOPs* mNetOps; + NetworkOPs* mNetOps; + InfoSub* isCurrent; + boost::mutex mLock; typedef Json::Value (RPCHandler::*doFuncPtr)(const Json::Value ¶ms); enum { @@ -15,6 +17,9 @@ class RPCHandler optClosed = 4+optNetwork, // Need closed ledger }; + // Utilities + void addSubmitPath(Json::Value& txJSON); + boost::unordered_set parseAccountIds(const Json::Value& jvArray); int getParamCount(const Json::Value& params); bool extractString(std::string& param, const Json::Value& params, int index); @@ -84,8 +89,9 @@ class RPCHandler Json::Value doLedgerEntry(const Json::Value& params); Json::Value doTransactionEntry(const Json::Value& params); + Json::Value doSubscribe(const Json::Value& params); + Json::Value doUnsubscribe(const Json::Value& params); - void addSubmitPath(Json::Value& txJSON); public: @@ -95,6 +101,7 @@ public: // Misc failure rpcLOAD_FAILED, rpcNO_PERMISSION, + rpcNO_EVENTS, rpcNOT_STANDALONE, // Networking @@ -152,7 +159,7 @@ public: RPCHandler(NetworkOPs* netOps); - Json::Value doCommand(const std::string& command, Json::Value& params,int role); + Json::Value doCommand(const std::string& command, Json::Value& params, int role, InfoSub* sub = NULL); Json::Value rpcError(int iError); Json::Value handleJSONSubmit(const Json::Value& jvRequest); diff --git a/src/cpp/ripple/WSConnection.cpp b/src/cpp/ripple/WSConnection.cpp index 7037836b1..f06c798a5 100644 --- a/src/cpp/ripple/WSConnection.cpp +++ b/src/cpp/ripple/WSConnection.cpp @@ -18,8 +18,8 @@ WSConnection::~WSConnection() mNetwork.unsubRTTransactions(this); mNetwork.unsubLedger(this); mNetwork.unsubServer(this); - mNetwork.unsubAccount(this, mSubAccountInfo,true); - mNetwork.unsubAccount(this, mSubAccountInfo,false); + mNetwork.unsubAccount(this, mSubAccountInfo, true); + mNetwork.unsubAccount(this, mSubAccountInfo, false); } void WSConnection::send(const Json::Value& jvObj) @@ -33,17 +33,6 @@ void WSConnection::send(const Json::Value& jvObj) Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) { - static struct { - const char* pCommand; - doFuncPtr dfpFunc; - } commandsA[] = { - // Request-Response Commands: - { "submit", &WSConnection::doSubmit }, - { "subscribe", &WSConnection::doSubscribe }, - { "unsubscribe", &WSConnection::doUnsubscribe }, - { "rpc", &WSConnection::doRPC }, - }; - if (!jvRequest.isMember("command")) { Json::Value jvResult(Json::objectValue); @@ -56,22 +45,27 @@ Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) return jvResult; } - std::string strCommand = jvRequest["command"].asString(); - - int i = NUMBER(commandsA); - - while (i-- && strCommand != commandsA[i].pCommand) - ; - Json::Value jvResult(Json::objectValue); - if (i < 0) + // Regular RPC command + jvResult["result"] = theApp->getRPCHandler().doCommand( + jvRequest["command"].asString(), + jvRequest.isMember("params") + ? jvRequest["params"] + : jvRequest, + mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN, + this); + + // Currently we will simply unwrap errors returned by the RPC + // API, in the future maybe we can make the responses + // consistent. + if (jvResult["result"].isObject() && jvResult["result"].isMember("error")) { - jvResult["error"] = "unknownCommand"; // Unknown command. - } - else - { - (this->*(commandsA[i].dfpFunc))(jvResult, jvRequest); + jvResult = jvResult["result"]; + jvResult["status"] = "error"; + jvResult["request"] = jvRequest; + } else { + jvResult["status"] = "success"; } if (jvRequest.isMember("id")) @@ -79,237 +73,9 @@ Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) jvResult["id"] = jvRequest["id"]; } - if (jvResult.isMember("error")) - { - jvResult["result"] = "error"; - jvResult["request"] = jvRequest; - } - else - { - jvResult["result"] = "success"; - } - - jvResult["type"] = "response"; + jvResult["type"] = "response"; return jvResult; } -boost::unordered_set WSConnection::parseAccountIds(const Json::Value& jvArray) -{ - boost::unordered_set usnaResult; - - for (Json::Value::const_iterator it = jvArray.begin(); it != jvArray.end(); it++) - { - RippleAddress naString; - - if (!(*it).isString() || !naString.setAccountID((*it).asString())) - { - usnaResult.clear(); - break; - } - else - { - (void) usnaResult.insert(naString); - } - } - - return usnaResult; -} - -// -// Commands -// - -/* -server : Sends a message anytime the server status changes such as network connectivity. -ledger : Sends a message at every ledger close. -transactions : Sends a message for every transaction that makes it into a ledger. -rt_transactions -accounts -rt_accounts -*/ -void WSConnection::doSubscribe(Json::Value& jvResult, Json::Value& jvRequest) -{ - if (jvRequest.isMember("streams")) - { - for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) - { - if ((*it).isString()) - { - std::string streamName=(*it).asString(); - - if(streamName=="server") - { - mNetwork.subServer(this, jvResult); - }else if(streamName=="ledger") - { - mNetwork.subLedger(this, jvResult); - }else if(streamName=="transactions") - { - mNetwork.subTransactions(this); - }else if(streamName=="rt_transactions") - { - mNetwork.subRTTransactions(this); - }else - { - jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); - } - }else - { - jvResult["error"] = "malformedSteam"; - } - } - } - - if (jvRequest.isMember("rt_accounts")) - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - }else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountInfo.insert(naAccountID); - } - - mNetwork.subAccount(this, usnaAccoundIds,true); - } - } - - if (jvRequest.isMember("accounts")) - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - }else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountInfo.insert(naAccountID); - } - - mNetwork.subAccount(this, usnaAccoundIds,false); - } - } -} - -void WSConnection::doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest) -{ - if (jvRequest.isMember("streams")) - { - for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) - { - if ((*it).isString() ) - { - std::string streamName=(*it).asString(); - - if(streamName=="server") - { - mNetwork.unsubServer(this); - }else if(streamName=="ledger") - { - mNetwork.unsubLedger(this); - }else if(streamName=="transactions") - { - mNetwork.unsubTransactions(this); - }else if(streamName=="rt_transactions") - { - mNetwork.unsubRTTransactions(this); - }else - { - jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); - } - }else - { - jvResult["error"] = "malformedSteam"; - } - } - } - - if (jvRequest.isMember("rt_accounts")) - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - }else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountInfo.insert(naAccountID); - } - - mNetwork.unsubAccount(this, usnaAccoundIds,true); - } - } - - if (jvRequest.isMember("accounts")) - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - }else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountInfo.insert(naAccountID); - } - - mNetwork.unsubAccount(this, usnaAccoundIds,false); - } - } -} - -void WSConnection::doRPC(Json::Value& jvResult, Json::Value& jvRequest) -{ - if (jvRequest.isMember("rpc_command") ) - { - jvResult = theApp->getRPCHandler().doCommand( - jvRequest["rpc_command"].asString(), - jvRequest.isMember("params") - ? jvRequest["params"] - : jvRequest, - mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN); - - jvResult["type"] = "response"; - } - else - { - jvResult["error"] = "fieldNotCommand"; - } -} - -// XXX Currently requires secret. Allow signed transaction as an alternative. -void WSConnection::doSubmit(Json::Value& jvResult, Json::Value& jvRequest) -{ - if (!jvRequest.isMember("tx_json")) - { - jvResult["error"] = "fieldNotFoundTxJson"; - }else if (!jvRequest.isMember("secret")) - { - jvResult["error"] = "fieldNotFoundSecret"; - }else - { - jvResult=theApp->getRPCHandler().handleJSONSubmit(jvRequest); - - // TODO: track the transaction mNetwork.subSubmit(this, jvResult["tx hash"] ); - } -} - // vim:ts=4 diff --git a/src/cpp/ripple/WSConnection.h b/src/cpp/ripple/WSConnection.h index 47cdf898b..2bc4914d4 100644 --- a/src/cpp/ripple/WSConnection.h +++ b/src/cpp/ripple/WSConnection.h @@ -21,10 +21,6 @@ public: protected: typedef void (WSConnection::*doFuncPtr)(Json::Value& jvResult, Json::Value &jvRequest); - boost::mutex mLock; - boost::unordered_set mSubAccountInfo; - boost::unordered_set mSubAccountTransaction; - WSServerHandler* mHandler; connection_ptr mConnection; NetworkOPs& mNetwork; @@ -44,13 +40,6 @@ public: // Utilities Json::Value invokeCommand(Json::Value& jvRequest); - boost::unordered_set parseAccountIds(const Json::Value& jvArray); - - // Commands - void doSubmit(Json::Value& jvResult, Json::Value& jvRequest); - void doRPC(Json::Value& jvResult, Json::Value& jvRequest); - void doSubscribe(Json::Value& jvResult, Json::Value& jvRequest); - void doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest); }; diff --git a/src/js/remote.js b/src/js/remote.js index a9433bdda..c87e9d924 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -429,10 +429,10 @@ Remote.prototype._connect_message = function (ws, json) { if (!request) { unexpected = true; } - else if ('success' === message.result) { + else if ('success' === message.status) { if (this.trace) console.log("message: %s", json); - request.emit('success', message); + request.emit('success', message.result); } else if (message.error) { if (this.trace) console.log("message: %s", json); @@ -515,7 +515,7 @@ Remote.prototype.request_ledger_hash = function () { var request = new Request(this, 'rpc'); - request.message.rpc_command = 'ledger_closed'; + request.message.command = 'ledger_closed'; return request; }; @@ -525,7 +525,7 @@ Remote.prototype.request_ledger_hash = function () { Remote.prototype.request_ledger_current = function () { var request = new Request(this, 'rpc'); - request.message.rpc_command = 'ledger_current'; + request.message.command = 'ledger_current'; return request; }; @@ -540,7 +540,7 @@ Remote.prototype.request_ledger_entry = function (type) { var self = this; var request = new Request(this, 'rpc'); - request.message.rpc_command = 'ledger_entry'; + request.message.command = 'ledger_entry'; if (type) this.type = type; @@ -593,10 +593,28 @@ Remote.prototype.request_ledger_entry = function (type) { return request; }; -Remote.prototype.request_subscribe = function () { +Remote.prototype.request_subscribe = function (streams) { var request = new Request(this, 'subscribe'); - request.message.streams = [ 'ledger', 'server' ]; + if (streams) { + if ("object" !== typeof streams) { + streams = [streams]; + } + request.message.streams = streams; + } + + return request; +}; + +Remote.prototype.request_unsubscribe = function (streams) { + var request = new Request(this, 'unsubscribe'); + + if (streams) { + if ("object" !== typeof streams) { + streams = [streams]; + } + request.message.streams = streams; + } return request; }; @@ -606,7 +624,7 @@ Remote.prototype.request_transaction_entry = function (hash) { var request = new Request(this, 'rpc'); - request.message.rpc_command = 'transaction_entry'; + request.message.command = 'transaction_entry'; return request .tx_hash(hash); @@ -658,7 +676,7 @@ Remote.prototype.submit = function (transaction) { else { var submit_request = new Request(this, 'rpc'); - submit_request.message.rpc_command = 'submit_json'; + submit_request.message.command = 'submit_json'; submit_request.tx_json(transaction.tx_json); submit_request.secret(transaction.secret); @@ -685,7 +703,7 @@ Remote.prototype.submit = function (transaction) { Remote.prototype._server_subscribe = function () { var self = this; - this.request_subscribe() + this.request_subscribe([ 'ledger', 'server' ]) .on('success', function (message) { self.stand_alone = !!message.stand_alone; @@ -715,7 +733,7 @@ Remote.prototype.ledger_accept = function () { { var request = new Request(this, 'rpc'); - request.message.rpc_command = 'ledger_accept'; + request.message.command = 'ledger_accept'; request .request(); @@ -766,7 +784,7 @@ Remote.prototype.set_account_seq = function (account, seq) { var account = UInt160.json_rewrite(account); if (!this.accounts[account]) this.accounts[account] = {}; - + this.accounts[account].seq = seq; } @@ -806,7 +824,7 @@ Remote.prototype.account_seq_cache = function (account, current) { account_info.caching_seq_request = request; } - return request + return request; }; // Mark an account's root node as dirty. From 0b9971d0d08bfee14931ec4782ce93f3b105cee1 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Fri, 9 Nov 2012 16:54:14 -0800 Subject: [PATCH 2/5] More consistent trace logs. Remove redundant trace. The trace on transaction submit is always immediately followed by a trace showing the request, which contains the same information anyway. --- src/js/remote.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/js/remote.js b/src/js/remote.js index c87e9d924..7aebe09dc 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -430,12 +430,12 @@ Remote.prototype._connect_message = function (ws, json) { unexpected = true; } else if ('success' === message.status) { - if (this.trace) console.log("message: %s", json); + if (this.trace) console.log("remote: response: %s", json); request.emit('success', message.result); } else if (message.error) { - if (this.trace) console.log("message: %s", json); + if (this.trace) console.log("remote: error: %s", json); request.emit('error', { 'error' : 'remoteError', @@ -634,8 +634,6 @@ Remote.prototype.request_transaction_entry = function (hash) { Remote.prototype.submit = function (transaction) { var self = this; - if (this.trace) console.log("remote: submit: %s", JSON.stringify(transaction.tx_json)); - if (transaction.secret && !this.trusted) { transaction.emit('error', { From a92202c7e4f7144faac3afa46882d8a2df4a9f5a Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Fri, 9 Nov 2012 21:10:14 -0800 Subject: [PATCH 3/5] Added RPC methods for ripple-client. --- src/js/remote.js | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/src/js/remote.js b/src/js/remote.js index 7aebe09dc..d67f1d807 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -147,6 +147,21 @@ Request.prototype.ripple_state = function (account, issuer, currency) { return this; }; +Request.prototype.accounts = function (accounts) { + if ("object" !== typeof accounts) { + accounts = [accounts]; + } + + // Process accounts parameters + var procAccounts = []; + for (var i = 0, l = accounts.length; i < l; i++) { + procAccounts.push(UInt160.json_rewrite(accounts[i])); + } + this.message.accounts = procAccounts; + + return this; +}; + // // Remote - access to a remote Ripple server via websocket. // @@ -509,6 +524,26 @@ Remote.prototype.request = function (request) { } }; +Remote.prototype.request_server_info = function () { + var request = new Request(this, 'rpc'); + + request.message.command = 'server_info'; + + return request; +}; + +Remote.prototype.request_ledger = function (params) { + // XXX Does this require the server to be trusted? + //assert(this.trusted); + + var request = new Request(this, 'rpc'); + + request.message.command = 'ledger'; + request.message.params = params; + + return request; +}; + // Only for unit testing. Remote.prototype.request_ledger_hash = function () { assert(this.trusted); // If not trusted, need to check proof. @@ -630,6 +665,45 @@ Remote.prototype.request_transaction_entry = function (hash) { .tx_hash(hash); }; +Remote.prototype.request_ripple_lines_get = function (accountID) { + // XXX Does this require the server to be trusted? + //assert(this.trusted); + + var request = new Request(this, 'rpc'); + + request.message.command = 'ripple_lines_get'; + // XXX Convert API call to JSON + request.message.params = [accountID]; + + return request; +}; + +Remote.prototype.request_wallet_accounts = function (key) { + // XXX Does this require the server to be trusted? + //assert(this.trusted); + + var request = new Request(this, 'rpc'); + + request.message.command = 'wallet_accounts'; + // XXX Convert API call to JSON + request.message.params = [key]; + + return request; +}; + +Remote.prototype.request_account_tx = function (accountID, minLedger, maxLedger) { + // XXX Does this require the server to be trusted? + //assert(this.trusted); + + var request = new Request(this, 'rpc'); + + request.message.command = 'account_tx'; + // XXX Convert API call to JSON + request.message.params = [accountID, minLedger, maxLedger]; + + return request; +}; + // Submit a transaction. Remote.prototype.submit = function (transaction) { var self = this; @@ -877,6 +951,49 @@ Remote.prototype.request_ripple_balance = function (account, issuer, currency, c }); } +Remote.prototype.request_unl_list = function () { + var request = new Request(this, 'rpc'); + + request.message.command = 'unl_list'; + + return request; +}; + +Remote.prototype.request_unl_add = function (addr, note) { + var request = new Request(this, 'rpc'); + + request.message.command = 'unl_add'; + request.message.params = [addr, note]; + + return request; +}; + +Remote.prototype.request_unl_delete = function (publicKey) { + var request = new Request(this, 'rpc'); + + request.message.command = 'unl_delete'; + request.message.params = [publicKey]; + + return request; +}; + +Remote.prototype.request_peers = function () { + var request = new Request(this, 'rpc'); + + request.message.command = 'peers'; + + return request; +}; + +Remote.prototype.request_connect = function (ip, port) { + var request = new Request(this, 'rpc'); + + request.message.command = 'connect'; + request.message.params = [ip, port]; + + return request; +}; + Remote.prototype.transaction = function () { return new Transaction(this); }; From b6bbef84cfa90453411cc40aff6b9d3415b4175d Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Sat, 10 Nov 2012 15:16:30 -0800 Subject: [PATCH 4/5] Move lock from RPCHandler to InfoSub. --- src/cpp/ripple/NetworkOPs.h | 11 +++++++---- src/cpp/ripple/RPCHandler.cpp | 8 -------- src/cpp/ripple/RPCHandler.h | 1 - 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 41515b003..0997f42af 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -33,11 +33,14 @@ protected: boost::unordered_set mSubAccountInfo; boost::unordered_set mSubAccountTransaction; + boost::mutex mLock; + public: - void insertSubAccountInfo(RippleAddress addr) - { - mSubAccountInfo.insert(addr); - } + void insertSubAccountInfo(RippleAddress addr) + { + boost::mutex::scoped_lock sl(mLock); + mSubAccountInfo.insert(addr); + } }; class NetworkOPs diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 9d9d1544e..dbe8e824e 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2023,8 +2023,6 @@ Json::Value RPCHandler::doSubscribe(const Json::Value& jvRequest) jvResult["error"] = "malformedAccount"; }else { - boost::mutex::scoped_lock sl(mLock); - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { isCurrent->insertSubAccountInfo(naAccountID); @@ -2043,8 +2041,6 @@ Json::Value RPCHandler::doSubscribe(const Json::Value& jvRequest) jvResult["error"] = "malformedAccount"; }else { - boost::mutex::scoped_lock sl(mLock); - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { isCurrent->insertSubAccountInfo(naAccountID); @@ -2101,8 +2097,6 @@ Json::Value RPCHandler::doUnsubscribe(const Json::Value& jvRequest) jvResult["error"] = "malformedAccount"; }else { - boost::mutex::scoped_lock sl(mLock); - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { isCurrent->insertSubAccountInfo(naAccountID); @@ -2121,8 +2115,6 @@ Json::Value RPCHandler::doUnsubscribe(const Json::Value& jvRequest) jvResult["error"] = "malformedAccount"; }else { - boost::mutex::scoped_lock sl(mLock); - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { isCurrent->insertSubAccountInfo(naAccountID); diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 90cc0132a..7e804d197 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -8,7 +8,6 @@ class RPCHandler { NetworkOPs* mNetOps; InfoSub* isCurrent; - boost::mutex mLock; typedef Json::Value (RPCHandler::*doFuncPtr)(const Json::Value ¶ms); enum { From f5a6fdbab9704ba08a968f75e23145a13ac96046 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Sat, 10 Nov 2012 15:51:45 -0800 Subject: [PATCH 5/5] Instantiate an RPCHandler per request. RPCHandlers are pretty light objects and this allows us to pass in parameters like the InfoSub object without locking or adding more parameters to the RPC methods. --- src/cpp/ripple/RPCHandler.cpp | 48 +++++++++++++++++---------------- src/cpp/ripple/RPCHandler.h | 5 ++-- src/cpp/ripple/RPCServer.cpp | 4 ++- src/cpp/ripple/RPCServer.h | 2 -- src/cpp/ripple/WSConnection.cpp | 6 ++--- 5 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index dbe8e824e..5d8ca3ef4 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -93,6 +93,13 @@ Json::Value RPCHandler::rpcError(int iError) RPCHandler::RPCHandler(NetworkOPs* netOps) { mNetOps=netOps; + mInfoSub=NULL; +} + +RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub* infoSub) +{ + mNetOps=netOps; + mInfoSub=infoSub; } int RPCHandler::getParamCount(const Json::Value& params) @@ -1299,7 +1306,7 @@ Json::Value RPCHandler::doLogRotate(const Json::Value& params) return Log::rotateLog(); } -Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params, int role, InfoSub* sub) +Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params, int role) { cLog(lsTRACE) << "RPC:" << command; cLog(lsTRACE) << "RPC params:" << params; @@ -1376,7 +1383,7 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { return rpcError(rpcNO_PERMISSION); } - else if (commandsA[i].mEvented && sub == NULL) + else if (commandsA[i].mEvented && mInfoSub == NULL) { return rpcError(rpcNO_EVENTS); } @@ -1406,11 +1413,6 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param } else { - if (sub != NULL) - { - isCurrent = sub; - } - try { return (this->*(commandsA[i].dfpFunc))(params); } @@ -1993,16 +1995,16 @@ Json::Value RPCHandler::doSubscribe(const Json::Value& jvRequest) if(streamName=="server") { - mNetOps->subServer(isCurrent, jvResult); + mNetOps->subServer(mInfoSub, jvResult); }else if(streamName=="ledger") { - mNetOps->subLedger(isCurrent, jvResult); + mNetOps->subLedger(mInfoSub, jvResult); }else if(streamName=="transactions") { - mNetOps->subTransactions(isCurrent); + mNetOps->subTransactions(mInfoSub); }else if(streamName=="rt_transactions") { - mNetOps->subRTTransactions(isCurrent); + mNetOps->subRTTransactions(mInfoSub); }else { jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); @@ -2025,10 +2027,10 @@ Json::Value RPCHandler::doSubscribe(const Json::Value& jvRequest) { BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { - isCurrent->insertSubAccountInfo(naAccountID); + mInfoSub->insertSubAccountInfo(naAccountID); } - mNetOps->subAccount(isCurrent, usnaAccoundIds, true); + mNetOps->subAccount(mInfoSub, usnaAccoundIds, true); } } @@ -2043,10 +2045,10 @@ Json::Value RPCHandler::doSubscribe(const Json::Value& jvRequest) { BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { - isCurrent->insertSubAccountInfo(naAccountID); + mInfoSub->insertSubAccountInfo(naAccountID); } - mNetOps->subAccount(isCurrent, usnaAccoundIds, false); + mNetOps->subAccount(mInfoSub, usnaAccoundIds, false); } } @@ -2067,16 +2069,16 @@ Json::Value RPCHandler::doUnsubscribe(const Json::Value& jvRequest) if(streamName=="server") { - mNetOps->unsubServer(isCurrent); + mNetOps->unsubServer(mInfoSub); }else if(streamName=="ledger") { - mNetOps->unsubLedger(isCurrent); + mNetOps->unsubLedger(mInfoSub); }else if(streamName=="transactions") { - mNetOps->unsubTransactions(isCurrent); + mNetOps->unsubTransactions(mInfoSub); }else if(streamName=="rt_transactions") { - mNetOps->unsubRTTransactions(isCurrent); + mNetOps->unsubRTTransactions(mInfoSub); }else { jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); @@ -2099,10 +2101,10 @@ Json::Value RPCHandler::doUnsubscribe(const Json::Value& jvRequest) { BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { - isCurrent->insertSubAccountInfo(naAccountID); + mInfoSub->insertSubAccountInfo(naAccountID); } - mNetOps->unsubAccount(isCurrent, usnaAccoundIds,true); + mNetOps->unsubAccount(mInfoSub, usnaAccoundIds,true); } } @@ -2117,10 +2119,10 @@ Json::Value RPCHandler::doUnsubscribe(const Json::Value& jvRequest) { BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { - isCurrent->insertSubAccountInfo(naAccountID); + mInfoSub->insertSubAccountInfo(naAccountID); } - mNetOps->unsubAccount(isCurrent, usnaAccoundIds,false); + mNetOps->unsubAccount(mInfoSub, usnaAccoundIds,false); } } diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 7e804d197..7be97a6d5 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -7,7 +7,7 @@ class NetworkOPs; class RPCHandler { NetworkOPs* mNetOps; - InfoSub* isCurrent; + InfoSub* mInfoSub; typedef Json::Value (RPCHandler::*doFuncPtr)(const Json::Value ¶ms); enum { @@ -157,8 +157,9 @@ public: enum { GUEST, USER, ADMIN }; RPCHandler(NetworkOPs* netOps); + RPCHandler(NetworkOPs* netOps, InfoSub* infoSub); - Json::Value doCommand(const std::string& command, Json::Value& params, int role, InfoSub* sub = NULL); + Json::Value doCommand(const std::string& command, Json::Value& params, int role); Json::Value rpcError(int iError); Json::Value handleJSONSubmit(const Json::Value& jvRequest); diff --git a/src/cpp/ripple/RPCServer.cpp b/src/cpp/ripple/RPCServer.cpp index 01568d999..3b87189a0 100644 --- a/src/cpp/ripple/RPCServer.cpp +++ b/src/cpp/ripple/RPCServer.cpp @@ -26,7 +26,7 @@ SETUP_LOG(); #endif RPCServer::RPCServer(boost::asio::io_service& io_service , NetworkOPs* nopNetwork) - : mNetOps(nopNetwork), mRPCHandler(nopNetwork), mSocket(io_service) + : mNetOps(nopNetwork), mSocket(io_service) { mRole = RPCHandler::GUEST; @@ -142,6 +142,8 @@ std::string RPCServer::handleRequest(const std::string& requestStr) else if (!valParams.isArray()) return(HTTPReply(400, "params unparseable")); + RPCHandler mRPCHandler(mNetOps); + cLog(lsTRACE) << valParams; Json::Value result = mRPCHandler.doCommand(strMethod, valParams,mRole); cLog(lsTRACE) << result; diff --git a/src/cpp/ripple/RPCServer.h b/src/cpp/ripple/RPCServer.h index a2b51caab..c96774354 100644 --- a/src/cpp/ripple/RPCServer.h +++ b/src/cpp/ripple/RPCServer.h @@ -22,9 +22,7 @@ public: private: - NetworkOPs* mNetOps; - RPCHandler mRPCHandler; boost::asio::ip::tcp::socket mSocket; diff --git a/src/cpp/ripple/WSConnection.cpp b/src/cpp/ripple/WSConnection.cpp index f06c798a5..ae6a70ee8 100644 --- a/src/cpp/ripple/WSConnection.cpp +++ b/src/cpp/ripple/WSConnection.cpp @@ -45,16 +45,16 @@ Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) return jvResult; } + RPCHandler mRPCHandler(&mNetwork, this); Json::Value jvResult(Json::objectValue); // Regular RPC command - jvResult["result"] = theApp->getRPCHandler().doCommand( + jvResult["result"] = mRPCHandler.doCommand( jvRequest["command"].asString(), jvRequest.isMember("params") ? jvRequest["params"] : jvRequest, - mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN, - this); + mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN); // Currently we will simply unwrap errors returned by the RPC // API, in the future maybe we can make the responses