diff --git a/src/ripple/net/impl/RPCCall.cpp b/src/ripple/net/impl/RPCCall.cpp index b185bfd418f..904f5ccacac 100644 --- a/src/ripple/net/impl/RPCCall.cpp +++ b/src/ripple/net/impl/RPCCall.cpp @@ -497,6 +497,17 @@ class RPCParser bool isValidJson2(Json::Value const& jv) { + if (jv.isArray()) + { + if (jv.size() == 0) + return false; + for (auto const& j : jv) + { + if (!isValidJson2(j)) + return false; + } + return true; + } if (jv.isObject()) { if (jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0" && @@ -504,7 +515,7 @@ class RPCParser jv.isMember(jss::id) && jv.isMember(jss::method)) { if (jv.isMember(jss::params) && - !(jv[jss::params].isArray() || jv[jss::params].isNull())) + !(jv[jss::params].isArray() || jv[jss::params].isObject())) return false; return true; } @@ -519,17 +530,36 @@ class RPCParser bool valid_parse = reader.parse(jvParams[0u].asString(), jv); if (valid_parse && isValidJson2(jv)) { - Json::Value jv1{Json::objectValue}; - if (jv.isMember(jss::params)) + if (jv.isObject()) { - auto const& params = jv[jss::params][0u]; - for (auto i = params.begin(); i != params.end(); ++i) - jv1[i.key().asString()] = *i; + Json::Value jv1{Json::objectValue}; + if (jv.isMember(jss::params)) + { + auto const& params = jv[jss::params]; + for (auto i = params.begin(); i != params.end(); ++i) + jv1[i.key().asString()] = *i; + } + jv1[jss::jsonrpc] = jv[jss::jsonrpc]; + jv1[jss::ripplerpc] = jv[jss::ripplerpc]; + jv1[jss::id] = jv[jss::id]; + jv1[jss::method] = jv[jss::method]; + return jv1; + } + // else jv.isArray() + Json::Value jv1{Json::arrayValue}; + for (Json::UInt j = 0; j < jv.size(); ++j) + { + if (jv[j].isMember(jss::params)) + { + auto const& params = jv[j][jss::params]; + for (auto i = params.begin(); i != params.end(); ++i) + jv1[j][i.key().asString()] = *i; + } + jv1[j][jss::jsonrpc] = jv[j][jss::jsonrpc]; + jv1[j][jss::ripplerpc] = jv[j][jss::ripplerpc]; + jv1[j][jss::id] = jv[j][jss::id]; + jv1[j][jss::method] = jv[j][jss::method]; } - jv1[jss::jsonrpc] = jv[jss::jsonrpc]; - jv1[jss::ripplerpc] = jv[jss::ripplerpc]; - jv1[jss::id] = jv[jss::id]; - jv1[jss::method] = jv[jss::method]; return jv1; } auto jv_error = rpcError(rpcINVALID_PARAMS); @@ -1145,7 +1175,6 @@ struct RPCCallImp Json::Reader reader; Json::Value jvReply; - if (!reader.parse (strData, jvReply)) Throw ("couldn't parse reply from server"); @@ -1202,7 +1231,6 @@ rpcCmdLineToJson (std::vector const& args, jvRequest = rpParser.parseCommand (args[0], jvRpcParams, true); JLOG (j.trace()) << "RPC Request: " << jvRequest << std::endl; - return jvRequest; } @@ -1287,7 +1315,13 @@ rpcClient(std::vector const& args, if (!setup.client.admin_password.empty ()) jvRequest["admin_password"] = setup.client.admin_password; - jvParams.append (jvRequest); + if (jvRequest.isObject()) + jvParams.append (jvRequest); + else if (jvRequest.isArray()) + { + for (Json::UInt i = 0; i < jvRequest.size(); ++i) + jvParams.append(jvRequest[i]); + } if (jvRequest.isMember(jss::params)) { @@ -1305,7 +1339,10 @@ rpcClient(std::vector const& args, setup.client.password, "", jvRequest.isMember (jss::method) // Allow parser to rewrite method. - ? jvRequest[jss::method].asString () : args[0], + ? jvRequest[jss::method].asString () + : jvRequest.isArray() + ? "batch" + : args[0], jvParams, // Parsed, execute. setup.client.secure != 0, // Use SSL config.quiet(), @@ -1314,7 +1351,6 @@ rpcClient(std::vector const& args, std::placeholders::_1)); isService.run(); // This blocks until there is no more outstanding async calls. } - if (jvOutput.isMember ("result")) { // Had a successful JSON-RPC 2.0 call. @@ -1343,10 +1379,12 @@ rpcClient(std::vector const& args, if (jvOutput.isMember (jss::error)) { jvOutput[jss::status] = "error"; - - nRet = jvOutput.isMember (jss::error_code) - ? beast::lexicalCast (jvOutput[jss::error_code].asString ()) - : rpcBAD_SYNTAX; + if (jvOutput.isMember(jss::error_code)) + nRet = std::stoi(jvOutput[jss::error_code].asString()); + else if (jvOutput[jss::error].isMember(jss::error_code)) + nRet = std::stoi(jvOutput[jss::error][jss::error_code].asString()); + else + nRet = rpcBAD_SYNTAX; } // YYY We could have a command line flag for single line output for scripts. @@ -1359,13 +1397,6 @@ rpcClient(std::vector const& args, nRet = rpcINTERNAL; } - if (jvRequest.isMember(jss::jsonrpc)) - jvOutput[jss::jsonrpc] = jvRequest[jss::jsonrpc]; - if (jvRequest.isMember(jss::ripplerpc)) - jvOutput[jss::ripplerpc] = jvRequest[jss::ripplerpc]; - if (jvRequest.isMember(jss::id)) - jvOutput[jss::id] = jvRequest[jss::id]; - return { nRet, std::move(jvOutput) }; } diff --git a/src/ripple/rpc/impl/ServerHandlerImp.cpp b/src/ripple/rpc/impl/ServerHandlerImp.cpp index 0e806c134b6..0802c3924a3 100644 --- a/src/ripple/rpc/impl/ServerHandlerImp.cpp +++ b/src/ripple/rpc/impl/ServerHandlerImp.cpp @@ -537,6 +537,22 @@ ServerHandlerImp::processSession (std::shared_ptr const& session, session->close (true); } +static +Json::Value +make_json_error(Json::Int code, Json::Value&& message) +{ + Json::Value sub{Json::objectValue}; + sub["code"] = code; + sub["message"] = std::move(message); + Json::Value r{Json::objectValue}; + r["error"] = sub; + return r; +} + +Json::Int constexpr method_not_found = -32601; +Json::Int constexpr server_overloaded = -32604; +Json::Int constexpr forbidden = -32605; + void ServerHandlerImp::processRequest (Port const& port, std::string const& request, beast::IP::Endpoint const& remoteIPAddress, @@ -545,164 +561,243 @@ ServerHandlerImp::processRequest (Port const& port, { auto rpcJ = app_.journal ("RPC"); - Json::Value jsonRPC; + Json::Value jsonOrig; { Json::Reader reader; if ((request.size () > RPC::Tuning::maxRequestSize) || - ! reader.parse (request, jsonRPC) || - ! jsonRPC || - ! jsonRPC.isObject ()) + ! reader.parse (request, jsonOrig) || + ! jsonOrig || + ! (jsonOrig.isObject () || jsonOrig.isArray())) { HTTPReply (400, "Unable to parse request", output, rpcJ); return; } } - /* ---------------------------------------------------------------------- */ - // Determine role/usage so we can charge for invalid requests - Json::Value const& method = jsonRPC [jss::method]; - - auto role = Role::FORBID; - auto required = RPC::roleRequired(method.asString()); - if (jsonRPC.isMember(jss::params) && - jsonRPC[jss::params].isArray() && - jsonRPC[jss::params].size() > 0 && - jsonRPC[jss::params][Json::UInt(0)].isObject()) - { - role = requestRole(required, port, jsonRPC[jss::params][Json::UInt(0)], - remoteIPAddress, user); - } - else + bool batch = false; + if (jsonOrig.isMember(jss::method) && jsonOrig[jss::method] == "batch") + batch = true; + auto size = batch ? jsonOrig[jss::params].size() : 1; + Json::Value reply(batch ? Json::arrayValue : Json::objectValue); + auto const start (std::chrono::high_resolution_clock::now ()); + for (unsigned i = 0; i < size; ++i) { - role = requestRole(required, port, Json::objectValue, - remoteIPAddress, user); - } + Json::Value const& jsonRPC = batch ? jsonOrig[jss::params][i] : jsonOrig; + /* ---------------------------------------------------------------------- */ + // Determine role/usage so we can charge for invalid requests + Json::Value const& method = jsonRPC [jss::method]; + + auto role = Role::FORBID; + auto required = RPC::roleRequired(method.asString()); + if (jsonRPC.isMember(jss::params) && + jsonRPC[jss::params].isArray() && + jsonRPC[jss::params].size() > 0 && + jsonRPC[jss::params][Json::UInt(0)].isObject()) + { + role = requestRole(required, port, jsonRPC[jss::params][Json::UInt(0)], + remoteIPAddress, user); + } + else + { + role = requestRole(required, port, Json::objectValue, + remoteIPAddress, user); + } - Resource::Consumer usage; - if (isUnlimited(role)) - { - usage = m_resourceManager.newUnlimitedEndpoint( - remoteIPAddress.to_string()); - } - else - { - usage = m_resourceManager.newInboundEndpoint(remoteIPAddress); - if (usage.disconnect()) + Resource::Consumer usage; + if (isUnlimited(role)) { - HTTPReply(503, "Server is overloaded", output, rpcJ); - return; + usage = m_resourceManager.newUnlimitedEndpoint( + remoteIPAddress.to_string()); + } + else + { + usage = m_resourceManager.newInboundEndpoint(remoteIPAddress); + if (usage.disconnect()) + { + if (!batch) + { + HTTPReply(503, "Server is overloaded", output, rpcJ); + return; + } + Json::Value r = jsonRPC; + r[jss::error] = make_json_error(server_overloaded, "Server is overloaded"); + reply.append(r); + continue; + } } - } - if (role == Role::FORBID) - { - usage.charge(Resource::feeInvalidRPC); - HTTPReply (403, "Forbidden", output, rpcJ); - return; - } + if (role == Role::FORBID) + { + usage.charge(Resource::feeInvalidRPC); + if (!batch) + { + HTTPReply (403, "Forbidden", output, rpcJ); + return; + } + Json::Value r = jsonRPC; + r[jss::error] = make_json_error(forbidden, "Forbidden"); + reply.append(r); + continue; + } - if (method.isNull()) - { - usage.charge(Resource::feeInvalidRPC); - HTTPReply (400, "Null method", output, rpcJ); - return; - } + if (method.isNull()) + { + usage.charge(Resource::feeInvalidRPC); + if (!batch) + { + HTTPReply (400, "Null method", output, rpcJ); + return; + } + Json::Value r = jsonRPC; + r[jss::error] = make_json_error(method_not_found, "Null method"); + reply.append(r); + continue; + } - if (! method.isString ()) - { - usage.charge(Resource::feeInvalidRPC); - HTTPReply (400, "method is not string", output, rpcJ); - return; - } + if (! method.isString ()) + { + usage.charge(Resource::feeInvalidRPC); + if (!batch) + { + HTTPReply (400, "method is not string", output, rpcJ); + return; + } + Json::Value r = jsonRPC; + r[jss::error] = make_json_error(method_not_found, "method is not string"); + reply.append(r); + continue; + } - std::string strMethod = method.asString (); - if (strMethod.empty()) - { - usage.charge(Resource::feeInvalidRPC); - HTTPReply (400, "method is empty", output, rpcJ); - return; - } + std::string strMethod = method.asString (); + if (strMethod.empty()) + { + usage.charge(Resource::feeInvalidRPC); + if (!batch) + { + HTTPReply (400, "method is empty", output, rpcJ); + return; + } + Json::Value r = jsonRPC; + r[jss::error] = make_json_error(method_not_found, "method is empty"); + reply.append(r); + continue; + } - // Extract request parameters from the request Json as `params`. - // - // If the field "params" is empty, `params` is an empty object. - // - // Otherwise, that field must be an array of length 1 (why?) - // and we take that first entry and validate that it's an object. - Json::Value params = jsonRPC [jss::params]; + // Extract request parameters from the request Json as `params`. + // + // If the field "params" is empty, `params` is an empty object. + // + // Otherwise, that field must be an array of length 1 (why?) + // and we take that first entry and validate that it's an object. + Json::Value params; + if (!batch) + { + params = jsonRPC [jss::params]; + if (! params) + params = Json::Value (Json::objectValue); - if (! params) - params = Json::Value (Json::objectValue); + else if (!params.isArray () || params.size() != 1) + { + usage.charge(Resource::feeInvalidRPC); + HTTPReply (400, "params unparseable", output, rpcJ); + return; + } + else + { + params = std::move (params[0u]); + if (!params.isObject()) + { + usage.charge(Resource::feeInvalidRPC); + HTTPReply (400, "params unparseable", output, rpcJ); + return; + } + } + } + else // batch + { + params = jsonRPC; + } - else if (!params.isArray () || params.size() != 1) - { - usage.charge(Resource::feeInvalidRPC); - HTTPReply (400, "params unparseable", output, rpcJ); - return; - } - else - { - params = std::move (params[0u]); - if (!params.isObject()) + std::string ripplerpc = "1.0"; + if (params.isMember(jss::ripplerpc) && params[jss::ripplerpc] != "1.0") + ripplerpc = params[jss::ripplerpc].asString(); + /** + * Clear header-assigned values if not positively identified from a + * secure_gateway. + */ + if (role != Role::IDENTIFIED) { - usage.charge(Resource::feeInvalidRPC); - HTTPReply (400, "params unparseable", output, rpcJ); - return; + forwardedFor.clear(); + user.clear(); } - } - /** - * Clear header-assigned values if not positively identified from a - * secure_gateway. - */ - if (role != Role::IDENTIFIED) - { - forwardedFor.clear(); - user.clear(); - } + JLOG(m_journal.debug()) << "Query: " << strMethod << params; - JLOG(m_journal.debug()) << "Query: " << strMethod << params; + // Provide the JSON-RPC method as the field "command" in the request. + params[jss::command] = strMethod; + JLOG (m_journal.trace()) + << "doRpcCommand:" << strMethod << ":" << params; - // Provide the JSON-RPC method as the field "command" in the request. - params[jss::command] = strMethod; - JLOG (m_journal.trace()) - << "doRpcCommand:" << strMethod << ":" << params; + Resource::Charge loadType = Resource::feeReferenceRPC; - Resource::Charge loadType = Resource::feeReferenceRPC; - auto const start (std::chrono::high_resolution_clock::now ()); + RPC::Context context {m_journal, params, app_, loadType, m_networkOPs, + app_.getLedgerMaster(), usage, role, coro, InfoSub::pointer(), + {user, forwardedFor}}; + Json::Value result; + RPC::doCommand (context, result); + usage.charge (loadType); + if (usage.warn()) + result[jss::warning] = jss::load; - RPC::Context context {m_journal, params, app_, loadType, m_networkOPs, - app_.getLedgerMaster(), usage, role, coro, InfoSub::pointer(), - {user, forwardedFor}}; - Json::Value result; - RPC::doCommand (context, result); + Json::Value r(Json::objectValue); + if (ripplerpc >= "2.0") + { + if (result.isMember(jss::error)) + { + result[jss::status] = jss::error; + result["code"] = result[jss::error_code]; + result["message"] = result[jss::error_message]; + result.removeMember(jss::error_message); + r[jss::error] = std::move(result); + JLOG (m_journal.debug()) << + "rpcError: " << result [jss::error] << + ": " << result [jss::error_message]; + } + else + { + result[jss::status] = jss::success; + r[jss::result] = std::move(result); + } + } + else + { + // Always report "status". On an error report the request as received. + if (result.isMember (jss::error)) + { + result[jss::status] = jss::error; + result[jss::request] = params; + JLOG (m_journal.debug()) << + "rpcError: " << result [jss::error] << + ": " << result [jss::error_message]; + } + else + { + result[jss::status] = jss::success; + } + r[jss::result] = std::move(result); + } - // Always report "status". On an error report the request as received. - if (result.isMember (jss::error)) - { - result[jss::status] = jss::error; - result[jss::request] = params; - JLOG (m_journal.debug()) << - "rpcError: " << result [jss::error] << - ": " << result [jss::error_message]; - } - else - { - result[jss::status] = jss::success; + if (params.isMember(jss::jsonrpc)) + r[jss::jsonrpc] = params[jss::jsonrpc]; + if (params.isMember(jss::ripplerpc)) + r[jss::ripplerpc] = params[jss::ripplerpc]; + if (params.isMember(jss::id)) + r[jss::id] = params[jss::id]; + if (batch) + reply.append(std::move(r)); + else + reply = std::move(r); } - - usage.charge (loadType); - if (usage.warn()) - result[jss::warning] = jss::load; - - Json::Value reply (Json::objectValue); - reply[jss::result] = std::move (result); - if (jsonRPC.isMember(jss::jsonrpc)) - reply[jss::jsonrpc] = jsonRPC[jss::jsonrpc]; - if (jsonRPC.isMember(jss::ripplerpc)) - reply[jss::ripplerpc] = jsonRPC[jss::ripplerpc]; - if (jsonRPC.isMember(jss::id)) - reply[jss::id] = jsonRPC[jss::id]; auto response = to_string (reply); rpc_time_.notify (static_cast ( diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index bc3c5b57888..37b034c1313 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -465,31 +465,7 @@ Env::st (JTx const& jt) Json::Value Env::do_rpc(std::vector const& args) { - auto jv = cmdLineToJSONRPC(args, journal); - if (!jv.isMember(jss::jsonrpc)) - { - jv[jss::jsonrpc] = "2.0"; - jv[jss::ripplerpc] = "2.0"; - jv[jss::id] = 5; - } - auto response = client().invoke( - jv[jss::method].asString(), - jv[jss::params][0U]); - - if (jv.isMember(jss::jsonrpc)) - { - response[jss::jsonrpc] = jv[jss::jsonrpc]; - response[jss::ripplerpc] = jv[jss::ripplerpc]; - response[jss::id] = jv[jss::id]; - } - - if (jv[jss::params][0u].isMember(jss::error) && - (! response.isMember(jss::error))) - { - response["client_error"] = jv[jss::params][0u]; - } - - return response; + return rpcClient(args, app().config(), app().logs()).second; } void diff --git a/src/test/rpc/AccountInfo_test.cpp b/src/test/rpc/AccountInfo_test.cpp index 1dc29004d97..68c09a7eed1 100644 --- a/src/test/rpc/AccountInfo_test.cpp +++ b/src/test/rpc/AccountInfo_test.cpp @@ -174,17 +174,17 @@ class AccountInfo_test : public beast::unit_test::suite "\"ripplerpc\": \"2.0\", " "\"id\": 5, " "\"method\": \"account_info\", " - "\"params\": [{ " - "\"account\": \"" + alice.human() + "\"}]}"; + "\"params\": { " + "\"account\": \"" + alice.human() + "\"}}"; auto const withSigners = std::string ("{ ") + "\"jsonrpc\": \"2.0\", " "\"ripplerpc\": \"2.0\", " - "\"id\": 5, " + "\"id\": 6, " "\"method\": \"account_info\", " - "\"params\": [{ " + "\"params\": { " "\"account\": \"" + alice.human() + "\", " + - "\"signer_lists\": true }]}"; + "\"signer_lists\": true }}"; // Alice has no SignerList yet. { // account_info without the "signer_lists" argument. @@ -209,7 +209,30 @@ class AccountInfo_test : public beast::unit_test::suite BEAST_EXPECT(signerLists.size() == 0); BEAST_EXPECT(info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0"); BEAST_EXPECT(info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0"); - BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 5); + BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 6); + } + { + // Do both of the above as a batch job + auto const info = env.rpc ("json2", '[' + withoutSigners + ", " + + withSigners + ']'); + BEAST_EXPECT(info[0u].isMember(jss::result) && + info[0u][jss::result].isMember(jss::account_data)); + BEAST_EXPECT(! info[0u][jss::result][jss::account_data]. + isMember (jss::signer_lists)); + BEAST_EXPECT(info[0u].isMember(jss::jsonrpc) && info[0u][jss::jsonrpc] == "2.0"); + BEAST_EXPECT(info[0u].isMember(jss::ripplerpc) && info[0u][jss::ripplerpc] == "2.0"); + BEAST_EXPECT(info[0u].isMember(jss::id) && info[0u][jss::id] == 5); + + BEAST_EXPECT(info[1u].isMember(jss::result) && + info[1u][jss::result].isMember(jss::account_data)); + auto const& data = info[1u][jss::result][jss::account_data]; + BEAST_EXPECT(data.isMember (jss::signer_lists)); + auto const& signerLists = data[jss::signer_lists]; + BEAST_EXPECT(signerLists.isArray()); + BEAST_EXPECT(signerLists.size() == 0); + BEAST_EXPECT(info[1u].isMember(jss::jsonrpc) && info[1u][jss::jsonrpc] == "2.0"); + BEAST_EXPECT(info[1u].isMember(jss::ripplerpc) && info[1u][jss::ripplerpc] == "2.0"); + BEAST_EXPECT(info[1u].isMember(jss::id) && info[1u][jss::id] == 6); } // Give alice a SignerList. @@ -247,7 +270,7 @@ class AccountInfo_test : public beast::unit_test::suite BEAST_EXPECT(entry0[sfSignerWeight.jsonName] == 3); BEAST_EXPECT(info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0"); BEAST_EXPECT(info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0"); - BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 5); + BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 6); } // Give alice a big signer list @@ -287,7 +310,7 @@ class AccountInfo_test : public beast::unit_test::suite } BEAST_EXPECT(info.isMember(jss::jsonrpc) && info[jss::jsonrpc] == "2.0"); BEAST_EXPECT(info.isMember(jss::ripplerpc) && info[jss::ripplerpc] == "2.0"); - BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 5); + BEAST_EXPECT(info.isMember(jss::id) && info[jss::id] == 6); } } diff --git a/src/test/rpc/AccountLinesRPC_test.cpp b/src/test/rpc/AccountLinesRPC_test.cpp index 860726b4756..c7e2dc3b8f8 100644 --- a/src/test/rpc/AccountLinesRPC_test.cpp +++ b/src/test/rpc/AccountLinesRPC_test.cpp @@ -374,7 +374,7 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("ripplerpc" : "2.0",)" R"("id" : 5)" " }"); - BEAST_EXPECT(lines[jss::result][jss::error_message] == + BEAST_EXPECT(lines[jss::error][jss::message] == RPC::missing_field_error(jss::account)[jss::error_message]); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); @@ -387,10 +387,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": )" - R"("n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"}]})"); - BEAST_EXPECT(lines[jss::result][jss::error_message] == + R"("n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"}})"); + BEAST_EXPECT(lines[jss::error][jss::message] == RPC::make_error(rpcBAD_SEED)[jss::error_message]); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); @@ -404,9 +404,9 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" - R"({"account": ")" + alice.human() + R"("}]})"); - BEAST_EXPECT(lines[jss::result][jss::error_message] == + R"("params": )" + R"({"account": ")" + alice.human() + R"("}})"); + BEAST_EXPECT(lines[jss::error][jss::message] == RPC::make_error(rpcACT_NOT_FOUND)[jss::error_message]); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); @@ -424,8 +424,8 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" - R"({"account": ")" + alice.human() + R"("}]})"); + R"("params": )" + R"({"account": ")" + alice.human() + R"("}})"); BEAST_EXPECT(lines[jss::result][jss::lines].isArray()); BEAST_EXPECT(lines[jss::result][jss::lines].size() == 0); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); @@ -439,10 +439,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("ledger_index": "nonsense"}]})"); - BEAST_EXPECT(lines[jss::result][jss::error_message] == + R"("ledger_index": "nonsense"}})"); + BEAST_EXPECT(lines[jss::error][jss::message] == "ledgerIndexMalformed"); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); @@ -455,10 +455,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("ledger_index": 50000}]})"); - BEAST_EXPECT(lines[jss::result][jss::error_message] == + R"("ledger_index": 50000}})"); + BEAST_EXPECT(lines[jss::error][jss::message] == "ledgerNotFound"); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); @@ -525,9 +525,9 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + account.human() + R"(", )" - R"("ledger_index": )" + std::to_string(info.seq) + "}]}"); + R"("ledger_index": )" + std::to_string(info.seq) + "}}"); BEAST_EXPECT(linesSeq[jss::result][jss::lines].isArray()); BEAST_EXPECT(linesSeq[jss::result][jss::lines].size() == count); BEAST_EXPECT(linesSeq.isMember(jss::jsonrpc) && linesSeq[jss::jsonrpc] == "2.0"); @@ -540,9 +540,9 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + account.human() + R"(", )" - R"("ledger_hash": ")" + to_string(info.hash) + R"("}]})"); + R"("ledger_hash": ")" + to_string(info.hash) + R"("}})"); BEAST_EXPECT(linesHash[jss::result][jss::lines].isArray()); BEAST_EXPECT(linesHash[jss::result][jss::lines].size() == count); BEAST_EXPECT(linesHash.isMember(jss::jsonrpc) && linesHash[jss::jsonrpc] == "2.0"); @@ -567,10 +567,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" R"("ledger_hash": ")" + to_string(ledger4Info.hash) + R"(", )" - R"("ledger_index": )" + std::to_string(ledger58Info.seq) + "}]}"); + R"("ledger_index": )" + std::to_string(ledger58Info.seq) + "}}"); BEAST_EXPECT(lines[jss::result][jss::lines].isArray()); BEAST_EXPECT(lines[jss::result][jss::lines].size() == 26); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); @@ -584,8 +584,8 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" - R"({"account": ")" + alice.human() + R"("}]})"); + R"("params": )" + R"({"account": ")" + alice.human() + R"("}})"); BEAST_EXPECT(lines[jss::result][jss::lines].isArray()); BEAST_EXPECT(lines[jss::result][jss::lines].size() == 52); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); @@ -599,9 +599,9 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("peer": ")" + gw1.human() + R"("}]})"); + R"("peer": ")" + gw1.human() + R"("}})"); BEAST_EXPECT(lines[jss::result][jss::lines].isArray()); BEAST_EXPECT(lines[jss::result][jss::lines].size() == 26); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); @@ -615,11 +615,11 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" R"("peer": )" - R"("n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"}]})"); - BEAST_EXPECT(lines[jss::result][jss::error_message] == + R"("n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"}})"); + BEAST_EXPECT(lines[jss::error][jss::message] == RPC::make_error(rpcBAD_SEED)[jss::error_message]); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); @@ -632,10 +632,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("limit": -1}]})"); - BEAST_EXPECT(lines[jss::result][jss::error_message] == + R"("limit": -1}})"); + BEAST_EXPECT(lines[jss::error][jss::message] == RPC::expected_field_message(jss::limit, "unsigned integer")); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); @@ -648,9 +648,9 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("limit": 1}]})"); + R"("limit": 1}})"); BEAST_EXPECT(linesA[jss::result][jss::lines].isArray()); BEAST_EXPECT(linesA[jss::result][jss::lines].size() == 1); BEAST_EXPECT(linesA.isMember(jss::jsonrpc) && linesA[jss::jsonrpc] == "2.0"); @@ -664,9 +664,9 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("marker": ")" + marker + R"("}]})"); + R"("marker": ")" + marker + R"("}})"); BEAST_EXPECT(linesB[jss::result][jss::lines].isArray()); BEAST_EXPECT(linesB[jss::result][jss::lines].size() == 51); BEAST_EXPECT(linesB.isMember(jss::jsonrpc) && linesB[jss::jsonrpc] == "2.0"); @@ -679,10 +679,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" R"("limit": 3, )" - R"("marker": ")" + marker + R"("}]})"); + R"("marker": ")" + marker + R"("}})"); BEAST_EXPECT(linesC[jss::result][jss::lines].isArray()); BEAST_EXPECT(linesC[jss::result][jss::lines].size() == 3); BEAST_EXPECT(linesC.isMember(jss::jsonrpc) && linesC[jss::jsonrpc] == "2.0"); @@ -696,10 +696,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("marker": ")" + marker + R"("}]})"); - BEAST_EXPECT(linesD[jss::result][jss::error_message] == + R"("marker": ")" + marker + R"("}})"); + BEAST_EXPECT(linesD[jss::error][jss::message] == RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); BEAST_EXPECT(linesD.isMember(jss::jsonrpc) && linesD[jss::jsonrpc] == "2.0"); BEAST_EXPECT(linesD.isMember(jss::ripplerpc) && linesD[jss::ripplerpc] == "2.0"); @@ -712,10 +712,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("marker": true}]})"); - BEAST_EXPECT(lines[jss::result][jss::error_message] == + R"("marker": true}})"); + BEAST_EXPECT(lines[jss::error][jss::message] == RPC::expected_field_message(jss::marker, "string")); BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0"); BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0"); @@ -728,10 +728,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" R"("limit": 1, )" - R"("peer": ")" + gw2.human() + R"("}]})"); + R"("peer": ")" + gw2.human() + R"("}})"); auto const& line = lines[jss::result][jss::lines][0u]; BEAST_EXPECT(line[jss::freeze].asBool() == true); BEAST_EXPECT(line[jss::no_ripple].asBool() == true); @@ -747,10 +747,10 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + gw2.human() + R"(", )" R"("limit": 1, )" - R"("peer": ")" + alice.human() + R"("}]})"); + R"("peer": ")" + alice.human() + R"("}})"); auto const& lineA = linesA[jss::result][jss::lines][0u]; BEAST_EXPECT(lineA[jss::freeze_peer].asBool() == true); BEAST_EXPECT(lineA[jss::no_ripple_peer].asBool() == true); @@ -767,11 +767,11 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + gw2.human() + R"(", )" R"("limit": 25, )" R"("marker": ")" + marker + R"(", )" - R"("peer": ")" + alice.human() + R"("}]})"); + R"("peer": ")" + alice.human() + R"("}})"); BEAST_EXPECT(linesB[jss::result][jss::lines].isArray()); BEAST_EXPECT(linesB[jss::result][jss::lines].size() == 25); BEAST_EXPECT(! linesB[jss::result].isMember(jss::marker)); @@ -833,9 +833,9 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" - R"("limit": 1}]})"); + R"("limit": 1}})"); BEAST_EXPECT(linesBeg[jss::result][jss::lines][0u][jss::currency] == "USD"); BEAST_EXPECT(linesBeg[jss::result].isMember(jss::marker)); BEAST_EXPECT(linesBeg.isMember(jss::jsonrpc) && linesBeg[jss::jsonrpc] == "2.0"); @@ -853,11 +853,11 @@ class AccountLinesRPC_test : public beast::unit_test::suite R"("jsonrpc" : "2.0",)" R"("ripplerpc" : "2.0",)" R"("id" : 5,)" - R"("params": [ )" + R"("params": )" R"({"account": ")" + alice.human() + R"(", )" R"("marker": ")" + - linesBeg[jss::result][jss::marker].asString() + R"("}]})"); - BEAST_EXPECT(linesEnd[jss::result][jss::error_message] == + linesBeg[jss::result][jss::marker].asString() + R"("}})"); + BEAST_EXPECT(linesEnd[jss::error][jss::message] == RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]); BEAST_EXPECT(linesEnd.isMember(jss::jsonrpc) && linesEnd[jss::jsonrpc] == "2.0"); BEAST_EXPECT(linesEnd.isMember(jss::ripplerpc) && linesEnd[jss::ripplerpc] == "2.0"); diff --git a/src/test/rpc/AccountObjects_test.cpp b/src/test/rpc/AccountObjects_test.cpp index 302287e26fb..3feff341a81 100644 --- a/src/test/rpc/AccountObjects_test.cpp +++ b/src/test/rpc/AccountObjects_test.cpp @@ -122,9 +122,9 @@ class AccountObjects_test : public beast::unit_test::suite // test error on no account { - auto resp = env.rpc("account_objects"); - BEAST_EXPECT( resp[jss::result][jss::error_message] == - "Missing field 'account'."); + auto resp = env.rpc("json", "account_objects"); + BEAST_EXPECT( resp[jss::error_message] == + "Syntax error."); } // test error on malformed account string. { diff --git a/src/test/rpc/AccountOffers_test.cpp b/src/test/rpc/AccountOffers_test.cpp index de574cbc25a..639e1780986 100644 --- a/src/test/rpc/AccountOffers_test.cpp +++ b/src/test/rpc/AccountOffers_test.cpp @@ -197,10 +197,10 @@ class AccountOffers_test : public beast::unit_test::suite { // no account field - auto const jrr = env.rpc ("account_offers")[jss::result]; - BEAST_EXPECT(jrr[jss::error] == "invalidParams"); + auto const jrr = env.rpc ("account_offers"); + BEAST_EXPECT(jrr[jss::error] == "badSyntax"); BEAST_EXPECT(jrr[jss::status] == "error"); - BEAST_EXPECT(jrr[jss::error_message] == "Missing field 'account'."); + BEAST_EXPECT(jrr[jss::error_message] == "Syntax error."); } { diff --git a/src/test/rpc/Feature_test.cpp b/src/test/rpc/Feature_test.cpp index 7fa2aa12a64..d2518778a25 100644 --- a/src/test/rpc/Feature_test.cpp +++ b/src/test/rpc/Feature_test.cpp @@ -257,10 +257,8 @@ class Feature_test : public beast::unit_test::suite // anything other than accept or reject is an error jrr = env.rpc("feature", "CryptoConditions", "maybe"); - if(! BEAST_EXPECT(jrr.isMember("client_error"))) - return; - BEAST_EXPECT(jrr["client_error"][jss::error] == "invalidParams"); - BEAST_EXPECT(jrr["client_error"][jss::error_message] == "Invalid parameters."); + BEAST_EXPECT(jrr[jss::error] == "invalidParams"); + BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters."); } public: diff --git a/src/test/rpc/ServerInfo_test.cpp b/src/test/rpc/ServerInfo_test.cpp index 87367887c03..bb032a63ff8 100644 --- a/src/test/rpc/ServerInfo_test.cpp +++ b/src/test/rpc/ServerInfo_test.cpp @@ -73,16 +73,16 @@ class ServerInfo_test : public beast::unit_test::suite { Env env(*this); - auto const result = env.rpc("server_info", "1"); + auto const result = env.rpc("server_info"); BEAST_EXPECT(!result[jss::result].isMember (jss::error)); - BEAST_EXPECT(result[jss::status] == "success"); + BEAST_EXPECT(result[jss::result][jss::status] == "success"); BEAST_EXPECT(result[jss::result].isMember(jss::info)); } { Env env(*this, makeValidatorConfig()); - auto const result = env.rpc("server_info", "1"); + auto const result = env.rpc("server_info"); BEAST_EXPECT(!result[jss::result].isMember (jss::error)); - BEAST_EXPECT(result[jss::status] == "success"); + BEAST_EXPECT(result[jss::result][jss::status] == "success"); BEAST_EXPECT(result[jss::result].isMember(jss::info)); BEAST_EXPECT(result[jss::result][jss::info] [jss::pubkey_validator] == validator_data::public_key); diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index 23865755731..5904bd06659 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -786,7 +786,7 @@ class ServerStatus_test : using namespace beast::http; Env env {*this, validator( envconfig([](std::unique_ptr cfg) { - cfg->section("port_rpc").set("protocol", "http,https"); + cfg->section("port_rpc").set("protocol", "http"); return cfg; }), "")};