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] 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/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 912e093bc..27b173a2c 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..114414037 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. @@ -26,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. @@ -110,6 +115,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/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index a40a0cb97..d09283fbd 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..41c9c6062 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2374,12 +2374,12 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest) } else { - jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); + jvResult["error"] = "unknownStream"; } } else { - jvResult["error"] = "malformedSteam"; + jvResult["error"] = "malformedStream"; } } } @@ -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); }; 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) 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. } 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; 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;