diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 826b55fa5..91dacfb38 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -282,7 +282,7 @@ void Application::loadOldLedger() if (!lastLedger) { - std::cout << "No Ledger found?" << std::endl; + cLog(lsFATAL) << "No Ledger found?" << std::endl; exit(-1); } lastLedger->setClosed(); diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index a8337198e..1b10a077c 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -13,11 +13,16 @@ #include "../json/value.h" #include "../json/reader.h" -#include "CallRPC.h" #include "RPC.h" +#include "Log.h" +#include "RPCErr.h" #include "Config.h" #include "BitcoinUtil.h" +#include "CallRPC.h" + +SETUP_LOG(); + static inline bool isSwitchChar(char c) { #ifdef __WXMSW__ @@ -46,73 +51,393 @@ std::string EncodeBase64(const std::string& s) return result; } +Json::Value RPCParser::parseAsIs(const Json::Value& jvParams) +{ + return Json::Value(Json::objectValue); +} + +// account_info || +// account_info || [] +Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams) +{ + Json::Value jvRequest(Json::objectValue); + std::string strIdent = jvParams[0u].asString(); + // YYY This could be more strict and report casting errors. + int iIndex = 2 == jvParams.size() ? lexical_cast_s(jvParams[1u].asString()) : 0; + + RippleAddress raAddress; + + if (!raAddress.setAccountPublic(strIdent) && !raAddress.setAccountID(strIdent) && !raAddress.setSeedGeneric(strIdent)) + return rpcError(rpcACT_MALFORMED); + + jvRequest["ident"] = strIdent; + jvRequest["index"] = iIndex; + + return jvRequest; +} + +// account_tx +// account_tx +Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) +{ + Json::Value jvRequest(Json::objectValue); + RippleAddress raAccount; + + if (jvParams.size() < 2 || jvParams.size() > 3) + return rpcError(rpcINVALID_PARAMS); + + if (!raAccount.setAccountID(jvParams[0u].asString())) + return rpcError(rpcACT_MALFORMED); + + // YYY This could be more strict and report casting errors. + if (jvParams.size() == 2) + { + jvRequest["ledger"] = jvParams[1u].asUInt(); + } + else + { + uint32 uLedgerMin = jvParams[1u].asUInt(); + uint32 uLedgerMax = jvParams[2u].asUInt(); + + if ((uLedgerMax < uLedgerMin) || (uLedgerMax == 0)) + { + return rpcError(rpcLGR_IDXS_INVALID); + } + + jvRequest["ledger_min"] = uLedgerMin; + jvRequest["ledger_max"] = uLedgerMax; + } + + jvRequest["account"] = raAccount.humanAccountID(); + + return jvRequest; +} + +// connect [port] +Json::Value RPCParser::parseConnect(const Json::Value& jvParams) +{ + Json::Value jvRequest(Json::objectValue); + + jvRequest["ip"] = jvParams[0u].asString(); + + if (jvParams.size() == 2) + jvRequest["port"] = jvParams[1u].asUInt(); + + return jvRequest; +} + +Json::Value RPCParser::parseEvented(const Json::Value& jvParams) +{ + return rpcError(rpcNO_EVENTS); +} + +// ledger [id|ledger_current|ledger_closed] [full] +Json::Value RPCParser::parseLedger(const Json::Value& jvParams) +{ + Json::Value jvRequest(Json::objectValue); + + if (!jvParams.size()) + { + return jvRequest; + } + + std::string strLedger = jvParams[0u].asString(); + + if (strLedger == "current" || strLedger == "ledger_closed" || strLedger.length() > 12) + jvRequest["ledger"] = strLedger; + else + jvRequest["ledger"] = lexical_cast_s(strLedger); + + if (2 == jvParams.size() && jvParams[1u].asString() == "full") + { + jvRequest["full"] = bool(1); + } + + return jvRequest; +} + +// ripple_lines_get || [] +Json::Value RPCParser::parseRippleLinesGet(const Json::Value& jvParams) +{ + std::string strIdent = jvParams[0u].asString(); + bool bIndex = 2 == jvParams.size(); + int iIndex = bIndex ? lexical_cast_s(jvParams[1u].asString()) : 0; + + if (bIndex && !iIndex) // Don't send default. + bIndex = false; + + // Get info on account. + Json::Value jvRequest(Json::objectValue); + + jvRequest["account"] = strIdent; + if (bIndex) + jvRequest["index"] = iIndex; + + return jvRequest; +} + +// submit any transaction to the network +// submit private_key json +Json::Value RPCParser::parseSubmit(const Json::Value& jvParams) +{ + Json::Value txJSON; + Json::Reader reader; + + if (reader.parse(jvParams[1u].asString(), txJSON)) + { + Json::Value jvRequest; + + jvRequest["secret"] = jvParams[0u].asString(); + jvRequest["tx_json"] = txJSON; + + return jvRequest; + } + + return rpcError(rpcINVALID_PARAMS); +} + +// unl_add | [] +Json::Value RPCParser::parseUnlAdd(const Json::Value& jvParams) +{ + std::string strNode = jvParams[0u].asString(); + std::string strComment = (jvParams.size() == 2) ? jvParams[1u].asString() : ""; + + RippleAddress naNodePublic; + + if (strNode.length()) + { + Json::Value jvRequest; + + jvRequest["node"] = strNode; + + if (strComment.length()) + jvRequest["comment"] = strComment; + + return jvRequest; + } + + return rpcError(rpcINVALID_PARAMS); +} + +// unl_delete | +Json::Value RPCParser::parseUnlDelete(const Json::Value& jvParams) +{ + Json::Value jvRequest; + + jvRequest["node"] = jvParams[0u].asString(); + + return jvRequest; +} + +// wallet_accounts +Json::Value RPCParser::parseWalletAccounts(const Json::Value& jvParams) +{ + Json::Value jvRequest; + + jvRequest["seed"] = jvParams[0u].asString(); + + return jvRequest; +} + +// +// parseCommand +// + +// Convert a rpc method and params to a request. +// <-- { method: xyz, params: [... ] } or { error: ..., ... } +Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) +{ + cLog(lsTRACE) << "RPC method:" << strMethod; + cLog(lsTRACE) << "RPC params:" << jvParams; + + static struct { + const char* pCommand; + parseFuncPtr pfpFunc; + int iMinParams; + int iMaxParams; + } commandsA[] = { + // Request-response methods + // - Returns an error, or the request. + // - To modify the method, provide a new method in the request. + { "accept_ledger", &RPCParser::parseAsIs, 0, 0 }, + { "account_info", &RPCParser::parseAccountInfo, 1, 2 }, + { "account_tx", &RPCParser::parseAccountTransactions, 2, 3 }, + { "connect", &RPCParser::parseConnect, 1, 2 }, +// { "data_delete", &RPCParser::doDataDelete, 1, 1, true, false, optNone }, +// { "data_fetch", &RPCParser::doDataFetch, 1, 1, true, false, optNone }, +// { "data_store", &RPCParser::doDataStore, 2, 2, true, false, optNone }, +// { "get_counts", &RPCParser::doGetCounts, 0, 1, true, false, optNone }, + { "ledger", &RPCParser::parseLedger, 0, 2 }, + { "ledger_accept", &RPCParser::parseAsIs, 0, 0 }, + { "ledger_closed", &RPCParser::parseAsIs, 0, 0 }, + { "ledger_current", &RPCParser::parseAsIs, 0, 0 }, +// { "ledger_entry", &RPCParser::doLedgerEntry, -1, -1, false, false, optCurrent }, +// { "ledger_header", &RPCParser::doLedgerHeader, -1, -1, false, false, optCurrent }, +// { "log_level", &RPCParser::doLogLevel, 0, 2, true, false, optNone }, + { "logrotate", &RPCParser::parseAsIs, 0, 0 }, +// { "nickname_info", &RPCParser::doNicknameInfo, 1, 1, false, false, optCurrent }, +// { "owner_info", &RPCParser::doOwnerInfo, 1, 2, false, false, optCurrent }, + { "peers", &RPCParser::parseAsIs, 0, 0 }, +// { "profile", &RPCParser::doProfile, 1, 9, false, false, optCurrent }, + { "ripple_lines_get", &RPCParser::parseRippleLinesGet, 1, 2 }, +// { "ripple_path_find", &RPCParser::doRipplePathFind, -1, -1, false, false, optCurrent }, + { "submit", &RPCParser::parseSubmit, 2, 2 }, + { "server_info", &RPCParser::parseAsIs, 0, 0 }, + { "stop", &RPCParser::parseAsIs, 0, 0 }, +// { "transaction_entry", &RPCParser::doTransactionEntry, -1, -1, false, false, optCurrent }, +// { "tx", &RPCParser::doTx, 1, 1, true, false, optNone }, +// { "tx_history", &RPCParser::doTxHistory, 1, 1, false, false, optNone }, +// + { "unl_add", &RPCParser::parseUnlAdd, 1, 2 }, + { "unl_delete", &RPCParser::parseUnlDelete, 1, 1 }, + { "unl_list", &RPCParser::parseAsIs, 0, 0 }, + { "unl_load", &RPCParser::parseAsIs, 0, 0 }, + { "unl_network", &RPCParser::parseAsIs, 0, 0 }, + { "unl_reset", &RPCParser::parseAsIs, 0, 0 }, + { "unl_score", &RPCParser::parseAsIs, 0, 0 }, + +// { "validation_create", &RPCParser::doValidationCreate, 0, 1, false, false, optNone }, +// { "validation_seed", &RPCParser::doValidationSeed, 0, 1, false, false, optNone }, + + { "wallet_accounts", &RPCParser::parseWalletAccounts, 1, 1 }, +// { "wallet_propose", &RPCParser::doWalletPropose, 0, 1, false, false, optNone }, +// { "wallet_seed", &RPCParser::doWalletSeed, 0, 1, false, false, optNone }, +// +// { "login", &RPCParser::doLogin, 2, 2, true, false, optNone }, + + // Evented methods + { "subscribe", &RPCParser::parseEvented, -1, -1 }, + { "unsubscribe", &RPCParser::parseEvented, -1, -1 }, + }; + + int i = NUMBER(commandsA); + + while (i-- && strMethod != commandsA[i].pCommand) + ; + + if (i < 0) + { + return rpcError(rpcBAD_SYNTAX); + } + else if ((commandsA[i].iMinParams >= 0 && jvParams.size() < commandsA[i].iMinParams) + || (commandsA[i].iMaxParams >= 0 && jvParams.size() > commandsA[i].iMaxParams)) + { + cLog(lsWARNING) << "Wrong number of parameters: minimum=" << commandsA[i].iMinParams + << " maximum=" << commandsA[i].iMaxParams + << " actual=" << jvParams.size(); + + return rpcError(rpcBAD_SYNTAX); + } + + return (this->*(commandsA[i].pfpFunc))(jvParams); +} + int commandLineRPC(const std::vector& vCmd) { - std::string strPrint; - int nRet = 0; + Json::Value jvOutput; + int nRet = 0; + Json::Value jvRequest(Json::objectValue); + try { - if (vCmd.empty()) return 1; + RPCParser rpParser; + Json::Value jvRpcParams(Json::arrayValue); - std::string strMethod = vCmd[0]; + if (vCmd.empty()) return 1; // 1 = print usage. - // Parameters default to strings - Json::Value params(Json::arrayValue); for (int i = 1; i != vCmd.size(); i++) - params.append(vCmd[i]); + jvRpcParams.append(vCmd[i]); - // Execute - Json::Value reply = callRPC(strMethod, params); + Json::Value jvRpc = Json::Value(Json::objectValue); - // Parse reply - Json::Value result = reply.get("result", Json::Value()); - Json::Value error = reply.get("error", Json::Value()); + jvRpc["method"] = vCmd[0]; + jvRpc["params"] = jvRpcParams; - if (result.isString() && (result.asString() == "unknown command")) - nRet=1; + jvRequest = rpParser.parseCommand(vCmd[0], jvRpcParams); - if (!error.isNull()) - { // Error - strPrint = "error: " + error.toStyledString(); - int code = error["code"].asInt(); - nRet = abs(code); + // std::cerr << "Request: " << jvRequest << std::endl; + + if (jvRequest.isMember("error")) + { + jvOutput = jvRequest; + jvOutput["rpc"] = jvRpc; } else - { // Result - if (result.isNull()) - strPrint = ""; - else if (result.isString()) - strPrint = result.asString(); + { + Json::Value jvParams(Json::arrayValue); + + jvParams.append(jvRequest); + + jvOutput = callRPC( + jvRequest.isMember("method") // Allow parser to rewrite method. + ? jvRequest["method"].asString() + : vCmd[0], + jvParams); // Parsed, execute. + + if (jvOutput.isMember("result")) + { + // Had a successful JSON-RPC 2.0 call. + jvOutput = jvOutput["result"]; + + // jvOutput may report a server side error. + // It should report "status". + } else - strPrint = result.toStyledString(); + { + // Transport error. + Json::Value jvRpcError = jvOutput; + + jvOutput = rpcError(rpcJSON_RPC); + jvOutput["result"] = jvRpcError; + } + + // If had an error, supply invokation in result. + if (jvOutput.isMember("error")) + { + jvOutput["rpc"] = jvRpc; // How the command was seen as method + params. + jvOutput["request_sent"] = jvRequest; // How the command was translated. + } } + + if (jvOutput.isMember("error")) + { + jvOutput["status"] = "error"; + + nRet = jvOutput.isMember("error_code") + ? lexical_cast_s(jvOutput["error_code"].asString()) + : 1; + } + + // YYY We could have a command line flag for single line output for scripts. + // YYY We would intercept output here and simplify it. } catch (std::exception& e) { - strPrint = std::string("error: ") + e.what(); - nRet = 87; + jvOutput = rpcError(rpcINTERNAL); + jvOutput["error_what"] = e.what(); + nRet = rpcINTERNAL; } catch (...) { - std::cout << "Exception CommandLineRPC()" << std::endl; + jvOutput = rpcError(rpcINTERNAL); + jvOutput["error_what"] = "exception"; + nRet = rpcINTERNAL; } - if (strPrint != "") - { - std::cout << strPrint << std::endl; - } + std::cout << jvOutput.toStyledString(); + return nRet; } - Json::Value callRPC(const std::string& strMethod, const Json::Value& params) { if (theConfig.RPC_USER.empty() && theConfig.RPC_PASSWORD.empty()) - throw std::runtime_error("You must set rpcpassword= in the configuration file" + throw std::runtime_error("You must set rpcpassword= in the configuration file. " "If the file does not exist, create it with owner-readable-only file permissions."); // Connect to localhost - std::cout << "Connecting to: " << theConfig.RPC_IP << ":" << theConfig.RPC_PORT << std::endl; + if (!theConfig.QUIET) + std::cerr << "Connecting to: " << theConfig.RPC_IP << ":" << theConfig.RPC_PORT << std::endl; boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(theConfig.RPC_IP), theConfig.RPC_PORT); @@ -128,11 +453,11 @@ Json::Value callRPC(const std::string& strMethod, const Json::Value& params) // Send request std::string strRequest = JSONRPCRequest(strMethod, params, Json::Value(1)); - std::cout << "send request " << strMethod << " : " << strRequest << std::endl; + cLog(lsDEBUG) << "send request " << strMethod << " : " << strRequest << std::endl; std::string strPost = createHTTPPost(strRequest, mapRequestHeaders); stream << strPost << std::flush; - // std::cout << "post " << strPost << std::endl; + // std::cerr << "post " << strPost << std::endl; // Receive reply std::map mapHeaders; @@ -146,11 +471,14 @@ Json::Value callRPC(const std::string& strMethod, const Json::Value& params) throw std::runtime_error("no response from server"); // Parse reply - std::cout << "RPC reply: " << strReply << std::endl; + cLog(lsDEBUG) << "RPC reply: " << strReply << std::endl; + Json::Reader reader; Json::Value valReply; + if (!reader.parse(strReply, valReply)) throw std::runtime_error("couldn't parse reply from server"); + if (valReply.isNull()) throw std::runtime_error("expected reply to have result, error and id properties"); diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index ac6fc3dae..99d5caabd 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -1,7 +1,35 @@ +#ifndef __CALLRPC__ +#define __CALLRPC__ + #include #include "../json/value.h" +class RPCParser +{ +protected: + typedef Json::Value (RPCParser::*parseFuncPtr)(const Json::Value &jvParams); + + Json::Value parseAsIs(const Json::Value& jvParams); + Json::Value parseAccountInfo(const Json::Value& jvParams); + Json::Value parseAccountTransactions(const Json::Value& jvParams); + Json::Value parseConnect(const Json::Value& jvParams); + Json::Value parseEvented(const Json::Value& jvParams); + Json::Value parseLedger(const Json::Value& jvParams); + Json::Value parseWalletAccounts(const Json::Value& jvParams); + Json::Value parseRippleLinesGet(const Json::Value& jvParams); + Json::Value parseSubmit(const Json::Value& jvParams); + Json::Value parseUnlAdd(const Json::Value& jvParams); + Json::Value parseUnlDelete(const Json::Value& jvParams); + +public: + Json::Value parseCommand(std::string strMethod, Json::Value jvParams); +}; + extern int commandLineRPC(const std::vector& vCmd); extern Json::Value callRPC(const std::string& strMethod, const Json::Value& params); + +#endif + +// vim:ts=4 diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp index 4701634fb..03c545332 100644 --- a/src/cpp/ripple/Config.cpp +++ b/src/cpp/ripple/Config.cpp @@ -1,3 +1,6 @@ +// +// TODO: Check permissions on config file before using it. +// #include "Config.h" #include "utils.h" @@ -48,7 +51,7 @@ Config theConfig; -void Config::setup(const std::string& strConf) +void Config::setup(const std::string& strConf, bool bQuiet) { boost::system::error_code ec; @@ -58,6 +61,8 @@ void Config::setup(const std::string& strConf) // that with "db" as the data directory. // + QUIET = bQuiet; + if (!strConf.empty()) { // --conf= : everything is relative that file. @@ -172,7 +177,8 @@ void Config::setup(const std::string& strConf) void Config::load() { - std::cout << "Loading: " << CONFIG_FILE << std::endl; + if (!QUIET) + std::cerr << "Loading: " << CONFIG_FILE << std::endl; std::ifstream ifsConfig(CONFIG_FILE.c_str(), std::ios::in); diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h index 9704396f7..b7e4898c0 100644 --- a/src/cpp/ripple/Config.h +++ b/src/cpp/ripple/Config.h @@ -46,6 +46,8 @@ class Config { public: // Configuration parameters + bool QUIET; + boost::filesystem::path CONFIG_FILE; boost::filesystem::path CONFIG_DIR; boost::filesystem::path DATA_DIR; @@ -113,7 +115,7 @@ public: // Client behavior int ACCOUNT_PROBE_MAX; // How far to scan for accounts. - void setup(const std::string& strConf); + void setup(const std::string& strConf, bool bQuiet); void load(); }; diff --git a/src/cpp/ripple/ConnectionPool.cpp b/src/cpp/ripple/ConnectionPool.cpp index 85bc05ea5..6de593b2b 100644 --- a/src/cpp/ripple/ConnectionPool.cpp +++ b/src/cpp/ripple/ConnectionPool.cpp @@ -101,7 +101,7 @@ bool ConnectionPool::savePeer(const std::string& strIp, int iPort, char code) } else { - std::cout << "Error saving Peer" << std::endl; + std::cerr << "Error saving Peer" << std::endl; } if (bNew) diff --git a/src/cpp/ripple/PeerDoor.cpp b/src/cpp/ripple/PeerDoor.cpp index a3740846b..97ddc1c2c 100644 --- a/src/cpp/ripple/PeerDoor.cpp +++ b/src/cpp/ripple/PeerDoor.cpp @@ -54,7 +54,7 @@ void PeerDoor::handleConnect(Peer::pointer new_connection, { new_connection->connected(error); } - else cout << "Error: " << error; + else cerr << "Error: " << error; startListening(); } diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp new file mode 100644 index 000000000..9c4112db1 --- /dev/null +++ b/src/cpp/ripple/RPCErr.cpp @@ -0,0 +1,84 @@ + +#include "Log.h" + +#include "RPCErr.h" +#include "utils.h" + +#include "../json/writer.h" + +SETUP_LOG(); + +Json::Value rpcError(int iError, Json::Value jvResult) +{ + static struct { + int iError; + const char* pToken; + const char* pMessage; + } errorInfoA[] = { + { rpcACT_EXISTS, "actExists", "Account already exists." }, + { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, + { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, + { rpcBAD_SEED, "badSeed", "Disallowed seed." }, + { rpcBAD_SYNTAX, "badSyntax", "Syntax error." }, + { rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." }, + { rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exists." }, + { rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." }, + { rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." }, + { rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." }, + { rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets amount malformed." }, + { rpcHOST_IP_MALFORMED, "hostIpMalformed", "Host IP is malformed." }, + { rpcINSUF_FUNDS, "insufFunds", "Insufficient funds." }, + { rpcINTERNAL, "internal", "Internal error." }, + { rpcINVALID_PARAMS, "invalidParams", "Invalid parameters." }, + { rpcJSON_RPC, "json_rpc", "JSON-RPC transport error." }, + { rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid." }, + { rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed." }, + { rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found." }, + { rpcNICKNAME_MALFORMED, "nicknameMalformed","Nickname is malformed." }, + { rpcNICKNAME_MISSING, "nicknameMissing", "Nickname does not exist." }, + { rpcNICKNAME_PERM, "nicknamePerm", "Account does not control nickname." }, + { rpcNOT_IMPL, "notImpl", "Not implemented." }, + { 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_PATH, "noPath", "Unable to find a ripple path." }, + { rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." }, + { rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." }, + { rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." }, + { rpcPAYS_ACT_MALFORMED, "paysActMalformed", "Pays account malformed." }, + { rpcPAYS_AMT_MALFORMED, "paysAmtMalformed", "Pays amount malformed." }, + { rpcPORT_MALFORMED, "portMalformed", "Port is malformed." }, + { rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed." }, + { rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed." }, + { rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." }, + { rpcSRC_ACT_MISSING, "srcActMissing", "Source account does not exist." }, + { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." }, + { rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." }, + { rpcSUCCESS, "success", "Success." }, + { rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." }, + { rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown command." }, + { rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." }, + }; + + int i; + + for (i=NUMBER(errorInfoA); i-- && errorInfoA[i].iError != iError;) + ; + + jvResult["error"] = i >= 0 ? errorInfoA[i].pToken : lexical_cast_i(iError); + jvResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexical_cast_i(iError); + jvResult["error_code"] = iError; + + if (i >= 0) + { + cLog(lsDEBUG) << "rpcError: " + << errorInfoA[i].pToken << ": " << errorInfoA[i].pMessage << std::endl; + } + + return jvResult; +} + +// vim:ts=4 diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h new file mode 100644 index 000000000..f2b73336e --- /dev/null +++ b/src/cpp/ripple/RPCErr.h @@ -0,0 +1,75 @@ +#ifndef __RPCERR__ +#define __RPCERR__ + +#include "../json/value.h" + +enum { + rpcSUCCESS, + + rpcBAD_SYNTAX, // Must be 1 to print usage to command line. + rpcJSON_RPC, + + // Error numbers beyond this line are not stable between versions. + // Programs should use error tokens. + + // Misc failure + rpcLOAD_FAILED, + rpcNO_PERMISSION, + rpcNO_EVENTS, + rpcNOT_STANDALONE, + + // Networking + rpcNO_CLOSED, + rpcNO_CURRENT, + rpcNO_NETWORK, + + // Ledger state + rpcACT_EXISTS, + rpcACT_NOT_FOUND, + rpcINSUF_FUNDS, + rpcLGR_NOT_FOUND, + rpcNICKNAME_MISSING, + rpcNO_ACCOUNT, + rpcNO_PATH, + rpcPASSWD_CHANGED, + rpcSRC_MISSING, + rpcSRC_UNCLAIMED, + rpcTXN_NOT_FOUND, + rpcWRONG_SEED, + + // Malformed command + rpcINVALID_PARAMS, + rpcUNKNOWN_COMMAND, + + // Bad parameter + rpcACT_MALFORMED, + rpcQUALITY_MALFORMED, + rpcBAD_SEED, + rpcDST_ACT_MALFORMED, + rpcDST_ACT_MISSING, + rpcDST_AMT_MALFORMED, + rpcGETS_ACT_MALFORMED, + rpcGETS_AMT_MALFORMED, + rpcHOST_IP_MALFORMED, + rpcLGR_IDXS_INVALID, + rpcLGR_IDX_MALFORMED, + rpcNICKNAME_MALFORMED, + rpcNICKNAME_PERM, + rpcPAYS_ACT_MALFORMED, + rpcPAYS_AMT_MALFORMED, + rpcPORT_MALFORMED, + rpcPUBLIC_MALFORMED, + rpcSRC_ACT_MALFORMED, + rpcSRC_ACT_MISSING, + rpcSRC_AMT_MALFORMED, + + // Internal error (should never happen) + rpcINTERNAL, // Generic internal error. + rpcFAIL_GEN_DECRPYT, + rpcNOT_IMPL, + rpcNO_GEN_DECRPYT, +}; + +Json::Value rpcError(int iError, Json::Value jvResult=Json::Value(Json::objectValue)); +#endif +// vim:ts=4 diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 4b7d6acea..b2d602824 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -10,6 +10,7 @@ #include "Wallet.h" #include "RippleAddress.h" #include "RippleCalc.h" +#include "RPCErr.h" #include "AccountState.h" #include "NicknameState.h" #include "InstanceCounter.h" @@ -20,77 +21,6 @@ SETUP_LOG(); -Json::Value RPCHandler::rpcError(int iError) -{ - static struct { - int iError; - const char* pToken; - const char* pMessage; - } errorInfoA[] = { - { rpcACT_EXISTS, "actExists", "Account already exists." }, - { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, - { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, - { rpcBAD_SEED, "badSeed", "Disallowed seed." }, - { rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." }, - { rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exists." }, - { rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." }, - { rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." }, - { rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." }, - { rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets amount malformed." }, - { rpcHOST_IP_MALFORMED, "hostIpMalformed", "Host IP is malformed." }, - { rpcINSUF_FUNDS, "insufFunds", "Insufficient funds." }, - { rpcINTERNAL, "internal", "Internal error." }, - { rpcINVALID_PARAMS, "invalidParams", "Invalid parameters." }, - { rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid." }, - { rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed." }, - { rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found." }, - { rpcNICKNAME_MALFORMED, "nicknameMalformed","Nickname is malformed." }, - { rpcNICKNAME_MISSING, "nicknameMissing", "Nickname does not exist." }, - { rpcNICKNAME_PERM, "nicknamePerm", "Account does not control nickname." }, - { rpcNOT_IMPL, "notImpl", "Not implemented." }, - { 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_PATH, "noPath", "Unable to find a ripple path." }, - { rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." }, - { rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." }, - { rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." }, - { rpcPAYS_ACT_MALFORMED, "paysActMalformed", "Pays account malformed." }, - { rpcPAYS_AMT_MALFORMED, "paysAmtMalformed", "Pays amount malformed." }, - { rpcPORT_MALFORMED, "portMalformed", "Port is malformed." }, - { rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed." }, - { rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed." }, - { rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." }, - { rpcSRC_ACT_MISSING, "srcActMissing", "Source account does not exist." }, - { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." }, - { rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." }, - { rpcSUCCESS, "success", "Success." }, - { rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." }, - { rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown command." }, - { rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." }, - }; - - int i; - - for (i=NUMBER(errorInfoA); i-- && errorInfoA[i].iError != iError;) - ; - - Json::Value jsonResult(Json::objectValue); - - jsonResult["error"] = i >= 0 ? errorInfoA[i].pToken : lexical_cast_i(iError); - jsonResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexical_cast_i(iError); - jsonResult["error_code"] = iError; - if (i >= 0) - std::cerr << "rpcError: " - << errorInfoA[i].pToken << ": " << errorInfoA[i].pMessage << std::endl; - - return jsonResult; -} - - RPCHandler::RPCHandler(NetworkOPs* netOps) { mNetOps=netOps; @@ -103,7 +33,7 @@ RPCHandler::RPCHandler(NetworkOPs* netOps, InfoSub* infoSub) mInfoSub=infoSub; } -int RPCHandler::getParamCount(const Json::Value& params) +int RPCHandler::getParamCount(Json::Value params) { // If non-array, only counts strings if (params.isNull()) return 0; if (params.isArray()) return params.size(); @@ -325,26 +255,32 @@ Json::Value RPCHandler::accountFromString(const uint256& uLedger, RippleAddress& return Json::Value(Json::objectValue); } -Json::Value RPCHandler::doAcceptLedger(const Json::Value ¶ms) +Json::Value RPCHandler::doAcceptLedger(Json::Value jvRequest) { if (!theConfig.RUN_STANDALONE) return rpcError(rpcNOT_STANDALONE); - Json::Value obj(Json::objectValue); - obj["newLedger"] = theApp->getOPs().acceptLedger(); - return obj; + Json::Value jvResult(Json::objectValue); + + jvResult["newLedger"] = theApp->getOPs().acceptLedger(); + + return jvResult; } -// account_info || -// account_info || [] -Json::Value RPCHandler::doAccountInfo(const Json::Value ¶ms) +// { 'ident' : _indent_, 'index' : _index_ // optional } +Json::Value RPCHandler::doAccountInfo(Json::Value jvRequest) { - std::string strIdent = params[0u].asString(); + // cLog(lsDEBUG) << "doAccountInfo: " << jvRequest; + + if (!jvRequest.isMember("ident")) + return rpcError(rpcINVALID_PARAMS); + + std::string strIdent = jvRequest["ident"].asString(); bool bIndex; - int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; + int iIndex = jvRequest.isMember("index") ? jvRequest["index"].asUInt() : 0; RippleAddress naAccount; - Json::Value ret; + Json::Value jvResult; // Get info on account. @@ -359,7 +295,7 @@ Json::Value RPCHandler::doAccountInfo(const Json::Value ¶ms) asAccepted->addJson(jAccepted); } - ret["accepted"] = jAccepted; + jvResult["accepted"] = jAccepted; Json::Value jCurrent = accountFromString(uint256(0), naAccount, bIndex, strIdent, iIndex); @@ -371,46 +307,32 @@ Json::Value RPCHandler::doAccountInfo(const Json::Value ¶ms) asCurrent->addJson(jCurrent); } - ret["current"] = jCurrent; + jvResult["current"] = jCurrent; #if 0 if (!jAccepted && !asCurrent) { - ret["account"] = naAccount.humanAccountID(); - ret["status"] = "NotFound"; + jvResult["account"] = naAccount.humanAccountID(); + jvResult["status"] = "NotFound"; if (bIndex) - ret["index"] = iIndex; + jvResult["index"] = iIndex; } #endif - return ret; + return jvResult; } - - - -Json::Value RPCHandler::doConnect(const Json::Value& params) +// { +// ip: , +// port: +// } +// XXX Might allow domain for manual connections. +Json::Value RPCHandler::doConnect(Json::Value jvParams) { if (theConfig.RUN_STANDALONE) return "cannot connect in standalone mode"; - // connect [port] - std::string strIp; - int iPort = -1; - - // XXX Might allow domain for manual connections. - if (!extractString(strIp, params, 0)) - return rpcError(rpcHOST_IP_MALFORMED); - - if (params.size() == 2) - { - std::string strPort; - - // YYY Should make an extract int. - if (!extractString(strPort, params, 1)) - return rpcError(rpcPORT_MALFORMED); - - iPort = lexical_cast_s(strPort); - } + std::string strIp = jvParams["ip"].asString(); + int iPort = jvParams.isMember("port") ? jvParams["port"].asInt() : -1; // XXX Validate legal IP and port theApp->getConnectionPool().connectTo(strIp, iPort); @@ -419,7 +341,7 @@ Json::Value RPCHandler::doConnect(const Json::Value& params) } // data_delete -Json::Value RPCHandler::doDataDelete(const Json::Value& params) +Json::Value RPCHandler::doDataDelete(Json::Value params) { std::string strKey = params[0u].asString(); @@ -438,7 +360,7 @@ Json::Value RPCHandler::doDataDelete(const Json::Value& params) } // data_fetch -Json::Value RPCHandler::doDataFetch(const Json::Value& params) +Json::Value RPCHandler::doDataFetch(Json::Value params) { std::string strKey = params[0u].asString(); std::string strValue; @@ -453,7 +375,7 @@ Json::Value RPCHandler::doDataFetch(const Json::Value& params) } // data_store -Json::Value RPCHandler::doDataStore(const Json::Value& params) +Json::Value RPCHandler::doDataStore(Json::Value params) { std::string strKey = params[0u].asString(); std::string strValue = params[1u].asString(); @@ -475,7 +397,7 @@ Json::Value RPCHandler::doDataStore(const Json::Value& params) // nickname_info // Note: Nicknames are not automatically looked up by commands as they are advisory and can be changed. -Json::Value RPCHandler::doNicknameInfo(const Json::Value& params) +Json::Value RPCHandler::doNicknameInfo(Json::Value params) { std::string strNickname = params[0u].asString(); boost::trim(strNickname); @@ -503,7 +425,7 @@ Json::Value RPCHandler::doNicknameInfo(const Json::Value& params) // owner_info || // owner_info || [] -Json::Value RPCHandler::doOwnerInfo(const Json::Value& params) +Json::Value RPCHandler::doOwnerInfo(Json::Value params) { std::string strIdent = params[0u].asString(); bool bIndex; @@ -526,8 +448,7 @@ Json::Value RPCHandler::doOwnerInfo(const Json::Value& params) return ret; } - -Json::Value RPCHandler::doPeers(const Json::Value& params) +Json::Value RPCHandler::doPeers(Json::Value) { Json::Value obj(Json::objectValue); @@ -541,7 +462,7 @@ Json::Value RPCHandler::doPeers(const Json::Value& params) // issuer is the offering account // --> submit: 'submit|true|false': defaults to false // Prior to running allow each to have a credit line of what they will be getting from the other account. -Json::Value RPCHandler::doProfile(const Json::Value ¶ms) +Json::Value RPCHandler::doProfile(Json::Value params) { /* need to fix now that sharedOfferCreate is gone int iArgs = params.size(); @@ -628,42 +549,42 @@ Json::Value RPCHandler::doProfile(const Json::Value ¶ms) return obj; } -// ripple_lines_get || [] -Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms) +// { +// account: || [] +// index: // optional, defaults to 0. +// } +Json::Value RPCHandler::doRippleLinesGet(Json::Value jvRequest) { - // uint256 uAccepted = mNetOps->getClosedLedgerHash(); + std::string strIdent = jvRequest["account"].asString(); + bool bIndex = jvRequest.isMember("index"); + int iIndex = bIndex ? jvRequest["index"].asUInt() : 0; - std::string strIdent = params[0u].asString(); - bool bIndex; - int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; + RippleAddress raAccount; - RippleAddress naAccount; + Json::Value jvResult; - Json::Value ret; + jvResult = accountFromString(uint256(0), raAccount, bIndex, strIdent, iIndex); - ret = accountFromString(uint256(0), naAccount, bIndex, strIdent, iIndex); - - if (!ret.empty()) - return ret; + if (!jvResult.empty()) + return jvResult; // Get info on account. - ret = Json::Value(Json::objectValue); - ret["account"] = naAccount.humanAccountID(); + jvResult["account"] = raAccount.humanAccountID(); if (bIndex) - ret["index"] = iIndex; + jvResult["index"] = iIndex; - AccountState::pointer as = mNetOps->getAccountState(uint256(0), naAccount); + AccountState::pointer as = mNetOps->getAccountState(uint256(0), raAccount); if (as) { Json::Value jsonLines(Json::arrayValue); - ret["account"] = naAccount.humanAccountID(); + jvResult["account"] = raAccount.humanAccountID(); // XXX This is wrong, we do access the current ledger and do need to worry about changes. // We access a committed ledger and need not worry about changes. - RippleLines rippleLines(naAccount.getAccountID()); + RippleLines rippleLines(raAccount.getAccountID()); BOOST_FOREACH(RippleState::pointer line, rippleLines.getLines()) { STAmount saBalance = line->getBalance(); @@ -686,14 +607,14 @@ Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms) jsonLines.append(jPeer); } - ret["lines"] = jsonLines; + jvResult["lines"] = jsonLines; } else { - ret = rpcError(rpcACT_NOT_FOUND); + jvResult = rpcError(rpcACT_NOT_FOUND); } - return ret; + return jvResult; } // TODO: @@ -702,7 +623,7 @@ Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms) // - Allows clients to verify path exists. // - Return canonicalized path. // - From a trusted server, allows clients to use path without manipulation. -Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest) +Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest) { Json::Value jvResult(Json::objectValue); RippleAddress raSrc; @@ -869,39 +790,21 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest) return jvResult; } -// submit any transaction to the network -// submit private_key json -Json::Value RPCHandler::doSubmit(const Json::Value& params) -{ - Json::Value txJSON; - Json::Reader reader; - - //std::string hello=params[1u].asString(); - - if (reader.parse(params[1u].asString(), txJSON)) - { - Json::Value jvRequest; - - jvRequest["secret"] = params[0u].asString(); - jvRequest["tx_json"] = txJSON; - - return handleJSONSubmit(jvRequest); - } - - return rpcError(rpcINVALID_PARAMS); -} - -Json::Value RPCHandler::doSubmitJson(const Json::Value& jvRequest) -{ - return handleJSONSubmit(jvRequest); -} - - -Json::Value RPCHandler::handleJSONSubmit(const Json::Value& jvRequest) +// { +// tx_json: , +// secret: +// } +Json::Value RPCHandler::doSubmit(Json::Value jvRequest) { Json::Value jvResult; RippleAddress naSeed; RippleAddress raSrcAddressID; + + if (!jvRequest.isMember("secret") || !jvRequest.isMember("tx_json")) + { + return rpcError(rpcINVALID_PARAMS); + } + Json::Value txJSON = jvRequest["tx_json"]; @@ -1144,7 +1047,7 @@ Json::Value RPCHandler::handleJSONSubmit(const Json::Value& jvRequest) } } -Json::Value RPCHandler::doServerInfo(const Json::Value& params) +Json::Value RPCHandler::doServerInfo(Json::Value) { Json::Value ret(Json::objectValue); @@ -1153,7 +1056,7 @@ Json::Value RPCHandler::doServerInfo(const Json::Value& params) return ret; } -Json::Value RPCHandler::doTxHistory(const Json::Value& params) +Json::Value RPCHandler::doTxHistory(Json::Value params) { if (params.size() == 1) { @@ -1186,7 +1089,7 @@ Json::Value RPCHandler::doTxHistory(const Json::Value& params) return rpcError(rpcSRC_ACT_MALFORMED); } -Json::Value RPCHandler::doTx(const Json::Value& params) +Json::Value RPCHandler::doTx(Json::Value params) { // tx // tx @@ -1212,7 +1115,7 @@ Json::Value RPCHandler::doTx(const Json::Value& params) return rpcError(rpcNOT_IMPL); } -Json::Value RPCHandler::doLedgerClosed(const Json::Value& params) +Json::Value RPCHandler::doLedgerClosed(Json::Value) { Json::Value jvResult; uint256 uLedger = mNetOps->getClosedLedgerHash(); @@ -1223,79 +1126,87 @@ Json::Value RPCHandler::doLedgerClosed(const Json::Value& params) return jvResult; } -Json::Value RPCHandler::doLedgerCurrent(const Json::Value& params) +Json::Value RPCHandler::doLedgerCurrent(Json::Value) { Json::Value jvResult; jvResult["ledger_current_index"] = mNetOps->getCurrentLedgerID(); return jvResult; } -// ledger [id|current|lastclosed] [full] -Json::Value RPCHandler::doLedger(const Json::Value& params) +// ledger [id|ledger_current|lastclosed] [full] +// { +// ledger: 'ledger_current' | 'ledger_closed' | | , // optional +// full: true | false // optional, defaults to false. +// } +Json::Value RPCHandler::doLedger(Json::Value jvParams) { - if (getParamCount(params) == 0) + if (!jvParams.isMember("ledger")) { Json::Value ret(Json::objectValue), current(Json::objectValue), closed(Json::objectValue); + theApp->getLedgerMaster().getCurrentLedger()->addJson(current, 0); theApp->getLedgerMaster().getClosedLedger()->addJson(closed, 0); + ret["open"] = current; ret["closed"] = closed; + return ret; } - std::string param; - if (!extractString(param, params, 0)) - { - return "bad params"; - } - + std::string strLedger = jvParams["ledger"].asString(); Ledger::pointer ledger; - if (param == "current") + + if (strLedger == "ledger_current") ledger = theApp->getLedgerMaster().getCurrentLedger(); - else if ((param == "lastclosed") || (param == "lastaccepted")) + else if (strLedger == "ledger_closed") ledger = theApp->getLedgerMaster().getClosedLedger(); - else if (param.size() > 12) - ledger = theApp->getLedgerMaster().getLedgerByHash(uint256(param)); + else if (strLedger.size() > 12) + ledger = theApp->getLedgerMaster().getLedgerByHash(uint256(strLedger)); else - ledger = theApp->getLedgerMaster().getLedgerBySeq(lexical_cast_s(param)); + ledger = theApp->getLedgerMaster().getLedgerBySeq(jvParams["ledger"].asUInt()); if (!ledger) return rpcError(rpcLGR_NOT_FOUND); - bool full = extractString(param, params, 1) && (param == "full"); + bool full = jvParams.isMember("full") && jvParams["full"].asBool(); + Json::Value ret(Json::objectValue); + ledger->addJson(ret, full ? LEDGER_JSON_FULL : 0); + return ret; } -// account_tx -// account_tx -Json::Value RPCHandler::doAccountTransactions(const Json::Value& params) +// { account: , ledger: } +// { account: , ledger_min: , ledger_max: } +Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) { - std::string param; - uint32 minLedger, maxLedger; + RippleAddress raAccount; + uint32 minLedger; + uint32 maxLedger; - if (!extractString(param, params, 0)) + if (!jvRequest.isMember("account")) return rpcError(rpcINVALID_PARAMS); - RippleAddress account; - if (!account.setAccountID(param)) + if (!raAccount.setAccountID(jvRequest["account"].asString())) return rpcError(rpcACT_MALFORMED); - if (!extractString(param, params, 1)) - return rpcError(rpcLGR_IDX_MALFORMED); - - minLedger = lexical_cast_s(param); - - if ((params.size() == 3) && extractString(param, params, 2)) - maxLedger = lexical_cast_s(param); + if (jvRequest.isMember("ledger")) + { + minLedger = maxLedger = jvRequest["ledger"].asUInt(); + } + else if (jvRequest.isMember("ledger_min") && jvRequest.isMember("ledger_max")) + { + minLedger = jvRequest["ledger_min"].asUInt(); + maxLedger = jvRequest["ledger_max"].asUInt(); + } else - maxLedger = minLedger; + { + return rpcError(rpcLGR_IDX_MALFORMED); + } if ((maxLedger < minLedger) || (maxLedger == 0)) { - std::cerr << "minL=" << minLedger << ", maxL=" << maxLedger << std::endl; - return rpcError(rpcLGR_IDXS_INVALID); } @@ -1303,9 +1214,9 @@ Json::Value RPCHandler::doAccountTransactions(const Json::Value& params) try { #endif - std::vector< std::pair > txns = mNetOps->getAccountTxs(account, minLedger, maxLedger); + std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger); Json::Value ret(Json::objectValue); - ret["account"] = account.humanAccountID(); + ret["account"] = raAccount.humanAccountID(); Json::Value ledgers(Json::arrayValue); // uint32 currentLedger = 0; @@ -1326,33 +1237,11 @@ Json::Value RPCHandler::doAccountTransactions(const Json::Value& params) #endif } -// unl_add | [] -Json::Value RPCHandler::doUnlAdd(const Json::Value& params) -{ - std::string strNode = params[0u].asString(); - std::string strComment = (params.size() == 2) ? params[1u].asString() : ""; - - RippleAddress naNodePublic; - - if (naNodePublic.setNodePublic(strNode)) - { - theApp->getUNL().nodeAddPublic(naNodePublic, UniqueNodeList::vsManual, strComment); - - return "adding node by public key"; - } - else - { - theApp->getUNL().nodeAddDomain(strNode, UniqueNodeList::vsManual, strComment); - - return "adding node by domain"; - } -} - // validation_create [||] // // NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command // shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). -Json::Value RPCHandler::doValidationCreate(const Json::Value& params) { +Json::Value RPCHandler::doValidationCreate(Json::Value params) { RippleAddress naSeed; Json::Value obj(Json::objectValue); @@ -1378,7 +1267,7 @@ Json::Value RPCHandler::doValidationCreate(const Json::Value& params) { // // NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command // shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). -Json::Value RPCHandler::doValidationSeed(const Json::Value& params) { +Json::Value RPCHandler::doValidationSeed(Json::Value params) { Json::Value obj(Json::objectValue); if (params.empty()) @@ -1438,12 +1327,14 @@ Json::Value RPCHandler::accounts(const uint256& uLedger, const RippleAddress& na return jsonAccounts; } -// wallet_accounts -Json::Value RPCHandler::doWalletAccounts(const Json::Value& params) +// { +// seed: +// } +Json::Value RPCHandler::doWalletAccounts(Json::Value jvParams) { RippleAddress naSeed; - if (!naSeed.setSeedGeneric(params[0u].asString())) + if (!jvParams.isMember("seed") || !naSeed.setSeedGeneric(jvParams["seed"].asString())) { return rpcError(rpcBAD_SEED); } @@ -1476,143 +1367,14 @@ Json::Value RPCHandler::doWalletAccounts(const Json::Value& params) } } -Json::Value RPCHandler::doLogRotate(const Json::Value& params) +Json::Value RPCHandler::doLogRotate(Json::Value) { return Log::rotateLog(); } -Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params, int role) -{ - cLog(lsTRACE) << "RPC:" << command; - cLog(lsTRACE) << "RPC params:" << params; - - LoadEvent::autoptr le(theApp->getJobQueue().getLoadEventAP(jtRPC)); - - mRole = role; - - static struct { - const char* pCommand; - doFuncPtr dfpFunc; - int iMinParams; - int iMaxParams; - bool bAdminRequired; - bool bEvented; - unsigned int iOptions; - } commandsA[] = { - // Request-response methods - { "accept_ledger", &RPCHandler::doAcceptLedger, 0, 0, true, false, optNone }, - { "account_info", &RPCHandler::doAccountInfo, 1, 2, false, false, optCurrent }, - { "account_tx", &RPCHandler::doAccountTransactions, 2, 3, false, false, optNetwork }, - { "connect", &RPCHandler::doConnect, 1, 2, true, false, optNone }, - { "data_delete", &RPCHandler::doDataDelete, 1, 1, true, false, optNone }, - { "data_fetch", &RPCHandler::doDataFetch, 1, 1, true, false, optNone }, - { "data_store", &RPCHandler::doDataStore, 2, 2, true, false, optNone }, - { "get_counts", &RPCHandler::doGetCounts, 0, 1, true, false, optNone }, - { "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 }, - { "ledger_header", &RPCHandler::doLedgerHeader, -1, -1, false, false, optCurrent }, - { "log_level", &RPCHandler::doLogLevel, 0, 2, true, false, optNone }, - { "logrotate", &RPCHandler::doLogRotate, 0, 0, true, false, optNone }, - { "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, false, optCurrent }, - { "owner_info", &RPCHandler::doOwnerInfo, 1, 2, false, false, optCurrent }, - { "peers", &RPCHandler::doPeers, 0, 0, true, false, optNone }, - { "profile", &RPCHandler::doProfile, 1, 9, false, false, optCurrent }, - { "ripple_lines_get", &RPCHandler::doRippleLinesGet, 1, 2, false, false, optCurrent }, - { "ripple_path_find", &RPCHandler::doRipplePathFind, -1, -1, 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, false, optNone }, - { "stop", &RPCHandler::doStop, 0, 0, true, false, optNone }, - { "transaction_entry", &RPCHandler::doTransactionEntry, -1, -1, false, false, optCurrent }, - { "tx", &RPCHandler::doTx, 1, 1, true, false, optNone }, - { "tx_history", &RPCHandler::doTxHistory, 1, 1, false, false, optNone }, - - { "unl_add", &RPCHandler::doUnlAdd, 1, 2, true, false, optNone }, - { "unl_delete", &RPCHandler::doUnlDelete, 1, 1, true, false, optNone }, - { "unl_list", &RPCHandler::doUnlList, 0, 0, true, false, optNone }, - { "unl_load", &RPCHandler::doUnlLoad, 0, 0, true, false, optNone }, - { "unl_network", &RPCHandler::doUnlNetwork, 0, 0, true, false, optNone }, - { "unl_reset", &RPCHandler::doUnlReset, 0, 0, true, false, optNone }, - { "unl_score", &RPCHandler::doUnlScore, 0, 0, true, false, optNone }, - - { "validation_create", &RPCHandler::doValidationCreate, 0, 1, false, false, optNone }, - { "validation_seed", &RPCHandler::doValidationSeed, 0, 1, false, false, optNone }, - - { "wallet_accounts", &RPCHandler::doWalletAccounts, 1, 1, false, false, optCurrent }, - { "wallet_propose", &RPCHandler::doWalletPropose, 0, 1, false, false, optNone }, - { "wallet_seed", &RPCHandler::doWalletSeed, 0, 1, false, false, optNone }, - - { "login", &RPCHandler::doLogin, 2, 2, true, false, optNone }, - - // Evented methods - { "subscribe", &RPCHandler::doSubscribe, -1, -1, false, true, optNone }, - { "unsubscribe", &RPCHandler::doUnsubscribe, -1, -1, false, true, optNone }, - }; - - int i = NUMBER(commandsA); - - while (i-- && command != commandsA[i].pCommand) - ; - - if (i < 0) - { - return rpcError(rpcUNKNOWN_COMMAND); - } - else if (commandsA[i].bAdminRequired && mRole != ADMIN) - { - return rpcError(rpcNO_PERMISSION); - } - else if (commandsA[i].bEvented && mInfoSub == NULL) - { - return rpcError(rpcNO_EVENTS); - } - else if (commandsA[i].iMinParams >= 0 - ? commandsA[i].iMaxParams - ? (params.size() < commandsA[i].iMinParams - || (commandsA[i].iMaxParams >= 0 && params.size() > commandsA[i].iMaxParams)) - : false - : params.isArray()) - { - return rpcError(rpcINVALID_PARAMS); - } - else if ((commandsA[i].iOptions & optNetwork) && !mNetOps->available()) - { - return rpcError(rpcNO_NETWORK); - } - // XXX Should verify we have a current ledger. - - boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock()); - if ((commandsA[i].iOptions & optCurrent) && false) - { - return rpcError(rpcNO_CURRENT); - } - else if ((commandsA[i].iOptions & optClosed) && !mNetOps->getClosedLedger()) - { - return rpcError(rpcNO_CLOSED); - } - else - { - try { - return (this->*(commandsA[i].dfpFunc))(params); - } - catch (std::exception& e) - { - cLog(lsINFO) << "Caught throw: " << e.what(); - - return rpcError(rpcINTERNAL); - } - } -} - - - - // wallet_propose [] // is only for testing. Master seeds should only be generated randomly. -Json::Value RPCHandler::doWalletPropose(const Json::Value& params) +Json::Value RPCHandler::doWalletPropose(Json::Value params) { RippleAddress naSeed; RippleAddress naAccount; @@ -1639,7 +1401,7 @@ Json::Value RPCHandler::doWalletPropose(const Json::Value& params) } // wallet_seed [||] -Json::Value RPCHandler::doWalletSeed(const Json::Value& params) +Json::Value RPCHandler::doWalletSeed(Json::Value params) { RippleAddress naSeed; @@ -1674,7 +1436,7 @@ Json::Value RPCHandler::doWalletSeed(const Json::Value& params) // TODO: for now this simply checks if this is the admin account // TODO: need to prevent them hammering this over and over // TODO: maybe a better way is only allow admin from local host -Json::Value RPCHandler::doLogin(const Json::Value& params) +Json::Value RPCHandler::doLogin(Json::Value params) { std::string username = params[0u].asString(); std::string password = params[1u].asString(); @@ -1690,7 +1452,7 @@ Json::Value RPCHandler::doLogin(const Json::Value& params) } } -Json::Value RPCHandler::doGetCounts(const Json::Value& params) +Json::Value RPCHandler::doGetCounts(Json::Value params) { int minCount = 10; if (params.size() > 0) @@ -1704,7 +1466,7 @@ Json::Value RPCHandler::doGetCounts(const Json::Value& params) return ret; } -Json::Value RPCHandler::doLogLevel(const Json::Value& params) +Json::Value RPCHandler::doLogLevel(Json::Value params) { if (params.size() == 0) { // get log severities @@ -1744,49 +1506,43 @@ Json::Value RPCHandler::doLogLevel(const Json::Value& params) return rpcError(rpcINVALID_PARAMS); } - - -// Populate the UNL from ripple.com's validators.txt file. -Json::Value RPCHandler::doUnlNetwork(const Json::Value& params) +// { +// node: |, +// comment: // optional +// } +Json::Value RPCHandler::doUnlAdd(Json::Value jvParams) { - theApp->getUNL().nodeNetwork(); + std::string strNode = jvParams.isMember("node") ? jvParams["node"].asString() : ""; + std::string strComment = jvParams.isMember("comment") ? jvParams["comment"].asString() : ""; - return "fetching"; -} + RippleAddress raNodePublic; -// unl_reset -Json::Value RPCHandler::doUnlReset(const Json::Value& params) -{ - theApp->getUNL().nodeReset(); - - return "removing nodes"; -} - -// unl_score -Json::Value RPCHandler::doUnlScore(const Json::Value& params) -{ - theApp->getUNL().nodeScore(); - - return "scoring requested"; -} - -Json::Value RPCHandler::doStop(const Json::Value& params) -{ - theApp->stop(); - - return SYSTEM_NAME " server stopping"; -} - -// unl_delete | -Json::Value RPCHandler::doUnlDelete(const Json::Value& params) -{ - std::string strNode = params[0u].asString(); - - RippleAddress naNodePublic; - - if (naNodePublic.setNodePublic(strNode)) + if (raNodePublic.setNodePublic(strNode)) { - theApp->getUNL().nodeRemovePublic(naNodePublic); + theApp->getUNL().nodeAddPublic(raNodePublic, UniqueNodeList::vsManual, strComment); + + return "adding node by public key"; + } + else + { + theApp->getUNL().nodeAddDomain(strNode, UniqueNodeList::vsManual, strComment); + + return "adding node by domain"; + } +} + +// { +// node: | +// } +Json::Value RPCHandler::doUnlDelete(Json::Value jvParams) +{ + std::string strNode = jvParams[0u].asString(); + + RippleAddress raNodePublic; + + if (raNodePublic.setNodePublic(strNode)) + { + theApp->getUNL().nodeRemovePublic(raNodePublic); return "removing node by public key"; } @@ -1798,7 +1554,7 @@ Json::Value RPCHandler::doUnlDelete(const Json::Value& params) } } -Json::Value RPCHandler::doUnlList(const Json::Value& params) +Json::Value RPCHandler::doUnlList(Json::Value params) { Json::Value obj(Json::objectValue); @@ -1808,7 +1564,7 @@ Json::Value RPCHandler::doUnlList(const Json::Value& params) } // Populate the UNL from a local validators.txt file. -Json::Value RPCHandler::doUnlLoad(const Json::Value& params) +Json::Value RPCHandler::doUnlLoad(Json::Value params) { if (theConfig.VALIDATORS_FILE.empty() || !theApp->getUNL().nodeLoad(theConfig.VALIDATORS_FILE)) { @@ -1818,7 +1574,39 @@ Json::Value RPCHandler::doUnlLoad(const Json::Value& params) return "loading"; } -Json::Value RPCHandler::doLedgerAccept(const Json::Value& ) + +// Populate the UNL from ripple.com's validators.txt file. +Json::Value RPCHandler::doUnlNetwork(Json::Value params) +{ + theApp->getUNL().nodeNetwork(); + + return "fetching"; +} + +// unl_reset +Json::Value RPCHandler::doUnlReset(Json::Value params) +{ + theApp->getUNL().nodeReset(); + + return "removing nodes"; +} + +// unl_score +Json::Value RPCHandler::doUnlScore(Json::Value params) +{ + theApp->getUNL().nodeScore(); + + return "scoring requested"; +} + +Json::Value RPCHandler::doStop(Json::Value) +{ + theApp->stop(); + + return SYSTEM_NAME " server stopping"; +} + +Json::Value RPCHandler::doLedgerAccept(Json::Value) { Json::Value jvResult; @@ -1836,7 +1624,7 @@ Json::Value RPCHandler::doLedgerAccept(const Json::Value& ) return jvResult; } -Json::Value RPCHandler::doTransactionEntry(const Json::Value& jvRequest) +Json::Value RPCHandler::doTransactionEntry(Json::Value jvRequest) { Json::Value jvResult; @@ -1886,7 +1674,7 @@ Json::Value RPCHandler::doTransactionEntry(const Json::Value& jvRequest) return jvResult; } -Json::Value RPCHandler::lookupLedger(const Json::Value& jvRequest, Ledger::pointer& lpLedger) +Json::Value RPCHandler::lookupLedger(Json::Value jvRequest, Ledger::pointer& lpLedger) { Json::Value jvResult; @@ -1938,7 +1726,7 @@ Json::Value RPCHandler::lookupLedger(const Json::Value& jvRequest, Ledger::point return jvResult; } -Json::Value RPCHandler::doLedgerEntry(const Json::Value& jvRequest) +Json::Value RPCHandler::doLedgerEntry(Json::Value jvRequest) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -2141,7 +1929,7 @@ Json::Value RPCHandler::doLedgerEntry(const Json::Value& jvRequest) return jvResult; } -Json::Value RPCHandler::doLedgerHeader(const Json::Value& jvRequest) +Json::Value RPCHandler::doLedgerHeader(Json::Value jvRequest) { Ledger::pointer lpLedger; Json::Value jvResult = lookupLedger(jvRequest, lpLedger); @@ -2191,7 +1979,7 @@ rt_transactions accounts rt_accounts */ -Json::Value RPCHandler::doSubscribe(const Json::Value& jvRequest) +Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) { Json::Value jvResult(Json::objectValue); @@ -2265,7 +2053,7 @@ Json::Value RPCHandler::doSubscribe(const Json::Value& jvRequest) return jvResult; } -Json::Value RPCHandler::doUnsubscribe(const Json::Value& jvRequest) +Json::Value RPCHandler::doUnsubscribe(Json::Value jvRequest) { Json::Value jvResult(Json::objectValue); @@ -2339,4 +2127,178 @@ Json::Value RPCHandler::doUnsubscribe(const Json::Value& jvRequest) return jvResult; } +// Provide the JSON-RPC "result" value. +Json::Value RPCHandler::doRpcCommand(const std::string& strCommand, Json::Value& jvParams, int iRole) +{ + // cLog(lsTRACE) << "doRpcCommand:" << strCommand << ":" << jvParams; + + if (!jvParams.isArray() || jvParams.size() > 1) + return rpcError(rpcINVALID_PARAMS); + + Json::Value jvRequest = jvParams[0u]; + + jvRequest["command"] = strCommand; + + Json::Value jvResult = doCommand(jvRequest, iRole); + + // Always report "status". On an error report the request as received. + if (jvResult.isMember("error")) + { + jvResult["status"] = "error"; + jvResult["request"] = jvRequest; + + } else { + jvResult["status"] = "success"; + } + + return jvResult; +} + +Json::Value RPCHandler::doCommand(Json::Value& jvParams, int iRole) +{ + if (!jvParams.isMember("command")) + return rpcError(rpcINVALID_PARAMS); + + std::string strCommand = jvParams["command"].asString(); + + cLog(lsTRACE) << "COMMAND:" << strCommand; + cLog(lsTRACE) << "REQUEST:" << jvParams; + + LoadEvent::autoptr le(theApp->getJobQueue().getLoadEventAP(jtRPC)); + + mRole = iRole; + + static struct { + const char* pCommand; + doFuncPtr dfpFunc; + int iMinParams; + int iMaxParams; + bool bAdminRequired; + bool bEvented; + unsigned int iOptions; + } commandsA[] = { + // Request-response methods + { "accept_ledger", &RPCHandler::doAcceptLedger, -1, -1, true, false, optCurrent }, + { "account_info", &RPCHandler::doAccountInfo, -1, -1, false, false, optCurrent }, + { "account_tx", &RPCHandler::doAccountTransactions, -1, -1, false, false, optNetwork }, + { "connect", &RPCHandler::doConnect, 1, 2, true, false, optNone }, + { "data_delete", &RPCHandler::doDataDelete, 1, 1, true, false, optNone }, + { "data_fetch", &RPCHandler::doDataFetch, 1, 1, true, false, optNone }, + { "data_store", &RPCHandler::doDataStore, 2, 2, true, false, optNone }, + { "get_counts", &RPCHandler::doGetCounts, 0, 1, true, false, optNone }, + { "ledger", &RPCHandler::doLedger, -1, -1, false, false, optNetwork }, + { "ledger_accept", &RPCHandler::doLedgerAccept, -1, -1, true, false, optCurrent }, + { "ledger_closed", &RPCHandler::doLedgerClosed, -1, -1, false, false, optClosed }, + { "ledger_current", &RPCHandler::doLedgerCurrent, -1, -1, false, false, optCurrent }, + { "ledger_entry", &RPCHandler::doLedgerEntry, -1, -1, false, false, optCurrent }, + { "ledger_header", &RPCHandler::doLedgerHeader, -1, -1, false, false, optCurrent }, + { "log_level", &RPCHandler::doLogLevel, 0, 2, true, false, optNone }, + { "logrotate", &RPCHandler::doLogRotate, -1, -1, true, false, optNone }, + { "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, false, optCurrent }, + { "owner_info", &RPCHandler::doOwnerInfo, 1, 2, false, false, optCurrent }, + { "peers", &RPCHandler::doPeers, -1, -1, true, false, optNone }, + { "profile", &RPCHandler::doProfile, 1, 9, false, false, optCurrent }, + { "ripple_lines_get", &RPCHandler::doRippleLinesGet, 1, 2, false, false, optCurrent }, + { "ripple_path_find", &RPCHandler::doRipplePathFind, -1, -1, false, false, optCurrent }, + { "submit", &RPCHandler::doSubmit, -1, -1, false, false, optCurrent }, + { "server_info", &RPCHandler::doServerInfo, -1, -1, true, false, optNone }, + { "stop", &RPCHandler::doStop, -1, -1, true, false, optNone }, + { "transaction_entry", &RPCHandler::doTransactionEntry, -1, -1, false, false, optCurrent }, + { "tx", &RPCHandler::doTx, 1, 1, true, false, optNone }, + { "tx_history", &RPCHandler::doTxHistory, 1, 1, false, false, optNone }, + + { "unl_add", &RPCHandler::doUnlAdd, -1, -1, true, false, optNone }, + { "unl_delete", &RPCHandler::doUnlDelete, -1, -1, true, false, optNone }, + { "unl_list", &RPCHandler::doUnlList, -1, -1, true, false, optNone }, + { "unl_load", &RPCHandler::doUnlLoad, -1, -1, true, false, optNone }, + { "unl_network", &RPCHandler::doUnlNetwork, -1, -1, true, false, optNone }, + { "unl_reset", &RPCHandler::doUnlReset, -1, -1, true, false, optNone }, + { "unl_score", &RPCHandler::doUnlScore, -1, -1, true, false, optNone }, + + { "validation_create", &RPCHandler::doValidationCreate, 0, 1, false, false, optNone }, + { "validation_seed", &RPCHandler::doValidationSeed, 0, 1, false, false, optNone }, + + { "wallet_accounts", &RPCHandler::doWalletAccounts, -1, -1, false, false, optCurrent }, + { "wallet_propose", &RPCHandler::doWalletPropose, 0, 1, false, false, optNone }, + { "wallet_seed", &RPCHandler::doWalletSeed, 0, 1, false, false, optNone }, + + { "login", &RPCHandler::doLogin, 2, 2, true, false, optNone }, + + // Evented methods + { "subscribe", &RPCHandler::doSubscribe, -1, -1, false, true, optNone }, + { "unsubscribe", &RPCHandler::doUnsubscribe, -1, -1, false, true, optNone }, + }; + + int i = NUMBER(commandsA); + + while (i-- && strCommand != commandsA[i].pCommand) + ; + + if (i < 0) + { + return rpcError(rpcUNKNOWN_COMMAND); + } + else if (commandsA[i].bAdminRequired && mRole != ADMIN) + { + return rpcError(rpcNO_PERMISSION); + } + else if (commandsA[i].bEvented && mInfoSub == NULL) + { + return rpcError(rpcNO_EVENTS); + } + else if (commandsA[i].iMinParams >= 0 + ? commandsA[i].iMaxParams + ? (jvParams.size() < commandsA[i].iMinParams + || (commandsA[i].iMaxParams >= 0 && jvParams.size() > commandsA[i].iMaxParams)) + : false + : jvParams.isArray()) + { +cLog(lsDEBUG) << "params.size: " << jvParams.size() << " array: " << jvParams.isArray(); + return rpcError(rpcINVALID_PARAMS); + } + else if ((commandsA[i].iOptions & optNetwork) && !mNetOps->available()) + { + return rpcError(rpcNO_NETWORK); + } + // XXX Should verify we have a current ledger. + + boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock()); + if ((commandsA[i].iOptions & optCurrent) && false) + { + return rpcError(rpcNO_CURRENT); + } + else if ((commandsA[i].iOptions & optClosed) && !mNetOps->getClosedLedger()) + { + return rpcError(rpcNO_CLOSED); + } + else + { + try { + Json::Value jvRaw = (this->*(commandsA[i].dfpFunc))(jvParams); + + // Regularize result. + if (jvRaw.isObject()) + { + // Got an object. + return jvRaw; + } + else + { + // Probably got a string. + Json::Value jvResult(Json::objectValue); + + jvResult["message"] = jvRaw; + + return jvResult; + } + } + catch (std::exception& e) + { + cLog(lsINFO) << "Caught throw: " << e.what(); + + return rpcError(rpcINTERNAL); + } + } +} + // vim:ts=4 diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 2ac0e3f96..080defe65 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -1,5 +1,5 @@ -#ifndef RPCHANDLER__H -#define RPCHANDLER__H +#ifndef __RPCHANDLER__ +#define __RPCHANDLER__ // used by the RPCServer or WSDoor to carry out these RPC commands class NetworkOPs; @@ -10,7 +10,7 @@ class RPCHandler InfoSub* mInfoSub; int mRole; - typedef Json::Value (RPCHandler::*doFuncPtr)(const Json::Value& params); + typedef Json::Value (RPCHandler::*doFuncPtr)(Json::Value params); enum { optNone = 0, optNetwork = 1, // Need network @@ -21,10 +21,10 @@ class RPCHandler // Utilities void addSubmitPath(Json::Value& txJSON); boost::unordered_set parseAccountIds(const Json::Value& jvArray); - int getParamCount(const Json::Value& params); + int getParamCount(Json::Value params); bool extractString(std::string& param, const Json::Value& params, int index); - Json::Value lookupLedger(const Json::Value& jvRequest, Ledger::pointer& lpLedger); + Json::Value lookupLedger(Json::Value jvRequest, Ledger::pointer& lpLedger); Json::Value getMasterGenerator(const uint256& uLedger, const RippleAddress& naRegularSeed, RippleAddress& naMasterGenerator); Json::Value authorize(const uint256& uLedger, const RippleAddress& naRegularSeed, const RippleAddress& naSrcAccountID, @@ -35,142 +35,78 @@ class RPCHandler Json::Value accountFromString(const uint256& uLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex); - Json::Value doAcceptLedger(const Json::Value& params); + Json::Value doAcceptLedger(Json::Value jvRequest); - Json::Value doAccountInfo(const Json::Value& params); - Json::Value doAccountTransactions(const Json::Value& params); - Json::Value doConnect(const Json::Value& params); - Json::Value doDataDelete(const Json::Value& params); - Json::Value doDataFetch(const Json::Value& params); - Json::Value doDataStore(const Json::Value& params); - Json::Value doGetCounts(const Json::Value& params); - Json::Value doLedger(const Json::Value& params); - Json::Value doLogRotate(const Json::Value& params); - Json::Value doNicknameInfo(const Json::Value& params); + Json::Value doAccountInfo(Json::Value params); + Json::Value doAccountTransactions(Json::Value params); + Json::Value doConnect(Json::Value params); + Json::Value doDataDelete(Json::Value params); + Json::Value doDataFetch(Json::Value params); + Json::Value doDataStore(Json::Value params); + Json::Value doGetCounts(Json::Value params); + Json::Value doLedger(Json::Value params); + Json::Value doLogRotate(Json::Value params); + Json::Value doNicknameInfo(Json::Value params); - Json::Value doOwnerInfo(const Json::Value& params); + Json::Value doOwnerInfo(Json::Value params); - Json::Value doProfile(const Json::Value& params); - Json::Value doPeers(const Json::Value& params); + Json::Value doProfile(Json::Value params); + Json::Value doPeers(Json::Value params); - Json::Value doRippleLinesGet(const Json::Value& params); - Json::Value doRipplePathFind(const Json::Value& jvRequest); - Json::Value doServerInfo(const Json::Value& params); - Json::Value doSessionClose(const Json::Value& params); - Json::Value doSessionOpen(const Json::Value& params); - Json::Value doLogLevel(const Json::Value& params); - Json::Value doStop(const Json::Value& params); - Json::Value doSubmit(const Json::Value& params); - Json::Value doSubmitJson(const Json::Value& jvRequest); - Json::Value doTx(const Json::Value& params); - Json::Value doTxHistory(const Json::Value& params); + Json::Value doRippleLinesGet(Json::Value params); + Json::Value doRipplePathFind(Json::Value jvRequest); + Json::Value doServerInfo(Json::Value params); + Json::Value doSessionClose(Json::Value params); + Json::Value doSessionOpen(Json::Value params); + Json::Value doLogLevel(Json::Value params); + Json::Value doStop(Json::Value params); + Json::Value doSubmit(Json::Value params); + Json::Value doTx(Json::Value params); + Json::Value doTxHistory(Json::Value params); - Json::Value doUnlAdd(const Json::Value& params); - Json::Value doUnlDelete(const Json::Value& params); - Json::Value doUnlFetch(const Json::Value& params); - Json::Value doUnlList(const Json::Value& params); - Json::Value doUnlLoad(const Json::Value& params); - Json::Value doUnlNetwork(const Json::Value& params); - Json::Value doUnlReset(const Json::Value& params); - Json::Value doUnlScore(const Json::Value& params); + Json::Value doUnlAdd(Json::Value params); + Json::Value doUnlDelete(Json::Value params); + Json::Value doUnlFetch(Json::Value params); + Json::Value doUnlList(Json::Value params); + Json::Value doUnlLoad(Json::Value params); + Json::Value doUnlNetwork(Json::Value params); + Json::Value doUnlReset(Json::Value params); + Json::Value doUnlScore(Json::Value params); - Json::Value doValidationCreate(const Json::Value& params); - Json::Value doValidationSeed(const Json::Value& params); + Json::Value doValidationCreate(Json::Value params); + Json::Value doValidationSeed(Json::Value params); - Json::Value doWalletAccounts(const Json::Value& params); - Json::Value doWalletLock(const Json::Value& params); - Json::Value doWalletPropose(const Json::Value& params); - Json::Value doWalletSeed(const Json::Value& params); - Json::Value doWalletUnlock(const Json::Value& params); - Json::Value doWalletVerify(const Json::Value& params); + Json::Value doWalletAccounts(Json::Value params); + Json::Value doWalletLock(Json::Value params); + Json::Value doWalletPropose(Json::Value params); + Json::Value doWalletSeed(Json::Value params); + Json::Value doWalletUnlock(Json::Value params); + Json::Value doWalletVerify(Json::Value params); - Json::Value doLogin(const Json::Value& params); + Json::Value doLogin(Json::Value params); - Json::Value doLedgerAccept(const Json::Value& params); - Json::Value doLedgerClosed(const Json::Value& params); - Json::Value doLedgerCurrent(const Json::Value& params); - Json::Value doLedgerEntry(const Json::Value& params); - Json::Value doLedgerHeader(const Json::Value& params); - Json::Value doTransactionEntry(const Json::Value& params); + Json::Value doLedgerAccept(Json::Value params); + Json::Value doLedgerClosed(Json::Value params); + Json::Value doLedgerCurrent(Json::Value params); + Json::Value doLedgerEntry(Json::Value params); + Json::Value doLedgerHeader(Json::Value params); + Json::Value doTransactionEntry(Json::Value params); - Json::Value doSubscribe(const Json::Value& params); - Json::Value doUnsubscribe(const Json::Value& params); + Json::Value doSubscribe(Json::Value params); + Json::Value doUnsubscribe(Json::Value params); public: - enum { - rpcSUCCESS, - - // Misc failure - rpcLOAD_FAILED, - rpcNO_PERMISSION, - rpcNO_EVENTS, - rpcNOT_STANDALONE, - - // Networking - rpcNO_CLOSED, - rpcNO_CURRENT, - rpcNO_NETWORK, - - // Ledger state - rpcACT_EXISTS, - rpcACT_NOT_FOUND, - rpcINSUF_FUNDS, - rpcLGR_NOT_FOUND, - rpcNICKNAME_MISSING, - rpcNO_ACCOUNT, - rpcNO_PATH, - rpcPASSWD_CHANGED, - rpcSRC_MISSING, - rpcSRC_UNCLAIMED, - rpcTXN_NOT_FOUND, - rpcWRONG_SEED, - - // Malformed command - rpcINVALID_PARAMS, - rpcUNKNOWN_COMMAND, - - // Bad parameter - rpcACT_MALFORMED, - rpcQUALITY_MALFORMED, - rpcBAD_SEED, - rpcDST_ACT_MALFORMED, - rpcDST_ACT_MISSING, - rpcDST_AMT_MALFORMED, - rpcGETS_ACT_MALFORMED, - rpcGETS_AMT_MALFORMED, - rpcHOST_IP_MALFORMED, - rpcLGR_IDXS_INVALID, - rpcLGR_IDX_MALFORMED, - rpcNICKNAME_MALFORMED, - rpcNICKNAME_PERM, - rpcPAYS_ACT_MALFORMED, - rpcPAYS_AMT_MALFORMED, - rpcPORT_MALFORMED, - rpcPUBLIC_MALFORMED, - rpcSRC_ACT_MALFORMED, - rpcSRC_ACT_MISSING, - rpcSRC_AMT_MALFORMED, - - // Internal error (should never happen) - rpcINTERNAL, // Generic internal error. - rpcFAIL_GEN_DECRPYT, - rpcNOT_IMPL, - rpcNO_GEN_DECRPYT, - }; 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 rpcError(int iError); - - Json::Value handleJSONSubmit(const Json::Value& jvRequest); - + Json::Value doCommand(Json::Value& jvRequest, int role); + Json::Value doRpcCommand(const std::string& strCommand, Json::Value& jvParams, int iRole); }; #endif diff --git a/src/cpp/ripple/RPCServer.cpp b/src/cpp/ripple/RPCServer.cpp index 3b87189a0..98e7e0399 100644 --- a/src/cpp/ripple/RPCServer.cpp +++ b/src/cpp/ripple/RPCServer.cpp @@ -36,7 +36,7 @@ RPCServer::RPCServer(boost::asio::io_service& io_service , NetworkOPs* nopNetwor void RPCServer::connected() { - //std::cout << "RPC request" << std::endl; + //std::cerr << "RPC request" << std::endl; if (mSocket.remote_endpoint().address().to_string()=="127.0.0.1") mRole = RPCHandler::ADMIN; else mRole = RPCHandler::GUEST; @@ -145,7 +145,7 @@ std::string RPCServer::handleRequest(const std::string& requestStr) RPCHandler mRPCHandler(mNetOps); cLog(lsTRACE) << valParams; - Json::Value result = mRPCHandler.doCommand(strMethod, valParams,mRole); + Json::Value result = mRPCHandler.doRpcCommand(strMethod, valParams, mRole); cLog(lsTRACE) << result; std::string strReply = JSONRPCReply(result, Json::Value(), id); @@ -167,7 +167,7 @@ bool RPCServer::parseAcceptRate(const std::string& sAcceptRate) void RPCServer::handle_write(const boost::system::error_code& e) { - //std::cout << "async_write complete " << e << std::endl; + //std::cerr << "async_write complete " << e << std::endl; if (!e) { diff --git a/src/cpp/ripple/RippleLines.cpp b/src/cpp/ripple/RippleLines.cpp index c15d12b97..8d0a79af5 100644 --- a/src/cpp/ripple/RippleLines.cpp +++ b/src/cpp/ripple/RippleLines.cpp @@ -12,12 +12,12 @@ RippleLines::RippleLines(const uint160& accountID, Ledger::ref ledger) fillLines(accountID, ledger); } -void RippleLines::printRippleLines() +void RippleLines::printRippleLines() { for (unsigned int i =0; i < mLines.size(); i++) { - std::cout << i << ": " << mLines[i]->getAccountID().humanAccountID() << std::endl; + std::cerr << i << ": " << mLines[i]->getAccountID().humanAccountID() << std::endl; } - std::cout << std::endl; + std::cerr << std::endl; } RippleLines::RippleLines(const uint160& accountID ) diff --git a/src/cpp/ripple/SerializedTypes.cpp b/src/cpp/ripple/SerializedTypes.cpp index d56dd43d6..11ed9794b 100644 --- a/src/cpp/ripple/SerializedTypes.cpp +++ b/src/cpp/ripple/SerializedTypes.cpp @@ -30,25 +30,25 @@ SerializedType& SerializedType::operator=(const SerializedType& t) void STPathSet::printDebug() { for (int i = 0; i < value.size(); i++) { - std::cout << i << ": "; + std::cerr << i << ": "; for (int j = 0; j < value[i].mPath.size(); j++) { //STPathElement pe = value[i].mPath[j]; RippleAddress nad; nad.setAccountID(value[i].mPath[j].mAccountID); - std::cout << " " << nad.humanAccountID(); - //std::cout << " " << pe.mAccountID.GetHex(); + std::cerr << " " << nad.humanAccountID(); + //std::cerr << " " << pe.mAccountID.GetHex(); } - std::cout << std::endl; + std::cerr << std::endl; } } void STPath::printDebug() { - std::cout << "STPath:" << std::endl; + std::cerr << "STPath:" << std::endl; for(int i =0; i < mPath.size(); i++) { RippleAddress nad; nad.setAccountID(mPath[i].mAccountID); - std::cout << " " << i << ": " << nad.humanAccountID() << std::endl; + std::cerr << " " << i << ": " << nad.humanAccountID() << std::endl; } } diff --git a/src/cpp/ripple/WSConnection.cpp b/src/cpp/ripple/WSConnection.cpp index 875b18942..f217f07da 100644 --- a/src/cpp/ripple/WSConnection.cpp +++ b/src/cpp/ripple/WSConnection.cpp @@ -6,6 +6,7 @@ SETUP_LOG(); +#include "CallRPC.h" // XXX Remove this, don't provide support for RPC syntax. #include "WSConnection.h" #include "WSHandler.h" @@ -48,24 +49,42 @@ Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) RPCHandler mRPCHandler(&mNetwork, this); Json::Value jvResult(Json::objectValue); - // Regular RPC command + // XXX Temporarily support RPC style commands over websocket. Remove this. + if (jvRequest.isMember("params")) + { + RPCParser rpParser; + + Json::Value jvRpcRequest = rpParser.parseCommand(jvRequest["command"].asString(), jvRequest["params"]); + + if (jvRpcRequest.isMember("error")) + { + jvResult = jvRpcRequest; + } + else + { + jvResult["result"] = mRPCHandler.doCommand( + jvRpcRequest, + mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN); + } + } + else { jvResult["result"] = mRPCHandler.doCommand( - jvRequest["command"].asString(), - jvRequest.isMember("params") - ? jvRequest["params"] - : jvRequest, + 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")) + // + // Regularize result. This is duplicate code. + if (jvResult["result"].isMember("error")) { - jvResult = jvResult["result"]; + jvResult = jvResult["result"]; jvResult["status"] = "error"; jvResult["request"] = jvRequest; + } else { jvResult["status"] = "success"; } diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index 360c07857..7089b2032 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -33,51 +33,51 @@ bool init_unit_test() void printHelp(const po::options_description& desc) { - cout << SYSTEM_NAME "d [options] " << endl; + cerr << SYSTEM_NAME "d [options] " << endl; - cout << desc << endl; + cerr << desc << endl; - cout << "Commands: " << endl; - cout << " account_domain_set []" << endl; - cout << " account_email_set []" << endl; - cout << " account_info |" << endl; - cout << " account_info || []" << endl; - cout << " account_message_set " << endl; - cout << " account_publish_set " << endl; - cout << " account_rate_set " << endl; - cout << " account_wallet_set []" << endl; - cout << " connect []" << endl; - cout << " data_delete " << endl; - cout << " data_fetch " << endl; - cout << " data_store " << endl; - cout << " ledger [|current|lastclosed] [full]" << endl; - cout << " logrotate " << endl; - cout << " nickname_info " << endl; - cout << " nickname_set [] []" << endl; - cout << " offer_create [passive]" << endl; - cout << " offer_cancel " << endl; - cout << " password_fund []" << endl; - cout << " password_set []" << endl; - cout << " peers" << endl; - cout << " ripple ..." << endl; - cout << " ripple_lines_get || []" << endl; - cout << " ripple_line_set [] []" << endl; - cout << " send [] [] []" << endl; - cout << " stop" << endl; - cout << " tx " << endl; - cout << " unl_add | []" << endl; - cout << " unl_delete |" << endl; - cout << " unl_list" << endl; - cout << " unl_load" << endl; - cout << " unl_network" << endl; - cout << " unl_reset" << endl; - cout << " validation_create [||]" << endl; - cout << " validation_seed [||]" << endl; - cout << " wallet_add [] []" << endl; - cout << " wallet_accounts " << endl; - cout << " wallet_claim [] []" << endl; - cout << " wallet_seed [||]" << endl; - cout << " wallet_propose []" << endl; + cerr << "Commands: " << endl; + cerr << " account_domain_set []" << endl; + cerr << " account_email_set []" << endl; + cerr << " account_info |" << endl; + cerr << " account_info || []" << endl; + cerr << " account_message_set " << endl; + cerr << " account_publish_set " << endl; + cerr << " account_rate_set " << endl; + cerr << " account_wallet_set []" << endl; + cerr << " connect []" << endl; + cerr << " data_delete " << endl; + cerr << " data_fetch " << endl; + cerr << " data_store " << endl; + cerr << " ledger [|current|lastclosed] [full]" << endl; + cerr << " logrotate " << endl; + cerr << " nickname_info " << endl; + cerr << " nickname_set [] []" << endl; + cerr << " offer_create [passive]" << endl; + cerr << " offer_cancel " << endl; + cerr << " password_fund []" << endl; + cerr << " password_set []" << endl; + cerr << " peers" << endl; + cerr << " ripple ..." << endl; + cerr << " ripple_lines_get || []" << endl; + cerr << " ripple_line_set [] []" << endl; + cerr << " send [] [] []" << endl; + cerr << " stop" << endl; + cerr << " tx " << endl; + cerr << " unl_add | []" << endl; + cerr << " unl_delete |" << endl; + cerr << " unl_list" << endl; + cerr << " unl_load" << endl; + cerr << " unl_network" << endl; + cerr << " unl_reset" << endl; + cerr << " validation_create [||]" << endl; + cerr << " validation_seed [||]" << endl; + cerr << " wallet_add [] []" << endl; + cerr << " wallet_accounts " << endl; + cerr << " wallet_claim [] []" << endl; + cerr << " wallet_seed [||]" << endl; + cerr << " wallet_propose []" << endl; } int main(int argc, char* argv[]) @@ -96,6 +96,7 @@ int main(int argc, char* argv[]) ("standalone,a", "Run with no peers.") ("test,t", "Perform unit tests.") ("parameters", po::value< vector >(), "Specify comma separated parameters.") + ("quiet,q", "Reduce diagnotics.") ("verbose,v", "Increase log level.") ("load", "Load the current ledger from the local DB.") ("start", "Start from a fresh Ledger.") @@ -138,7 +139,6 @@ int main(int argc, char* argv[]) } } - if (vm.count("verbose")) Log::setMinSeverity(lsTRACE, true); else @@ -152,7 +152,9 @@ int main(int argc, char* argv[]) if (!iResult) { - theConfig.setup(vm.count("conf") ? vm["conf"].as() : ""); + theConfig.setup( + vm.count("conf") ? vm["conf"].as() : "", // Config file. + !!vm.count("quiet")); // Quiet flag. if (vm.count("standalone")) { @@ -185,7 +187,7 @@ int main(int argc, char* argv[]) iResult = commandLineRPC(vCmd); } - if (1 == iResult) + if (1 == iResult && !vm.count("quiet")) printHelp(desc); return iResult; diff --git a/src/js/remote.js b/src/js/remote.js index bc7b51d3b..fe07934ca 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -548,13 +548,18 @@ Remote.prototype.request_server_info = function () { return new Request(this, 'server_info'); }; -Remote.prototype.request_ledger = function (params) { - // XXX Does this require the server to be trusted? +// XXX This is a bad command. Some varients don't scale. +// XXX Require the server to be trusted. +Remote.prototype.request_ledger = function (ledger, full) { //assert(this.trusted); var request = new Request(this, 'ledger'); - request.message.params = params; + if (ledger) + request.message.ledger = ledger; + + if (full) + request.message.full = true; return request; }; @@ -693,38 +698,45 @@ Remote.prototype.request_transaction_entry = function (hash) { .tx_hash(hash); }; -Remote.prototype.request_ripple_lines_get = function (accountID) { +Remote.prototype.request_ripple_lines_get = function (accountID, index) { // XXX Does this require the server to be trusted? //assert(this.trusted); var request = new Request(this, 'ripple_lines_get'); - // XXX Convert API call to JSON - request.message.params = [accountID]; + request.message.account = accountID; + + if (index) + request.message.index = index; return request; }; -Remote.prototype.request_wallet_accounts = function (key) { - // XXX Does this require the server to be trusted? - //assert(this.trusted); +Remote.prototype.request_wallet_accounts = function (seed) { + assert(this.trusted); // Don't send secrets. var request = new Request(this, 'wallet_accounts'); - // XXX Convert API call to JSON - request.message.params = [key]; + request.message.seed = seed; return request; }; -Remote.prototype.request_account_tx = function (accountID, minLedger, maxLedger) { +Remote.prototype.request_account_tx = function (accountID, ledger_min, ledger_max) { // XXX Does this require the server to be trusted? //assert(this.trusted); var request = new Request(this, 'account_tx'); - // XXX Convert API call to JSON - request.message.params = [accountID, minLedger, maxLedger]; + request.message.account = accountID; + + if (ledger_min === ledger_max) { + request.message.ledger = ledger_min; + } + else { + request.message.ledger_min = ledger_min; + request.message.ledger_max = ledger_max; + } return request; }; @@ -773,7 +785,7 @@ Remote.prototype.submit = function (transaction) { else { // Convert the transaction into a request and submit it. - (new Request(this, 'submit_json')) + (new Request(this, 'submit')) .build_path(transaction._build_path) .tx_json(transaction.tx_json) .secret(transaction._secret) @@ -996,18 +1008,22 @@ Remote.prototype.request_unl_list = function () { return new Request(this, 'unl_list'); }; -Remote.prototype.request_unl_add = function (addr, note) { +Remote.prototype.request_unl_add = function (addr, comment) { var request = new Request(this, 'unl_add'); - request.message.params = [addr, note]; + request.message.node = addr; + + if (comment !== undefined) + request.message.comment = note; return request; }; -Remote.prototype.request_unl_delete = function (publicKey) { +// --> node: | +Remote.prototype.request_unl_delete = function (node) { var request = new Request(this, 'unl_delete'); - request.message.params = [publicKey]; + request.message.node = node; return request; }; @@ -1019,7 +1035,10 @@ Remote.prototype.request_peers = function () { Remote.prototype.request_connect = function (ip, port) { var request = new Request(this, 'connect'); - request.message.params = [ip, port]; + request.message.ip = ip; + + if (port) + request.message.port = port; return request; };