Recognize ripplerpc 2.0 requests and respond in kind:

* Force jtx to request/receive the 2.0 API
* Force the JSON and WebSocket tests to use 2.0 API
*  This specifically allows the Websocket to create 2.0 json/ripple
   and get back a 2.0 response.
* Add test for malformed json2
* Add check for parse failure
* Add check for params to be in array form.
* Correct type-o discovered in tests due to stricter checking.
* Add API version to the WSClient & JSONRPCClient test
* Update source.dox with more headers
This commit is contained in:
Howard Hinnant
2016-10-25 16:08:48 -04:00
committed by Nik Bougalis
parent cdf470e68d
commit 2fd0540ed4
20 changed files with 1178 additions and 62 deletions

View File

@@ -105,6 +105,10 @@ WARN_LOGFILE =
INPUT = \
\
../src/ripple/protocol/STObject.h \
../src/ripple/protocol/JsonFields.h \
../src/ripple/test/AbstractClient.h \
../src/ripple/test/JSONRPCClient.h \
../src/ripple/test/WSClient.h \
INPUT_ENCODING = UTF-8

View File

@@ -434,8 +434,8 @@ int PathRequest::parseJson (Json::Value const& jvParams)
}
}
if (jvParams.isMember ("id"))
jvId = jvParams["id"];
if (jvParams.isMember (jss::id))
jvId = jvParams[jss::id];
return PFR_PJ_NOCHANGE;
}
@@ -649,7 +649,7 @@ Json::Value PathRequest::doUpdate(
newStatus[jss::full_reply] = ! fast;
if (jvId)
newStatus["id"] = jvId;
newStatus[jss::id] = jvId;
bool loaded = app_.getFeeTrack().isLoadedLocal();

View File

@@ -487,7 +487,7 @@ private:
if (!jvRequest.isObject ())
return rpcError (rpcINVALID_PARAMS);
jvRequest["method"] = jvParams[0u];
jvRequest[jss::method] = jvParams[0u];
return jvRequest;
}
@@ -495,6 +495,53 @@ private:
return rpcError (rpcINVALID_PARAMS);
}
bool isValidJson2(Json::Value const& jv)
{
if (jv.isObject())
{
if (jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0" &&
jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0" &&
jv.isMember(jss::id) && jv.isMember(jss::method))
{
if (jv.isMember(jss::params) &&
!(jv[jss::params].isArray() || jv[jss::params].isNull()))
return false;
return true;
}
}
return false;
}
Json::Value parseJson2(Json::Value const& jvParams)
{
Json::Reader reader;
Json::Value jv;
bool valid_parse = reader.parse(jvParams[0u].asString(), jv);
if (valid_parse && isValidJson2(jv))
{
Json::Value jv1{Json::objectValue};
if (jv.isMember(jss::params))
{
auto const& params = jv[jss::params][0u];
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;
}
auto jv_error = rpcError(rpcINVALID_PARAMS);
if (jv.isMember(jss::jsonrpc))
jv_error[jss::jsonrpc] = jv[jss::jsonrpc];
if (jv.isMember(jss::ripplerpc))
jv_error[jss::ripplerpc] = jv[jss::ripplerpc];
if (jv.isMember(jss::id))
jv_error[jss::id] = jv[jss::id];
return jv_error;
}
// ledger [id|index|current|closed|validated] [full|tx]
Json::Value parseLedger (Json::Value const& jvParams)
{
@@ -1003,6 +1050,7 @@ public:
{ "gateway_balances", &RPCParser::parseGatewayBalances , 1, -1 },
{ "get_counts", &RPCParser::parseGetCounts, 0, 1 },
{ "json", &RPCParser::parseJson, 2, 2 },
{ "json2", &RPCParser::parseJson2, 1, 1 },
{ "ledger", &RPCParser::parseLedger, 0, 2 },
{ "ledger_accept", &RPCParser::parseAsIs, 0, 0 },
{ "ledger_closed", &RPCParser::parseAsIs, 0, 0 },
@@ -1181,7 +1229,7 @@ rpcCmdLineToJson (std::vector<std::string> const& args,
retParams = Json::Value (Json::objectValue);
retParams["method"] = args[0];
retParams[jss::method] = args[0];
retParams[jss::params] = jvRpcParams;
jvRequest = rpParser.parseCommand (args[0], jvRpcParams, true);
@@ -1210,6 +1258,12 @@ cmdLineToJSONRPC (std::vector<std::string> const& args, beast::Journal j)
auto& paramsArray = Json::setArray (jv, jss::params);
paramsArray.append (paramsObj);
}
if (paramsObj.isMember(jss::jsonrpc))
jv[jss::jsonrpc] = paramsObj[jss::jsonrpc];
if (paramsObj.isMember(jss::ripplerpc))
jv[jss::ripplerpc] = paramsObj[jss::ripplerpc];
if (paramsObj.isMember(jss::id))
jv[jss::id] = paramsObj[jss::id];
return jv;
}
@@ -1267,6 +1321,12 @@ rpcClient(std::vector<std::string> const& args,
jvParams.append (jvRequest);
if (jvRequest.isMember(jss::params))
{
auto const& params = jvRequest[jss::params];
assert(params.size() == 1);
jvParams.append(params[0u]);
}
{
boost::asio::io_service isService;
RPCCall::fromNetwork (
@@ -1276,8 +1336,8 @@ rpcClient(std::vector<std::string> const& args,
setup.client.user,
setup.client.password,
"",
jvRequest.isMember ("method") // Allow parser to rewrite method.
? jvRequest["method"].asString () : args[0],
jvRequest.isMember (jss::method) // Allow parser to rewrite method.
? jvRequest[jss::method].asString () : args[0],
jvParams, // Parsed, execute.
setup.client.secure != 0, // Use SSL
config.quiet(),
@@ -1331,6 +1391,13 @@ rpcClient(std::vector<std::string> 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) };
}

View File

@@ -213,6 +213,7 @@ JSS ( ip ); // in: Connect, out: OverlayImpl
JSS ( issuer ); // in: RipplePathFind, Subscribe,
// Unsubscribe, BookOffers
// out: paths/Node, STPathSet, STAmount
JSS ( jsonrpc ); // json version
JSS ( key ); // out: WalletSeed
JSS ( key_type ); // in/out: WalletPropose, TransactionSign
JSS ( latency ); // out: PeerImp
@@ -350,6 +351,7 @@ JSS ( response ); // websocket
JSS ( result ); // RPC
JSS ( ripple_lines ); // out: NetworkOPs
JSS ( ripple_state ); // in: LedgerEntr
JSS ( ripplerpc ); // ripple RPC version
JSS ( role ); // out: Ping.cpp
JSS ( rt_accounts ); // in: Subscribe, Unsubscribe
JSS ( sanity ); // out: PeerImp

View File

@@ -124,10 +124,18 @@ error_code_i fillHandler (Context& context,
}
}
if (!context.params.isMember ("command"))
if (!context.params.isMember(jss::command) && !context.params.isMember(jss::method))
return rpcCOMMAND_MISSING;
if (context.params.isMember(jss::command) && context.params.isMember(jss::method))
{
if (context.params[jss::command].asString() !=
context.params[jss::method].asString())
return rpcUNKNOWN_COMMAND;
}
std::string strCommand = context.params[jss::command].asString ();
std::string strCommand = context.params.isMember(jss::command) ?
context.params[jss::command].asString() :
context.params[jss::method].asString();
JLOG (context.j.trace()) << "COMMAND:" << strCommand;
JLOG (context.j.trace()) << "REQUEST:" << context.params;

View File

@@ -404,7 +404,9 @@ ServerHandlerImp::processSession(
// Requests without "command" are invalid.
Json::Value jr(Json::objectValue);
if (! jv.isMember (jss::command))
if ((!jv.isMember(jss::command) && !jv.isMember(jss::method)) ||
(jv.isMember(jss::command) && jv.isMember(jss::method) &&
jv[jss::command].asString() != jv[jss::method].asString()))
{
jr[jss::type] = jss::response;
jr[jss::status] = jss::error;
@@ -412,13 +414,19 @@ ServerHandlerImp::processSession(
jr[jss::request] = jv;
if (jv.isMember (jss::id))
jr[jss::id] = jv[jss::id];
if (jv.isMember(jss::jsonrpc))
jr[jss::jsonrpc] = jv[jss::jsonrpc];
if (jv.isMember(jss::jsonrpc))
jr[jss::ripplerpc] = jv[jss::ripplerpc];
is->getConsumer().charge(Resource::feeInvalidRPC);
return jr;
}
Resource::Charge loadType = Resource::feeReferenceRPC;
auto required = RPC::roleRequired(jv[jss::command].asString());
auto required = RPC::roleRequired(jv.isMember(jss::command) ?
jv[jss::command].asString() :
jv[jss::method].asString());
auto role = requestRole(
required,
session->port(),
@@ -475,6 +483,10 @@ ServerHandlerImp::processSession(
if (jv.isMember(jss::id))
jr[jss::id] = jv[jss::id];
if (jv.isMember(jss::jsonrpc))
jr[jss::jsonrpc] = jv[jss::jsonrpc];
if (jv.isMember(jss::jsonrpc))
jr[jss::ripplerpc] = jv[jss::ripplerpc];
jr[jss::type] = jss::response;
return jr;
}
@@ -539,8 +551,8 @@ ServerHandlerImp::processRequest (Port const& port,
//
// VFALCO NOTE Except that "id" isn't included in the following errors.
//
Json::Value const& id = jsonRPC ["id"];
Json::Value const& method = jsonRPC ["method"];
Json::Value const& id = jsonRPC [jss::id];
Json::Value const& method = jsonRPC [jss::method];
if (! method) {
HTTPReply (400, "Null method", output, rpcJ);
@@ -555,12 +567,12 @@ ServerHandlerImp::processRequest (Port const& port,
/* ---------------------------------------------------------------------- */
auto role = Role::FORBID;
auto required = RPC::roleRequired(id.asString());
if (jsonRPC.isMember("params") &&
jsonRPC["params"].isArray() &&
jsonRPC["params"].size() > 0 &&
jsonRPC["params"][Json::UInt(0)].isObject())
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["params"][Json::UInt(0)],
role = requestRole(required, port, jsonRPC[jss::params][Json::UInt(0)],
remoteIPAddress, user);
}
else
@@ -671,6 +683,12 @@ ServerHandlerImp::processRequest (Port const& port,
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 <beast::insight::Event::value_type> (

View File

@@ -262,11 +262,6 @@ SHAMap::fetchNodeFromDB (SHAMapHash const& hash) const
{
auto root = std::dynamic_pointer_cast<SHAMapInnerNode>(root_);
assert(root);
if (!root->isEmpty())
{
std::cerr << "isv2 = " << isv2 << '\n';
std::cerr << "is_v2() = " << is_v2() << '\n';
}
assert(root->isEmpty());
if (isv2)
{

View File

@@ -53,6 +53,9 @@ public:
Json::Value
invoke(std::string const& cmd,
Json::Value const& params = {}) = 0;
/// Get RPC 1.0 or RPC 2.0
virtual unsigned version() const = 0;
};
} // test

View File

@@ -29,7 +29,7 @@ namespace test {
/** Returns a client using JSON-RPC over HTTP/S. */
std::unique_ptr<AbstractClient>
makeJSONRPCClient(Config const& cfg);
makeJSONRPCClient(Config const& cfg, unsigned rpc_version = 2);
} // test
} // ripple

View File

@@ -47,7 +47,7 @@ public:
/** Returns a client operating through WebSockets/S. */
std::unique_ptr<WSClient>
makeWSClient(Config const& cfg, bool v2 = true);
makeWSClient(Config const& cfg, bool v2 = true, unsigned rpc_version = 2);
} // test
} // ripple

View File

@@ -16,11 +16,11 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/test/JSONRPCClient.h>
#include <ripple/json/json_reader.h>
#include <ripple/json/to_string.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/server/Port.h>
#include <beast/http/message_v1.hpp>
#include <beast/http/streambuf_body.hpp>
@@ -76,12 +76,14 @@ class JSONRPCClient : public AbstractClient
boost::asio::ip::tcp::socket stream_;
beast::streambuf bin_;
beast::streambuf bout_;
unsigned rpc_version_;
public:
explicit
JSONRPCClient(Config const& cfg)
JSONRPCClient(Config const& cfg, unsigned rpc_version)
: ep_(getEndpoint(cfg))
, stream_(ios_)
, rpc_version_(rpc_version)
{
stream_.connect(ep_);
}
@@ -115,10 +117,16 @@ public:
ep_.address().to_string() + ":" + std::to_string(ep_.port()));
{
Json::Value jr;
jr["method"] = cmd;
jr[jss::method] = cmd;
if (rpc_version_ == 2)
{
jr[jss::jsonrpc] = "2.0";
jr[jss::ripplerpc] = "2.0";
jr[jss::id] = 5;
}
if(params)
{
Json::Value& ja = jr["params"] = Json::arrayValue;
Json::Value& ja = jr[jss::params] = Json::arrayValue;
ja.append(params);
}
req.body = to_string(jr);
@@ -138,12 +146,17 @@ public:
jv["status"] = jv["result"]["status"];
return jv;
}
unsigned version() const override
{
return rpc_version_;
}
};
std::unique_ptr<AbstractClient>
makeJSONRPCClient(Config const& cfg)
makeJSONRPCClient(Config const& cfg, unsigned rpc_version)
{
return std::make_unique<JSONRPCClient>(cfg);
return std::make_unique<JSONRPCClient>(cfg, rpc_version);
}
} // test

View File

@@ -107,13 +107,16 @@ class WSClientImpl : public WSClient
std::condition_variable cv_;
std::list<std::shared_ptr<msg>> msgs_;
unsigned rpc_version_;
public:
WSClientImpl(Config const& cfg, bool v2)
WSClientImpl(Config const& cfg, bool v2, unsigned rpc_version)
: work_(ios_)
, strand_(ios_)
, thread_([&]{ ios_.run(); })
, stream_(ios_)
, ws_(stream_)
, rpc_version_(rpc_version)
{
auto const ep = getEndpoint(cfg, v2);
stream_.connect(ep);
@@ -143,6 +146,14 @@ public:
Json::Value jp;
if(params)
jp = params;
if (rpc_version_ == 2)
{
jp[jss::method] = cmd;
jp[jss::jsonrpc] = "2.0";
jp[jss::ripplerpc] = "2.0";
jp[jss::id] = 5;
}
else
jp[jss::command] = cmd;
auto const s = to_string(jp);
ws_.write_frame(true, buffer(s));
@@ -167,7 +178,6 @@ public:
ret[jss::status] = jss::error;
return ret;
}
if ((*jv).isMember(jss::status) &&
(*jv).isMember(jss::result))
(*jv)[jss::result][jss::status] =
@@ -221,6 +231,11 @@ public:
return std::move(m->jv);
}
unsigned version() const override
{
return rpc_version_;
}
private:
void
on_read_msg(error_code const& ec)
@@ -254,9 +269,9 @@ private:
};
std::unique_ptr<WSClient>
makeWSClient(Config const& cfg, bool v2)
makeWSClient(Config const& cfg, bool v2, unsigned rpc_version)
{
return std::make_unique<WSClientImpl>(cfg, v2);
return std::make_unique<WSClientImpl>(cfg, v2, rpc_version);
}
} // test

View File

@@ -537,9 +537,21 @@ Env::st (JTx const& jt)
Json::Value
Env::do_rpc(std::vector<std::string> const& args)
{
auto const jv = cmdLineToJSONRPC(args, journal);
return client().invoke(jv["method"].asString(),
jv["params"][0U]);
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];
}
return response;
}
} // jtx

View File

@@ -1744,6 +1744,12 @@ public:
auto jrr = wsc->invoke("submit", payment);
BEAST_EXPECT(jrr[jss::status] == "success");
BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "tesSUCCESS");
if (wsc->version() == 2)
{
BEAST_EXPECT(jrr.isMember(jss::jsonrpc) && jrr[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jrr.isMember(jss::ripplerpc) && jrr[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jrr.isMember(jss::id) && jrr[jss::id] == 5);
}
jrr = ledgerEntryState (env, alice, gw, "XTS");
BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");

View File

@@ -161,10 +161,141 @@ public:
}
}
// Test the "signer_lists" argument in account_info, version 2 API.
void testSignerListsV2()
{
using namespace jtx;
Env env(*this, features(featureMultiSign));
Account const alice {"alice"};
env.fund(XRP(1000), alice);
auto const withoutSigners = std::string ("{ ") +
"\"jsonrpc\": \"2.0\", "
"\"ripplerpc\": \"2.0\", "
"\"id\": 5, "
"\"method\": \"account_info\", "
"\"params\": [{ "
"\"account\": \"" + alice.human() + "\"}]}";
auto const withSigners = std::string ("{ ") +
"\"jsonrpc\": \"2.0\", "
"\"ripplerpc\": \"2.0\", "
"\"id\": 5, "
"\"method\": \"account_info\", "
"\"params\": [{ "
"\"account\": \"" + alice.human() + "\", " +
"\"signer_lists\": true }]}";
// Alice has no SignerList yet.
{
// account_info without the "signer_lists" argument.
auto const info = env.rpc ("json2", withoutSigners);
BEAST_EXPECT(info.isMember(jss::result) &&
info[jss::result].isMember(jss::account_data));
BEAST_EXPECT(! info[jss::result][jss::account_data].
isMember (jss::signer_lists));
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);
}
{
// account_info with the "signer_lists" argument.
auto const info = env.rpc ("json2", withSigners);
BEAST_EXPECT(info.isMember(jss::result) &&
info[jss::result].isMember(jss::account_data));
auto const& data = info[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.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);
}
// Give alice a SignerList.
Account const bogie {"bogie"};
Json::Value const smallSigners = signers(alice, 2, { { bogie, 3 } });
env(smallSigners);
{
// account_info without the "signer_lists" argument.
auto const info = env.rpc ("json2", withoutSigners);
BEAST_EXPECT(info.isMember(jss::result) &&
info[jss::result].isMember(jss::account_data));
BEAST_EXPECT(! info[jss::result][jss::account_data].
isMember (jss::signer_lists));
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);
}
{
// account_info with the "signer_lists" argument.
auto const info = env.rpc ("json2", withSigners);
BEAST_EXPECT(info.isMember(jss::result) &&
info[jss::result].isMember(jss::account_data));
auto const& data = info[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() == 1);
auto const& signers = signerLists[0u];
BEAST_EXPECT(signers.isObject());
BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 2);
auto const& signerEntries = signers[sfSignerEntries.jsonName];
BEAST_EXPECT(signerEntries.size() == 1);
auto const& entry0 = signerEntries[0u][sfSignerEntry.jsonName];
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);
}
// Give alice a big signer list
Account const demon {"demon"};
Account const ghost {"ghost"};
Account const haunt {"haunt"};
Account const jinni {"jinni"};
Account const phase {"phase"};
Account const shade {"shade"};
Account const spook {"spook"};
Json::Value const bigSigners = signers(alice, 4, {
{bogie, 1}, {demon, 1}, {ghost, 1}, {haunt, 1},
{jinni, 1}, {phase, 1}, {shade, 1}, {spook, 1}, });
env(bigSigners);
{
// account_info with the "signer_lists" argument.
auto const info = env.rpc ("json2", withSigners);
BEAST_EXPECT(info.isMember(jss::result) &&
info[jss::result].isMember(jss::account_data));
auto const& data = info[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() == 1);
auto const& signers = signerLists[0u];
BEAST_EXPECT(signers.isObject());
BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 4);
auto const& signerEntries = signers[sfSignerEntries.jsonName];
BEAST_EXPECT(signerEntries.size() == 8);
for (unsigned i = 0u; i < 8; ++i)
{
auto const& entry = signerEntries[i][sfSignerEntry.jsonName];
BEAST_EXPECT(entry.size() == 2);
BEAST_EXPECT(entry.isMember(sfAccount.jsonName));
BEAST_EXPECT(entry[sfSignerWeight.jsonName] == 1);
}
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);
}
}
void run()
{
testErrors();
testSignerLists();
testSignerListsV2();
}
};

View File

@@ -169,7 +169,7 @@ public:
// which case the hash wins.
auto const lines = env.rpc ("json", "account_lines",
R"({"account": ")" + alice.human() + R"(", )"
R"("ledger_hash": ")" + to_string(ledger4Info.hash) + R"("})"
R"("ledger_hash": ")" + to_string(ledger4Info.hash) + R"(", )"
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);
@@ -346,10 +346,523 @@ public:
RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]);
}
// test API V2
void testAccountLines2()
{
using namespace test::jtx;
Env env(*this);
{
// account_lines with mal-formed json2 (missing id field).
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0")"
" }");
BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0");
}
{
// account_lines with no account.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5)"
" }");
BEAST_EXPECT(lines[jss::result][jss::error_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");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// account_lines with a malformed account.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": )"
R"("n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"}]})");
BEAST_EXPECT(lines[jss::result][jss::error_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");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
Account const alice {"alice"};
{
// account_lines on an unfunded account.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
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] ==
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");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
env.fund(XRP(10000), alice);
env.close();
LedgerInfo const ledger3Info = env.closed()->info();
BEAST_EXPECT(ledger3Info.seq == 3);
{
// alice is funded but has no lines. An empty array is returned.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
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");
BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// Specify a ledger that doesn't exist.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("ledger_index": "nonsense"}]})");
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
"ledgerIndexMalformed");
BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// Specify a different ledger that doesn't exist.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("ledger_index": 50000}]})");
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
"ledgerNotFound");
BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
// Create trust lines to share with alice.
Account const gw1 {"gw1"};
env.fund(XRP(10000), gw1);
std::vector<IOU> gw1Currencies;
for (char c = 0; c <= ('Z' - 'A'); ++c)
{
// gw1 currencies have names "YAA" -> "YAZ".
gw1Currencies.push_back(
gw1[std::string("YA") + static_cast<char>('A' + c)]);
IOU const& gw1Currency = gw1Currencies.back();
// Establish trust lines.
env(trust(alice, gw1Currency(100 + c)));
env(pay(gw1, alice, gw1Currency(50 + c)));
}
env.close();
LedgerInfo const ledger4Info = env.closed()->info();
BEAST_EXPECT(ledger4Info.seq == 4);
// Add another set of trust lines in another ledger so we can see
// differences in historic ledgers.
Account const gw2 {"gw2"};
env.fund(XRP(10000), gw2);
// gw2 requires authorization.
env(fset(gw2, asfRequireAuth));
env.close();
std::vector<IOU> gw2Currencies;
for (char c = 0; c <= ('Z' - 'A'); ++c)
{
// gw2 currencies have names "ZAA" -> "ZAZ".
gw2Currencies.push_back(
gw2[std::string("ZA") + static_cast<char>('A' + c)]);
IOU const& gw2Currency = gw2Currencies.back();
// Establish trust lines.
env(trust(alice, gw2Currency(200 + c)));
env(trust(gw2, gw2Currency(0), alice, tfSetfAuth));
env.close();
env(pay(gw2, alice, gw2Currency(100 + c)));
env.close();
// Set flags on gw2 trust lines so we can look for them.
env(trust(alice, gw2Currency(0), gw2, tfSetNoRipple | tfSetFreeze));
}
env.close();
LedgerInfo const ledger58Info = env.closed()->info();
BEAST_EXPECT(ledger58Info.seq == 58);
// A re-usable test for historic ledgers.
auto testAccountLinesHistory =
[this, &env](Account const& account, LedgerInfo const& info, int count)
{
// Get account_lines by ledger index.
auto const linesSeq = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + account.human() + R"(", )"
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");
BEAST_EXPECT(linesSeq.isMember(jss::ripplerpc) && linesSeq[jss::ripplerpc] == "2.0");
BEAST_EXPECT(linesSeq.isMember(jss::id) && linesSeq[jss::id] == 5);
// Get account_lines by ledger hash.
auto const linesHash = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + account.human() + 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");
BEAST_EXPECT(linesHash.isMember(jss::ripplerpc) && linesHash[jss::ripplerpc] == "2.0");
BEAST_EXPECT(linesHash.isMember(jss::id) && linesHash[jss::id] == 5);
};
// Alice should have no trust lines in ledger 3.
testAccountLinesHistory (alice, ledger3Info, 0);
// Alice should have 26 trust lines in ledger 4.
testAccountLinesHistory (alice, ledger4Info, 26);
// Alice should have 52 trust lines in ledger 58.
testAccountLinesHistory (alice, ledger58Info, 52);
{
// Surprisingly, it's valid to specify both index and hash, in
// which case the hash wins.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("ledger_hash": ")" + to_string(ledger4Info.hash) + R"(", )"
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");
BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// alice should have 52 trust lines in the current ledger.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
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");
BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// alice should have 26 trust lines with gw1.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.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");
BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// Use a malformed peer.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("peer": )"
R"("n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"}]})");
BEAST_EXPECT(lines[jss::result][jss::error_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");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// A negative limit should fail.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("limit": -1}]})");
BEAST_EXPECT(lines[jss::result][jss::error_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");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// Limit the response to 1 trust line.
auto const linesA = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
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");
BEAST_EXPECT(linesA.isMember(jss::ripplerpc) && linesA[jss::ripplerpc] == "2.0");
BEAST_EXPECT(linesA.isMember(jss::id) && linesA[jss::id] == 5);
// Pick up from where the marker left off. We should get 51.
auto marker = linesA[jss::result][jss::marker].asString();
auto const linesB = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + 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");
BEAST_EXPECT(linesB.isMember(jss::ripplerpc) && linesB[jss::ripplerpc] == "2.0");
BEAST_EXPECT(linesB.isMember(jss::id) && linesB[jss::id] == 5);
// Go again from where the marker left off, but set a limit of 3.
auto const linesC = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("limit": 3, )"
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");
BEAST_EXPECT(linesC.isMember(jss::ripplerpc) && linesC[jss::ripplerpc] == "2.0");
BEAST_EXPECT(linesC.isMember(jss::id) && linesC[jss::id] == 5);
// Mess with the marker so it becomes bad and check for the error.
marker[5] = marker[5] == '7' ? '8' : '7';
auto const linesD = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("marker": ")" + marker + R"("}]})");
BEAST_EXPECT(linesD[jss::result][jss::error_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");
BEAST_EXPECT(linesD.isMember(jss::id) && linesD[jss::id] == 5);
}
{
// A non-string marker should also fail.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("marker": true}]})");
BEAST_EXPECT(lines[jss::result][jss::error_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");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// Check that the flags we expect from alice to gw2 are present.
auto const lines = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("limit": 1, )"
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);
BEAST_EXPECT(line[jss::peer_authorized].asBool() == true);
BEAST_EXPECT(lines.isMember(jss::jsonrpc) && lines[jss::jsonrpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::ripplerpc) && lines[jss::ripplerpc] == "2.0");
BEAST_EXPECT(lines.isMember(jss::id) && lines[jss::id] == 5);
}
{
// Check that the flags we expect from gw2 to alice are present.
auto const linesA = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + gw2.human() + R"(", )"
R"("limit": 1, )"
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);
BEAST_EXPECT(lineA[jss::authorized].asBool() == true);
BEAST_EXPECT(linesA.isMember(jss::jsonrpc) && linesA[jss::jsonrpc] == "2.0");
BEAST_EXPECT(linesA.isMember(jss::ripplerpc) && linesA[jss::ripplerpc] == "2.0");
BEAST_EXPECT(linesA.isMember(jss::id) && linesA[jss::id] == 5);
// Continue from the returned marker to make sure that works.
BEAST_EXPECT(linesA[jss::result].isMember(jss::marker));
auto const marker = linesA[jss::result][jss::marker].asString();
auto const linesB = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + gw2.human() + R"(", )"
R"("limit": 25, )"
R"("marker": ")" + marker + 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));
BEAST_EXPECT(linesB.isMember(jss::jsonrpc) && linesB[jss::jsonrpc] == "2.0");
BEAST_EXPECT(linesB.isMember(jss::ripplerpc) && linesB[jss::ripplerpc] == "2.0");
BEAST_EXPECT(linesB.isMember(jss::id) && linesB[jss::id] == 5);
}
}
// test API V2
void testAccountLineDelete2()
{
using namespace test::jtx;
Env env(*this);
// The goal here is to observe account_lines marker behavior if the
// entry pointed at by a returned marker is removed from the ledger.
//
// It isn't easy to explicitly delete a trust line, so we do so in a
// round-about fashion. It takes 4 actors:
// o Gateway gw1 issues USD
// o alice offers to buy 100 USD for 100 XRP.
// o becky offers to sell 100 USD for 100 XRP.
// There will now be an inferred trustline between alice and gw1.
// o alice pays her 100 USD to cheri.
// alice should now have no USD and no trustline to gw1.
Account const alice {"alice"};
Account const becky {"becky"};
Account const cheri {"cheri"};
Account const gw1 {"gw1"};
Account const gw2 {"gw2"};
env.fund(XRP(10000), alice, becky, cheri, gw1, gw2);
env.close();
auto const USD = gw1["USD"];
auto const EUR = gw2["EUR"];
env(trust(alice, EUR(200)));
env(trust(becky, USD(200)));
env(trust(cheri, USD(200)));
env.close();
// becky gets 100 USD from gw1.
env(pay(gw1, becky, USD(100)));
env.close();
// alice offers to buy 100 USD for 100 XRP.
env(offer(alice, USD(100), XRP(100)));
env.close();
// becky offers to buy 100 XRP for 100 USD.
env(offer(becky, XRP(100), USD(100)));
env.close();
// Get account_lines for alice. Limit at 1, so we get a marker.
auto const linesBeg = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("limit": 1}]})");
BEAST_EXPECT(linesBeg[jss::result][jss::lines][0u][jss::currency] == "EUR");
BEAST_EXPECT(linesBeg[jss::result].isMember(jss::marker));
BEAST_EXPECT(linesBeg.isMember(jss::jsonrpc) && linesBeg[jss::jsonrpc] == "2.0");
BEAST_EXPECT(linesBeg.isMember(jss::ripplerpc) && linesBeg[jss::ripplerpc] == "2.0");
BEAST_EXPECT(linesBeg.isMember(jss::id) && linesBeg[jss::id] == 5);
// alice pays 100 USD to cheri.
env(pay(alice, cheri, USD(100)));
env.close();
// Since alice paid all her USD to cheri, alice should no longer
// have a trust line to gw1. So the old marker should now be invalid.
auto const linesEnd = env.rpc ("json2", "{ "
R"("method" : "account_lines",)"
R"("jsonrpc" : "2.0",)"
R"("ripplerpc" : "2.0",)"
R"("id" : 5,)"
R"("params": [ )"
R"({"account": ")" + alice.human() + R"(", )"
R"("marker": ")" +
linesBeg[jss::result][jss::marker].asString() + R"("}]})");
BEAST_EXPECT(linesEnd[jss::result][jss::error_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");
BEAST_EXPECT(linesEnd.isMember(jss::id) && linesEnd[jss::id] == 5);
}
void run ()
{
testAccountLines();
testAccountLineDelete();
testAccountLines2();
testAccountLineDelete2();
}
};

View File

@@ -72,6 +72,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
@@ -106,8 +112,14 @@ public:
}
// RPC unsubscribe
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
auto jv = wsc->invoke("unsubscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
void
@@ -143,6 +155,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
@@ -181,8 +199,14 @@ public:
}
// RPC unsubscribe
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
auto jv = wsc->invoke("unsubscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
void
@@ -210,6 +234,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
@@ -254,8 +284,14 @@ public:
}
// RPC unsubscribe
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
auto jv = wsc->invoke("unsubscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
void
@@ -292,6 +328,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
@@ -344,8 +386,14 @@ public:
}
// RPC unsubscribe
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
auto jv = wsc->invoke("unsubscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
void
@@ -382,6 +430,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
@@ -441,8 +495,14 @@ public:
}
// RPC unsubscribe
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
auto jv = wsc->invoke("unsubscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
void
@@ -496,6 +556,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
@@ -563,8 +629,14 @@ public:
}
// RPC unsubscribe
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
auto jv = wsc->invoke("unsubscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
void
@@ -603,6 +675,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
@@ -681,8 +759,14 @@ public:
}
// RPC unsubscribe
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
auto jv = wsc->invoke("unsubscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
void
@@ -739,6 +823,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
@@ -833,8 +923,14 @@ public:
}
// RPC unsubscribe
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
auto jv = wsc->invoke("unsubscribe", books);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
void
@@ -863,6 +959,12 @@ public:
}
auto jv = wsc->invoke("subscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
if(! BEAST_EXPECT(jv[jss::status] == "success"))
return;
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
@@ -886,7 +988,15 @@ public:
jvParams[jss::ledger_index] = "validated";
jvParams[jss::taker_gets][jss::currency] = "USD";
jvParams[jss::taker_gets][jss::issuer] = gw.human();
auto jrr = wsc->invoke("book_offers", jvParams)[jss::result];
auto jv = wsc->invoke("book_offers", jvParams);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
auto jrr = jv[jss::result];
BEAST_EXPECT(jrr[jss::offers].isArray());
BEAST_EXPECT(jrr[jss::offers].size() == 1);
@@ -927,7 +1037,14 @@ public:
t[jss::TakerPays] == XRP(2000).value().getJson(0);
}));
jrr = wsc->invoke("book_offers", jvParams)[jss::result];
jv = wsc->invoke("book_offers", jvParams);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
jrr = jv[jss::result];
BEAST_EXPECT(jrr[jss::offers].isArray());
BEAST_EXPECT(jrr[jss::offers].size() == 2);
@@ -946,8 +1063,14 @@ public:
BEAST_EXPECT(jrNextOffer[jss::owner_funds] == "50");
BEAST_EXPECT(jrNextOffer[jss::quality] == "400000000");
BEAST_EXPECT(wsc->invoke("unsubscribe",
books)[jss::status] == "success");
jv = wsc->invoke("unsubscribe", books);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
void

View File

@@ -86,6 +86,12 @@ public:
auto jv = wsc->invoke("gateway_balances", qry);
expect(jv[jss::status] == "success");
if (wsc->version() == 2)
{
expect(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
expect(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
expect(jv.isMember(jss::id) && jv[jss::id] == 5);
}
auto const& result = jv[jss::result];
expect(result[jss::account] == alice.human());

View File

@@ -45,6 +45,12 @@ public:
jv[jss::streams].append("transactions");
jv = wsc->invoke("subscribe", jv);
BEAST_EXPECT(jv[jss::status] == "success");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
}
{
@@ -54,6 +60,12 @@ public:
payment[jss::tx_json] = pay("alice", "bob", XRP(1));
payment[jss::tx_json][sfLastLedgerSequence.fieldName] = 1;
auto jv = wsc->invoke("submit", payment);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
"tefMAX_LEDGER");
@@ -62,6 +74,12 @@ public:
payment[jss::tx_json][sfSequence.fieldName] =
env.seq("alice") - 1;
jv = wsc->invoke("submit", payment);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
"tefPAST_SEQ");
@@ -69,6 +87,12 @@ public:
payment[jss::tx_json][sfSequence.fieldName] =
env.seq("alice") + 1;
jv = wsc->invoke("submit", payment);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
"terPRE_SEQ");
@@ -76,6 +100,12 @@ public:
payment[jss::tx_json][sfSequence.fieldName] =
env.seq("alice");
jv = wsc->invoke("submit", payment);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
"tesSUCCESS");
@@ -84,6 +114,12 @@ public:
// Finalize transactions
jv = wsc->invoke("ledger_accept");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result].isMember(
jss::ledger_current_index));
}
@@ -115,6 +151,12 @@ public:
jv[jss::streams] = Json::arrayValue;
jv[jss::streams].append("transactions");
jv = wsc->invoke("unsubscribe", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
}
@@ -145,6 +187,12 @@ public:
jv[jss::secret] = toBase58(generateSeed("alice"));
jv[jss::tx_json] = pay("alice", "bob", XRP(1));
jv = wsc->invoke("submit", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
"tesSUCCESS");
@@ -163,6 +211,12 @@ public:
jv[jss::ledger_index_max] = -1;
wsc = makeWSClient(env.app().config());
jv = wsc->invoke("account_tx", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
// Check balance
auto ff = jv[jss::result][jss::transactions][0u][jss::meta]
@@ -189,11 +243,23 @@ public:
jv[jss::secret] = toBase58(generateSeed("alice"));
jv[jss::tx_json] = pay("alice", "bob", XRP(1));
jv = wsc->invoke("submit", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
"tesSUCCESS");
// Finalize transaction
jv = wsc->invoke("ledger_accept");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result].isMember(
jss::ledger_current_index));
@@ -208,13 +274,26 @@ public:
jv[jss::streams] = Json::arrayValue;
jv[jss::streams].append("ledger");
jv = wsc->invoke("subscribe", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
// Close ledgers
for(auto i = 0; i < 8; ++i)
{
BEAST_EXPECT(wsc->invoke("ledger_accept")[jss::result].
auto jv = wsc->invoke("ledger_accept");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result].
isMember(jss::ledger_current_index));
// Wait for the jobqueue to process everything
@@ -233,6 +312,12 @@ public:
jv[jss::streams] = Json::arrayValue;
jv[jss::streams].append("ledger");
jv = wsc->invoke("unsubscribe", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
}
@@ -246,13 +331,26 @@ public:
jv[jss::streams] = Json::arrayValue;
jv[jss::streams].append("ledger");
jv = wsc->invoke("subscribe", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
// Close ledgers
for (auto i = 0; i < 2; ++i)
{
BEAST_EXPECT(wsc->invoke("ledger_accept")[jss::result].
auto jv = wsc->invoke("ledger_accept");
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result].
isMember(jss::ledger_current_index));
// Wait for the jobqueue to process everything
@@ -271,6 +369,12 @@ public:
jv[jss::streams] = Json::arrayValue;
jv[jss::streams].append("ledger");
jv = wsc->invoke("unsubscribe", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
}
@@ -283,6 +387,12 @@ public:
jv[jss::ledger_index_max] = -1;
wsc = makeWSClient(env.app().config());
jv = wsc->invoke("account_tx", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
// Check balance
auto ff = jv[jss::result][jss::transactions][0u][jss::meta]
@@ -310,6 +420,12 @@ public:
jv[jss::accounts_proposed].append(
Account("alice").human());
jv = wsc->invoke("subscribe", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -320,6 +436,12 @@ public:
jv[jss::tx_json] = fset("alice", 0);
jv[jss::tx_json][jss::Fee] = 10;
jv = wsc->invoke("submit", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
"tesSUCCESS");
}
@@ -341,6 +463,12 @@ public:
jv[jss::accounts_proposed].append(
Account("alice").human());
jv = wsc->invoke("unsubscribe", jv);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
}

View File

@@ -42,6 +42,12 @@ public:
stream[jss::streams] = Json::arrayValue;
stream[jss::streams].append("server");
auto jv = wsc->invoke("subscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -63,6 +69,12 @@ public:
{
// RPC unsubscribe
auto jv = wsc->invoke("unsubscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -91,6 +103,12 @@ public:
stream[jss::streams] = Json::arrayValue;
stream[jss::streams].append("ledger");
auto jv = wsc->invoke("subscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 2);
}
@@ -120,6 +138,12 @@ public:
// RPC unsubscribe
auto jv = wsc->invoke("unsubscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -136,6 +160,12 @@ public:
stream[jss::streams] = Json::arrayValue;
stream[jss::streams].append("transactions");
auto jv = wsc->invoke("subscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -186,6 +216,12 @@ public:
{
// RPC unsubscribe
auto jv = wsc->invoke("unsubscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -195,6 +231,12 @@ public:
stream[jss::accounts] = Json::arrayValue;
stream[jss::accounts].append(Account("alice").human());
auto jv = wsc->invoke("subscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -228,6 +270,12 @@ public:
// RPC unsubscribe
auto jv = wsc->invoke("unsubscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -243,11 +291,23 @@ public:
stream[jss::streams] = Json::arrayValue;
stream[jss::streams].append("manifests");
auto jv = wsc->invoke("subscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
// RPC unsubscribe
auto jv = wsc->invoke("unsubscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -295,6 +355,12 @@ public:
stream[jss::streams] = Json::arrayValue;
stream[jss::streams].append("validations");
auto jv = wsc->invoke("subscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}
@@ -323,6 +389,12 @@ public:
// RPC unsubscribe
auto jv = wsc->invoke("unsubscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::status] == "success");
}