From c0e728c310ebe60cd3534ddf720e1f1a61cdf357 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Wed, 16 Jan 2013 19:09:26 +0100 Subject: [PATCH 1/8] Amount.product_human and Amount.ratio_human need to canonicalize. --- src/js/amount.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/js/amount.js b/src/js/amount.js index 0191ee6b1..0cef359a5 100644 --- a/src/js/amount.js +++ b/src/js/amount.js @@ -778,6 +778,7 @@ Amount.prototype.ratio_human = function (denominator) { if (denominator._is_native) { numerator = numerator.clone(); numerator._value = numerator._value.multiply(consts.bi_xns_unit); + numerator.canonicalize(); } return numerator.divide(denominator); @@ -812,6 +813,7 @@ Amount.prototype.product_human = function (factor) { // See also Amount#ratio_human. if (factor._is_native) { product._value = product._value.divide(consts.bi_xns_unit); + product.canonicalize(); } return product; From 503e9a7ddcab190426ea7a2da5f692376bdfe21d Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 16 Jan 2013 15:03:36 -0800 Subject: [PATCH 2/8] More specific RPC error reporting. --- src/cpp/ripple/RPCErr.cpp | 1 + src/cpp/ripple/RPCErr.h | 1 + src/cpp/ripple/RPCHandler.cpp | 4 ++-- src/cpp/ripple/RPCHandler.h | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index 9ab952080..5d7e3e163 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -21,6 +21,7 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." }, { rpcBAD_SEED, "badSeed", "Disallowed seed." }, { rpcBAD_SYNTAX, "badSyntax", "Syntax error." }, + { rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." }, { 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." }, diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index fa62c0e62..0057c6af2 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -45,6 +45,7 @@ enum { rpcQUALITY_MALFORMED, rpcBAD_BLOB, rpcBAD_SEED, + rpcCOMMAND_MISSING, rpcDST_ACT_MALFORMED, rpcDST_ACT_MISSING, rpcDST_AMT_MALFORMED, diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 2948b52e2..9f4efee10 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2553,10 +2553,10 @@ Json::Value RPCHandler::doInternal(Json::Value jvRequest) return RPCInternalHandler::runHandler(jvRequest["internal_command"].asString(), jvRequest["params"]); } -Json::Value RPCHandler::doCommand(Json::Value& jvRequest, int iRole) +Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) { if (!jvRequest.isMember("command")) - return rpcError(rpcINVALID_PARAMS); + return rpcError(rpcCOMMAND_MISSING); std::string strCommand = jvRequest["command"].asString(); diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 5c3365e01..3c79a7363 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -115,7 +115,7 @@ public: RPCHandler(NetworkOPs* netOps); RPCHandler(NetworkOPs* netOps, InfoSub* infoSub); - Json::Value doCommand(Json::Value& jvRequest, int role); + Json::Value doCommand(const Json::Value& jvRequest, int role); Json::Value doRpcCommand(const std::string& strCommand, Json::Value& jvParams, int iRole); }; From 4982ffdf74478c5a3329e37e1f9c4cf7c805cd1a Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 16 Jan 2013 15:05:44 -0800 Subject: [PATCH 3/8] Add support for calling RPC command at startup from config file. --- src/cpp/ripple/Application.cpp | 1 + src/cpp/ripple/CallRPC.cpp | 8 ++++++-- src/cpp/ripple/Config.cpp | 29 +++++++++++++++++++++++------ src/cpp/ripple/Config.h | 7 +++++-- src/cpp/ripple/main.cpp | 18 +++++++++++++++++- 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 5be04fc9b..ec034eedf 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -18,6 +18,7 @@ #include SETUP_LOG(); + LogPartition TaggedCachePartition("TaggedCache"); Application* theApp = NULL; diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 424f64a73..ae963cd7d 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,7 @@ #include "../json/value.h" #include "../json/reader.h" +#include "Application.h" #include "RPC.h" #include "Log.h" #include "RPCErr.h" @@ -54,8 +56,10 @@ std::string EncodeBase64(const std::string& s) Json::Value RPCParser::parseAsIs(const Json::Value& jvParams) { Json::Value v(Json::objectValue); + if (jvParams.isArray() && (jvParams.size() > 0)) v["params"] = jvParams; + return v; } @@ -656,7 +660,7 @@ int commandLineRPC(const std::vector& vCmd) return nRet; } -Json::Value callRPC(const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, const std::string& strPath, const std::string& strMethod, const Json::Value& params) +Json::Value callRPC(const std::string& strIp, const int iPort, const std::string& strUsername, const std::string& strPassword, const std::string& strPath, const std::string& strMethod, const Json::Value& jvParams) { // Connect to localhost if (!theConfig.QUIET) @@ -684,7 +688,7 @@ Json::Value callRPC(const std::string& strIp, const int iPort, const std::string // Log(lsDEBUG) << "requesting" << std::endl; // Send request - std::string strRequest = JSONRPCRequest(strMethod, params, Json::Value(1)); + std::string strRequest = JSONRPCRequest(strMethod, jvParams, Json::Value(1)); // cLog(lsDEBUG) << "send request " << strMethod << " : " << strRequest << std::endl; std::string strPost = createHTTPPost(strPath, strRequest, mapRequestHeaders); diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp index 129f25965..a886e27fa 100644 --- a/src/cpp/ripple/Config.cpp +++ b/src/cpp/ripple/Config.cpp @@ -1,17 +1,18 @@ // // TODO: Check permissions on config file before using it. // +#include +#include +#include +#include +#include +#include + #include "Config.h" #include "utils.h" #include "HashPrefixes.h" -#include -#include -#include -#include -#include - #define SECTION_ACCOUNT_PROBE_MAX "account_probe_max" #define SECTION_CLUSTER_NODES "cluster_nodes" #define SECTION_DATABASE_PATH "database_path" @@ -36,6 +37,7 @@ #define SECTION_RPC_ALLOW_REMOTE "rpc_allow_remote" #define SECTION_RPC_IP "rpc_ip" #define SECTION_RPC_PORT "rpc_port" +#define SECTION_RPC_STARTUP "rpc_startup" #define SECTION_SNTP "sntp_servers" #define SECTION_VALIDATORS_FILE "validators_file" #define SECTION_VALIDATION_QUORUM "validation_quorum" @@ -268,6 +270,21 @@ void Config::load() SNTP_SERVERS = *smtTmp; } + smtTmp = sectionEntries(secConfig, SECTION_RPC_STARTUP); + if (smtTmp) + { + BOOST_FOREACH(const std::string& strJson, *smtTmp) + { + Json::Reader jrReader; + Json::Value jvCommand; + + if (!jrReader.parse(strJson, jvCommand)) + throw std::runtime_error(boost::str(boost::format("Couldn't parse ["SECTION_RPC_STARTUP"] command: %s") % strJson)); + + RPC_STARTUP.push_back(jvCommand); + } + } + if (sectionSingleB(secConfig, SECTION_DATABASE_PATH, DATABASE_PATH)) DATA_DIR = DATABASE_PATH; diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h index 92f83c8db..b24df2ed1 100644 --- a/src/cpp/ripple/Config.h +++ b/src/cpp/ripple/Config.h @@ -1,13 +1,15 @@ #ifndef __CONFIG__ #define __CONFIG__ +#include +#include + #include "types.h" #include "RippleAddress.h" #include "ParseSection.h" #include "SerializedTypes.h" -#include -#include +#include "../json/value.h" #define ENABLE_INSECURE 0 // 1, to enable unnecessary features. @@ -110,6 +112,7 @@ public: std::string RPC_USER; std::string RPC_PASSWORD; bool RPC_ALLOW_REMOTE; + std::vector RPC_STARTUP; // Validation RippleAddress VALIDATION_SEED, VALIDATION_PUB, VALIDATION_PRIV; diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index d9e828145..e0646428f 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -9,8 +9,9 @@ #include "Application.h" #include "CallRPC.h" #include "Config.h" -#include "utils.h" #include "Log.h" +#include "RPCHandler.h" +#include "utils.h" namespace po = boost::program_options; @@ -26,6 +27,21 @@ void setupServer() void startServer() { + // + // Execute start up rpc commands. + // + BOOST_FOREACH(const Json::Value& jvCommand, theConfig.RPC_STARTUP) + { + if (!theConfig.QUIET) + cerr << "Startup RPC: " << jvCommand << endl; + + RPCHandler rhHandler(&theApp->getOPs()); + + std::cerr << "Result: " + << rhHandler.doCommand(jvCommand, RPCHandler::ADMIN) + << std::endl; + } + theApp->run(); // Blocks till we get a stop RPC. } From 8069ac8ab77986c15fd3d225ae98003852fe17f6 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 16 Jan 2013 15:13:11 -0800 Subject: [PATCH 4/8] Document [rpc_startup] in rippled-example.cfg. --- rippled-example.cfg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rippled-example.cfg b/rippled-example.cfg index 94164faab..54191e8b5 100644 --- a/rippled-example.cfg +++ b/rippled-example.cfg @@ -172,6 +172,11 @@ # [database_path]: # Full path of database directory. # +# [rpc_startup]: +# Specify a list of RPC commands to run at startup. +# +# Example: { "command" : "server_info" } +# # Allow other peers to connect to this server. [peer_ip] From 97716977c93a53688f9be972649c1a95d7cd7f11 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 16 Jan 2013 15:22:29 -0800 Subject: [PATCH 5/8] Report an error if RPC subscribe specifies no streams. --- src/cpp/ripple/RPCHandler.cpp | 53 ++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 9f4efee10..bbab25921 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2347,40 +2347,47 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) if (jvRequest.isMember("streams")) { - for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) + if (jvRequest["streams"].empty()) { - if ((*it).isString()) + jvResult["error"] = "noStreams"; + } + else + { + for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) { - std::string streamName=(*it).asString(); - - if (streamName=="server") + if ((*it).isString()) { - mNetOps->subServer(ispSub, jvResult); + std::string streamName=(*it).asString(); - } - else if (streamName=="ledger") - { - mNetOps->subLedger(ispSub, jvResult); + if (streamName=="server") + { + mNetOps->subServer(ispSub, jvResult); - } - else if (streamName=="transactions") - { - mNetOps->subTransactions(ispSub); + } + else if (streamName=="ledger") + { + mNetOps->subLedger(ispSub, jvResult); - } - else if (streamName=="rt_transactions") - { - mNetOps->subRTTransactions(ispSub); + } + else if (streamName=="transactions") + { + mNetOps->subTransactions(ispSub); + + } + else if (streamName=="rt_transactions") + { + mNetOps->subRTTransactions(ispSub); + } + else + { + jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); + } } else { - jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); + jvResult["error"] = "malformedStream"; } } - else - { - jvResult["error"] = "malformedSteam"; - } } } From 29ead4e3e0b6be1c258d635b784bdafc90061279 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 16 Jan 2013 19:30:21 -0800 Subject: [PATCH 6/8] UT: Add ledger_close to testutils. --- test/testutils.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/testutils.js b/test/testutils.js index 399a6f513..c0775c1e4 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -213,6 +213,10 @@ var credit_limits = function (remote, balances, callback) { }); }; +var ledger_close = function (remote, callback) { + remote.once('ledger_closed', function (m) { callback(); }).ledger_accept(); +} + var payment = function (remote, src, dst, amount, callback) { assert(5 === arguments.length); @@ -407,14 +411,14 @@ var verify_owner_counts = function (remote, counts, callback) { }; exports.account_dump = account_dump; - exports.build_setup = build_setup; +exports.build_teardown = build_teardown; exports.create_accounts = create_accounts; exports.credit_limit = credit_limit; exports.credit_limits = credit_limits; +exports.ledger_close = ledger_close; exports.payment = payment; exports.payments = payments; -exports.build_teardown = build_teardown; exports.transfer_rate = transfer_rate; exports.verify_balance = verify_balance; exports.verify_balances = verify_balances; From 2775d76df3b7935731d25fa6891e6d848ab6b816 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 16 Jan 2013 19:48:22 -0800 Subject: [PATCH 7/8] Limit length of Domain and MessageKey fields. --- src/cpp/ripple/AccountSetTransactor.cpp | 28 ++++++++++++++++++------- src/cpp/ripple/Config.h | 3 +++ src/cpp/ripple/TransactionErr.cpp | 2 ++ src/cpp/ripple/TransactionErr.h | 2 ++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/cpp/ripple/AccountSetTransactor.cpp b/src/cpp/ripple/AccountSetTransactor.cpp index 88c48b246..7834dd1bd 100644 --- a/src/cpp/ripple/AccountSetTransactor.cpp +++ b/src/cpp/ripple/AccountSetTransactor.cpp @@ -1,4 +1,5 @@ #include "AccountSetTransactor.h" +#include "Config.h" SETUP_LOG(); @@ -94,15 +95,22 @@ TER AccountSetTransactor::doApply() // MessageKey // - if (!mTxn.isFieldPresent(sfMessageKey)) + if (mTxn.isFieldPresent(sfMessageKey)) { - nothing(); - } - else - { - cLog(lsINFO) << "AccountSet: set message key"; + std::vector vucPublic = mTxn.getFieldVL(sfMessageKey); - mTxnAccount->setFieldVL(sfMessageKey, mTxn.getFieldVL(sfMessageKey)); + if (vucPublic.size() > PUBLIC_BYTES_MAX) + { + cLog(lsINFO) << "AccountSet: message key too long"; + + return telBAD_PUBLIC_KEY; + } + else + { + cLog(lsINFO) << "AccountSet: set message key"; + + mTxnAccount->setFieldVL(sfMessageKey, vucPublic); + } } // @@ -119,6 +127,12 @@ TER AccountSetTransactor::doApply() mTxnAccount->makeFieldAbsent(sfDomain); } + else if (vucDomain.size() > DOMAIN_BYTES_MAX) + { + cLog(lsINFO) << "AccountSet: domain too long"; + + return telBAD_DOMAIN; + } else { cLog(lsINFO) << "AccountSet: set domain"; diff --git a/src/cpp/ripple/Config.h b/src/cpp/ripple/Config.h index b24df2ed1..114414037 100644 --- a/src/cpp/ripple/Config.h +++ b/src/cpp/ripple/Config.h @@ -28,6 +28,9 @@ #define DEFAULT_VALIDATORS_SITE "" #define VALIDATORS_FILE_NAME "validators.txt" +const int DOMAIN_BYTES_MAX = 256; +const int PUBLIC_BYTES_MAX = 2048; // Maximum bytes for an account public key. + const int SYSTEM_PEER_PORT = 6561; const int SYSTEM_WEBSOCKET_PORT = 6562; const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away. diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp index dcf5f2df2..d9a55901f 100644 --- a/src/cpp/ripple/TransactionErr.cpp +++ b/src/cpp/ripple/TransactionErr.cpp @@ -35,7 +35,9 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { tefPAST_SEQ, "tefPAST_SEQ", "This sequence number has already past." }, { telLOCAL_ERROR, "telLOCAL_ERROR", "Local failure." }, + { telBAD_DOMAIN, "telBAD_DOMAIN", "Domain too long." }, { telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: Too many paths." }, + { telBAD_PUBLIC_KEY, "telBAD_PUBLIC_KEY", "Public key too long." }, { telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." }, { temMALFORMED, "temMALFORMED", "Malformed transaction." }, diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h index 7fb0e3215..e87ac3ead 100644 --- a/src/cpp/ripple/TransactionErr.h +++ b/src/cpp/ripple/TransactionErr.h @@ -13,7 +13,9 @@ enum TER // aka TransactionEngineResult // - Not forwarded // - No fee check telLOCAL_ERROR = -399, + telBAD_DOMAIN, telBAD_PATH_COUNT, + telBAD_PUBLIC_KEY, telINSUF_FEE_P, // -299 .. -200: M Malformed (bad signature) From c0a630a1962d632ec3af5ac04e49317b9411e5e6 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 16 Jan 2013 22:42:10 -0800 Subject: [PATCH 8/8] Allow RPC subscribe with no streams. --- src/cpp/ripple/RPCHandler.cpp | 53 +++++++++++++++-------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index bbab25921..41c9c6062 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2347,47 +2347,40 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) if (jvRequest.isMember("streams")) { - if (jvRequest["streams"].empty()) + for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) { - jvResult["error"] = "noStreams"; - } - else - { - for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) + if ((*it).isString()) { - if ((*it).isString()) + std::string streamName=(*it).asString(); + + if (streamName=="server") { - std::string streamName=(*it).asString(); + mNetOps->subServer(ispSub, jvResult); - if (streamName=="server") - { - mNetOps->subServer(ispSub, jvResult); + } + else if (streamName=="ledger") + { + mNetOps->subLedger(ispSub, jvResult); - } - else if (streamName=="ledger") - { - mNetOps->subLedger(ispSub, jvResult); + } + else if (streamName=="transactions") + { + mNetOps->subTransactions(ispSub); - } - else if (streamName=="transactions") - { - mNetOps->subTransactions(ispSub); - - } - else if (streamName=="rt_transactions") - { - mNetOps->subRTTransactions(ispSub); - } - else - { - jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); - } + } + else if (streamName=="rt_transactions") + { + mNetOps->subRTTransactions(ispSub); } else { - jvResult["error"] = "malformedStream"; + jvResult["error"] = "unknownStream"; } } + else + { + jvResult["error"] = "malformedStream"; + } } }