diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index eab46a592..0997f42af 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -28,6 +28,19 @@ public: virtual ~InfoSub() { ; } virtual void send(const Json::Value& jvObj) = 0; + +protected: + boost::unordered_set mSubAccountInfo; + boost::unordered_set mSubAccountTransaction; + + boost::mutex mLock; + +public: + 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 fbd24d555..5d8ca3ef4 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." }, @@ -92,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) @@ -1309,53 +1317,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 +1383,10 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { return rpcError(rpcNO_PERMISSION); } + else if (commandsA[i].mEvented && mInfoSub == NULL) + { + return rpcError(rpcNO_EVENTS); + } else if (commandsA[i].iMinParams >= 0 ? commandsA[i].iMaxParams ? (params.size() < commandsA[i].iMinParams @@ -1933,4 +1950,183 @@ 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(mInfoSub, jvResult); + }else if(streamName=="ledger") + { + mNetOps->subLedger(mInfoSub, jvResult); + }else if(streamName=="transactions") + { + mNetOps->subTransactions(mInfoSub); + }else if(streamName=="rt_transactions") + { + mNetOps->subRTTransactions(mInfoSub); + }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_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mInfoSub->insertSubAccountInfo(naAccountID); + } + + mNetOps->subAccount(mInfoSub, usnaAccoundIds, true); + } + } + + if (jvRequest.isMember("accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mInfoSub->insertSubAccountInfo(naAccountID); + } + + mNetOps->subAccount(mInfoSub, 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(mInfoSub); + }else if(streamName=="ledger") + { + mNetOps->unsubLedger(mInfoSub); + }else if(streamName=="transactions") + { + mNetOps->unsubTransactions(mInfoSub); + }else if(streamName=="rt_transactions") + { + mNetOps->unsubRTTransactions(mInfoSub); + }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_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mInfoSub->insertSubAccountInfo(naAccountID); + } + + mNetOps->unsubAccount(mInfoSub, usnaAccoundIds,true); + } + } + + if (jvRequest.isMember("accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mInfoSub->insertSubAccountInfo(naAccountID); + } + + mNetOps->unsubAccount(mInfoSub, usnaAccoundIds,false); + } + } + + return jvResult; +} + // vim:ts=4 diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index ca71d7bac..7be97a6d5 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -6,7 +6,8 @@ class NetworkOPs; class RPCHandler { - NetworkOPs* mNetOps; + NetworkOPs* mNetOps; + InfoSub* mInfoSub; typedef Json::Value (RPCHandler::*doFuncPtr)(const Json::Value ¶ms); enum { @@ -15,6 +16,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 +88,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 +100,7 @@ public: // Misc failure rpcLOAD_FAILED, rpcNO_PERMISSION, + rpcNO_EVENTS, rpcNOT_STANDALONE, // Networking @@ -151,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); + 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 7037836b1..ae6a70ee8 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) - ; - + RPCHandler mRPCHandler(&mNetwork, this); Json::Value jvResult(Json::objectValue); - if (i < 0) + // Regular RPC command + jvResult["result"] = mRPCHandler.doCommand( + jvRequest["command"].asString(), + jvRequest.isMember("params") + ? jvRequest["params"] + : jvRequest, + 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 + // 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..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. // @@ -429,13 +444,13 @@ Remote.prototype._connect_message = function (ws, json) { if (!request) { unexpected = true; } - else if ('success' === message.result) { - if (this.trace) console.log("message: %s", json); + else if ('success' === message.status) { + if (this.trace) console.log("remote: response: %s", json); - request.emit('success', message); + 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', @@ -509,13 +524,33 @@ 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. var request = new Request(this, 'rpc'); - request.message.rpc_command = 'ledger_closed'; + request.message.command = 'ledger_closed'; return request; }; @@ -525,7 +560,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 +575,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 +628,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,18 +659,55 @@ 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); }; +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; - if (this.trace) console.log("remote: submit: %s", JSON.stringify(transaction.tx_json)); - if (transaction.secret && !this.trusted) { transaction.emit('error', { @@ -658,7 +748,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 +775,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 +805,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 +856,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 +896,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. @@ -861,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); };