21#include <test/jtx/JSONRPCClient.h>
22#include <test/jtx/WSClient.h>
23#include <test/jtx/envconfig.h>
24#include <xrpld/app/ledger/LedgerMaster.h>
25#include <xrpld/app/misc/LoadFeeTrack.h>
26#include <xrpld/app/misc/NetworkOPs.h>
27#include <xrpld/rpc/ServerHandler.h>
28#include <xrpl/basics/base64.h>
29#include <xrpl/beast/test/yield_to.h>
30#include <xrpl/json/json_reader.h>
32#include <boost/algorithm/string/predicate.hpp>
33#include <boost/asio.hpp>
34#include <boost/asio/ssl.hpp>
35#include <boost/beast/core/multi_buffer.hpp>
36#include <boost/beast/http.hpp>
49 class myFields :
public boost::beast::http::fields
57 bool credentials =
false)
59 auto const section_name =
60 boost::starts_with(proto,
"h") ?
"port_rpc" :
"port_ws";
63 p->overwrite(section_name,
"protocol", proto);
65 p->overwrite(section_name,
"admin",
"");
69 (*p)[section_name].set(
"admin_password",
"p");
70 (*p)[section_name].set(
"admin_user",
"u");
74 boost::starts_with(proto,
"h") ?
"port_ws" :
"port_rpc",
76 boost::starts_with(proto,
"h") ?
"ws" :
"http");
83 (*p)[
"server"].append(
"port_alt");
85 (*p)[
"port_alt"].set(
"port",
"7099");
86 (*p)[
"port_alt"].set(
"protocol",
"http");
97 using namespace boost::beast::http;
98 request<string_body> req;
103 req.insert(
"User-Agent",
"test");
104 req.method(boost::beast::http::verb::get);
105 req.insert(
"Upgrade",
"websocket");
117 req.insert(
"Sec-WebSocket-Version",
"13");
118 req.insert(boost::beast::http::field::connection,
"upgrade");
130 using namespace boost::beast::http;
131 request<string_body> req;
135 for (
auto const& f : fields)
136 req.insert(f.name(), f.value());
138 req.insert(
"User-Agent",
"test");
141 req.method(boost::beast::http::verb::get);
145 req.method(boost::beast::http::verb::post);
146 req.insert(
"Content-Type",
"application/json; charset=UTF-8");
149 req.prepare_payload();
156 boost::asio::yield_context& yield,
157 boost::beast::http::request<boost::beast::http::string_body>&& req,
161 boost::beast::http::response<boost::beast::http::string_body>& resp,
162 boost::system::error_code& ec)
165 using namespace boost::beast::http;
167 ip::tcp::resolver r{ios};
168 boost::beast::multi_buffer sb;
170 auto it = r.async_resolve(
178 ssl::context ctx{ssl::context::sslv23};
179 ctx.set_verify_mode(ssl::verify_none);
180 ssl::stream<ip::tcp::socket> ss{ios, ctx};
181 async_connect(ss.next_layer(), it, yield[ec]);
184 ss.async_handshake(ssl::stream_base::client, yield[ec]);
187 boost::beast::http::async_write(ss, req, yield[ec]);
190 async_read(ss, sb, resp, yield[ec]);
196 ip::tcp::socket sock{ios};
197 async_connect(sock, it, yield[ec]);
200 boost::beast::http::async_write(sock, req, yield[ec]);
203 async_read(sock, sb, resp, yield[ec]);
214 boost::asio::yield_context& yield,
216 boost::beast::http::response<boost::beast::http::string_body>& resp,
217 boost::system::error_code& ec)
223 yield,
makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec);
230 boost::asio::yield_context& yield,
232 boost::beast::http::response<boost::beast::http::string_body>& resp,
233 boost::system::error_code& ec,
257 bool subobject =
false)
264 jp[
"admin_user"] = user;
269 jpi[
"admin_password"] = password;
270 jp[
"admin_password"] = jpi;
274 jp[
"admin_password"] = password;
278 if (boost::starts_with(proto,
"h"))
281 jrr = jrc->invoke(
"ledger_accept", jp);
286 jrr = wsc->invoke(
"ledger_accept", jp);
299 testcase <<
"Admin request over " << proto <<
", config "
300 << (admin ?
"enabled" :
"disabled") <<
", credentials "
301 << (credentials ?
"" :
"not ") <<
"set";
306 auto const proto_ws = boost::starts_with(proto,
"w");
311 if (admin && credentials)
313 auto const user = env.
app()
314 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
317 auto const password =
319 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
324 env, proto, *user, *password +
"_")[jss::result];
326 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
328 jrr[
"error_message"] == proto_ws
330 :
"You don't have permission for this command.");
334 env, proto, *user, *password,
true)[jss::result];
336 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
338 jrr[
"error_message"] == proto_ws
340 :
"You don't have permission for this command.");
344 env, proto, *user +
"_", *password)[jss::result];
346 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
348 jrr[
"error_message"] == proto_ws
350 :
"You don't have permission for this command.");
355 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
357 jrr[
"error_message"] == proto_ws
359 :
"You don't have permission for this command.");
363 BEAST_EXPECT(jrr[
"status"] ==
"success");
369 BEAST_EXPECT(jrr[
"status"] ==
"success");
373 BEAST_EXPECT(jrr[
"status"] ==
"success");
380 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
382 jrr[
"error_message"] == proto_ws
384 :
"You don't have permission for this command.");
391 testcase(
"WS client to http server fails");
394 cfg->section(
"port_ws").
set(
"protocol",
"http,https");
400 boost::system::error_code ec;
401 boost::beast::http::response<boost::beast::http::string_body> resp;
403 if (!BEAST_EXPECTS(!ec, ec.message()))
406 resp.result() == boost::beast::http::status::unauthorized);
411 boost::system::error_code ec;
412 boost::beast::http::response<boost::beast::http::string_body> resp;
414 if (!BEAST_EXPECTS(!ec, ec.message()))
417 resp.result() == boost::beast::http::status::unauthorized);
427 cfg->section(
"port_rpc").
set(
"protocol",
"ws2,wss2");
428 cfg->section(
"port_ws").
set(
"protocol",
"http");
434 boost::system::error_code ec;
435 boost::beast::http::response<boost::beast::http::string_body> resp;
437 if (!BEAST_EXPECTS(!ec, ec.message()))
439 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
444 boost::system::error_code ec;
445 boost::beast::http::response<boost::beast::http::string_body> resp;
447 if (!BEAST_EXPECTS(!ec, ec.message()))
449 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
456 testcase(
"Partial WS upgrade request");
459 using namespace boost::beast::http;
461 cfg->section(
"port_ws").
set(
"protocol",
"ws2");
469 boost::system::error_code ec;
470 response<string_body> resp;
474 auto req_string = boost::lexical_cast<std::string>(req);
475 req_string.erase(req_string.find_last_of(
"13"), std::string::npos);
478 ip::tcp::resolver r{ios};
479 boost::beast::multi_buffer sb;
481 auto it = r.async_resolve(
483 if (!BEAST_EXPECTS(!ec, ec.message()))
486 ip::tcp::socket sock{ios};
487 async_connect(sock, it, yield[ec]);
488 if (!BEAST_EXPECTS(!ec, ec.message()))
490 async_write(sock, boost::asio::buffer(req_string), yield[ec]);
491 if (!BEAST_EXPECTS(!ec, ec.message()))
495 async_read(sock, sb, resp, yield[ec]);
503 boost::asio::yield_context& yield)
508 testcase <<
"Connect fails: " << client_protocol <<
" client to "
509 << server_protocol <<
" server";
513 boost::beast::http::response<boost::beast::http::string_body> resp;
514 boost::system::error_code ec;
515 if (boost::starts_with(client_protocol,
"h"))
517 doHTTPRequest(env, yield, client_protocol ==
"https", resp, ec);
525 client_protocol ==
"wss" || client_protocol ==
"wss2",
533 testAuth(
bool secure, boost::asio::yield_context& yield)
535 testcase <<
"Server with authorization, "
536 << (secure ?
"secure" :
"non-secure");
538 using namespace test::jtx;
540 (*cfg)[
"port_rpc"].set(
"user",
"me");
541 (*cfg)[
"port_rpc"].set(
"password",
"secret");
542 (*cfg)[
"port_rpc"].set(
543 "protocol", secure ?
"https" :
"http");
545 (*cfg)[
"port_ws"].
set(
"protocol",
"http,ws");
550 jr[jss::method] =
"server_info";
551 boost::beast::http::response<boost::beast::http::string_body> resp;
552 boost::system::error_code ec;
554 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
557 auth.insert(
"Authorization",
"");
559 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
561 auth.set(
"Authorization",
"Basic NOT-VALID");
563 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
567 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
569 auto const user = env.
app()
581 auth.set(
"Authorization",
"Basic " + user +
":" +
pass);
583 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
588 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
589 BEAST_EXPECT(!resp.body().empty());
595 testcase <<
"Server with connection limit of " << limit;
597 using namespace test::jtx;
599 using namespace boost::beast::http;
610 boost::system::error_code ec;
612 ip::tcp::resolver r{ios};
615 jr[jss::method] =
"server_info";
617 auto it = r.async_resolve(
623 int connectionCount{1};
631 int testTo = (limit == 0) ? 50 : limit + 1;
632 while (connectionCount < testTo)
635 ip::tcp::socket{ios}, boost::beast::multi_buffer{}));
636 async_connect(clients.
back().first, it, yield[ec]);
639 async_write(clients.
back().first, req, yield[ec]);
645 for (
auto& [soc, buf] : clients)
647 boost::beast::http::response<boost::beast::http::string_body> resp;
648 async_read(soc, buf, resp, yield[ec]);
653 (limit == 0 || readCount < limit - 1) ? (!ec) :
bool(ec));
660 testcase(
"Connection with WS handoff");
662 using namespace test::jtx;
664 (*cfg)[
"port_ws"].set(
"protocol",
"wss");
672 boost::beast::http::response<boost::beast::http::string_body> resp;
673 boost::system::error_code ec;
676 resp.result() == boost::beast::http::status::switching_protocols);
678 resp.find(
"Upgrade") != resp.end() &&
679 resp[
"Upgrade"] ==
"websocket");
681 resp.find(
"Connection") != resp.end() &&
682 resp[
"Connection"] ==
"upgrade");
688 testcase(
"Connection to port with no RPC enabled");
690 using namespace test::jtx;
697 boost::beast::http::response<boost::beast::http::string_body> resp;
698 boost::system::error_code ec;
709 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
710 BEAST_EXPECT(resp.body() ==
"Forbidden\r\n");
716 testcase(
"WS client sends assorted input");
718 using namespace test::jtx;
720 using namespace boost::beast::http;
727 boost::system::error_code ec;
730 ip::tcp::resolver r{ios};
732 auto it = r.async_resolve(
734 if (!BEAST_EXPECT(!ec))
737 ip::tcp::socket sock{ios};
738 async_connect(sock, it, yield[ec]);
739 if (!BEAST_EXPECT(!ec))
742 boost::beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
747 ws.async_write_some(
true, buffer(req), yield[ec]);
748 if (!BEAST_EXPECT(!ec))
751 boost::beast::multi_buffer sb;
752 ws.async_read(sb, yield[ec]);
753 if (!BEAST_EXPECT(!ec))
758 if (!BEAST_EXPECT(jr.
parse(
759 boost::lexical_cast<std::string>(
760 boost::beast::make_printable(sb.data())),
763 sb.consume(sb.size());
768 auto resp = sendAndParse(
"NOT JSON");
770 resp.isMember(jss::error) && resp[jss::error] ==
"jsonInvalid");
771 BEAST_EXPECT(!resp.isMember(jss::status));
776 jv[jss::command] =
"foo";
777 jv[jss::method] =
"bar";
780 resp.isMember(jss::error) &&
781 resp[jss::error] ==
"missingCommand");
783 resp.isMember(jss::status) && resp[jss::status] ==
"error");
788 jv[jss::command] =
"ping";
791 resp.isMember(jss::status) && resp[jss::status] ==
"success");
793 resp.isMember(jss::result) &&
794 resp[jss::result].isMember(jss::role) &&
795 resp[jss::result][jss::role] ==
"admin");
803 "Status request over WS and RPC with/without Amendment Warning");
806 using namespace boost::beast::http;
811 cfg->section(
"port_rpc").
set(
"protocol",
"http");
825 auto si = env.
rpc(
"server_info")[jss::result];
826 BEAST_EXPECT(si.isMember(jss::info));
827 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
830 BEAST_EXPECT(!si.isMember(jss::warnings));
834 si = env.
rpc(
"server_state")[jss::result];
835 BEAST_EXPECT(si.isMember(jss::state));
836 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
839 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
845 boost::system::error_code ec;
846 response<string_body> resp;
857 if (!BEAST_EXPECTS(!ec, ec.message()))
859 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
861 resp.body().find(
"connectivity is working.") != std::string::npos);
873 si = env.
rpc(
"server_info")[jss::result];
874 BEAST_EXPECT(si.isMember(jss::info));
875 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
877 si[jss::info].isMember(jss::warnings) &&
878 si[jss::info][jss::warnings].isArray() &&
879 si[jss::info][jss::warnings].size() == 1 &&
880 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
885 si = env.
rpc(
"server_state")[jss::result];
886 BEAST_EXPECT(si.isMember(jss::state));
887 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
889 si[jss::state].isMember(jss::warnings) &&
890 si[jss::state][jss::warnings].isArray() &&
891 si[jss::state][jss::warnings].size() == 1 &&
892 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
905 if (!BEAST_EXPECTS(!ec, ec.message()))
907 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
909 resp.body().find(
"connectivity is working.") != std::string::npos);
923 if (!BEAST_EXPECTS(!ec, ec.message()))
925 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
927 resp.body().find(
"connectivity is working.") != std::string::npos);
933 testcase(
"Status request over WS and RPC with/without Amendment Block");
936 using namespace boost::beast::http;
941 cfg->section(
"port_rpc").
set(
"protocol",
"http");
955 auto si = env.
rpc(
"server_info")[jss::result];
956 BEAST_EXPECT(si.isMember(jss::info));
957 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
960 BEAST_EXPECT(!si.isMember(jss::warnings));
964 si = env.
rpc(
"server_state")[jss::result];
965 BEAST_EXPECT(si.isMember(jss::state));
966 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
969 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
975 boost::system::error_code ec;
976 response<string_body> resp;
987 if (!BEAST_EXPECTS(!ec, ec.message()))
989 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
991 resp.body().find(
"connectivity is working.") != std::string::npos);
1003 si = env.
rpc(
"server_info")[jss::result];
1004 BEAST_EXPECT(si.isMember(jss::info));
1006 si[jss::info].isMember(jss::amendment_blocked) &&
1007 si[jss::info][jss::amendment_blocked] ==
true);
1009 si[jss::info].isMember(jss::warnings) &&
1010 si[jss::info][jss::warnings].isArray() &&
1011 si[jss::info][jss::warnings].size() == 1 &&
1012 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
1016 si = env.
rpc(
"server_state")[jss::result];
1018 si[jss::state].isMember(jss::amendment_blocked) &&
1019 si[jss::state][jss::amendment_blocked] ==
true);
1021 si[jss::state].isMember(jss::warnings) &&
1022 si[jss::state][jss::warnings].isArray() &&
1023 si[jss::state][jss::warnings].size() == 1 &&
1024 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
1038 if (!BEAST_EXPECTS(!ec, ec.message()))
1040 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
1042 resp.body().find(
"connectivity is working.") != std::string::npos);
1055 if (!BEAST_EXPECTS(!ec, ec.message()))
1058 resp.result() == boost::beast::http::status::internal_server_error);
1060 resp.body().find(
"cannot accept clients:") != std::string::npos);
1062 resp.body().find(
"Server version too old") != std::string::npos);
1068 testcase(
"RPC client sends assorted input");
1070 using namespace test::jtx;
1073 boost::system::error_code ec;
1075 boost::beast::http::response<boost::beast::http::string_body> resp;
1078 resp.result() == boost::beast::http::status::bad_request);
1079 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1083 boost::beast::http::response<boost::beast::http::string_body> resp;
1088 resp.result() == boost::beast::http::status::bad_request);
1089 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1093 boost::beast::http::response<boost::beast::http::string_body> resp;
1098 resp.result() == boost::beast::http::status::bad_request);
1099 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1103 boost::beast::http::response<boost::beast::http::string_body> resp;
1110 resp.result() == boost::beast::http::status::bad_request);
1111 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1115 boost::beast::http::response<boost::beast::http::string_body> resp;
1117 jv[jss::method] =
"batch";
1118 jv[jss::params] = 2;
1121 resp.result() == boost::beast::http::status::bad_request);
1122 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1126 boost::beast::http::response<boost::beast::http::string_body> resp;
1128 jv[jss::method] =
"batch";
1130 jv[jss::params][
"invalid"] = 3;
1133 resp.result() == boost::beast::http::status::bad_request);
1134 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1139 boost::beast::http::response<boost::beast::http::string_body> resp;
1143 resp.result() == boost::beast::http::status::bad_request);
1144 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1148 boost::beast::http::response<boost::beast::http::string_body> resp;
1149 jv[jss::method] = 1;
1152 resp.result() == boost::beast::http::status::bad_request);
1153 BEAST_EXPECT(resp.body() ==
"method is not string\r\n");
1157 boost::beast::http::response<boost::beast::http::string_body> resp;
1158 jv[jss::method] =
"";
1161 resp.result() == boost::beast::http::status::bad_request);
1162 BEAST_EXPECT(resp.body() ==
"method is empty\r\n");
1166 boost::beast::http::response<boost::beast::http::string_body> resp;
1167 jv[jss::method] =
"some_method";
1168 jv[jss::params] =
"params";
1171 resp.result() == boost::beast::http::status::bad_request);
1172 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1176 boost::beast::http::response<boost::beast::http::string_body> resp;
1178 jv[jss::params][0u] =
"not an object";
1181 resp.result() == boost::beast::http::status::bad_request);
1182 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1189 testcase(
"Server status not okay");
1191 using namespace test::jtx;
1193 cfg->ELB_SUPPORT =
true;
1200 boost::beast::http::response<boost::beast::http::string_body> resp;
1201 boost::system::error_code ec;
1204 resp.result() == boost::beast::http::status::internal_server_error);
1205 std::regex body{
"Server cannot accept clients"};
1213 for (
auto it : {
"http",
"ws",
"ws2"})
1220 yield_to([&](boost::asio::yield_context& yield) {
1249BEAST_DEFINE_TESTSUITE(ServerStatus, server,
ripple);
Unserialize a JSON document into a Value.
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Value & append(const Value &value)
Append value to array at the end.
Mix-in to support tests using asio coroutines.
void yield_to(F0 &&f0, FN &&... fn)
Run one or more functions, each in a coroutine.
boost::asio::io_service & get_io_service()
Return the io_service associated with the object.
void pass()
Record a successful test condition.
testcase_t testcase
Memberspace for declaring test cases.
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual NetworkOPs & getOPs()=0
virtual LedgerMaster & getLedgerMaster()=0
Section & section(std::string const &name)
Returns the section with the given name.
virtual void setAmendmentWarned()=0
virtual Json::Value getConsensusInfo()=0
virtual void setAmendmentBlocked()=0
virtual bool beginConsensus(uint256 const &netLCL, std::unique_ptr< std::stringstream > const &clog)=0
std::optional< T > get(std::string const &name) const
void testAmendmentBlock(boost::asio::yield_context &yield)
void testCantConnect(std::string const &client_protocol, std::string const &server_protocol, boost::asio::yield_context &yield)
void testAuth(bool secure, boost::asio::yield_context &yield)
auto makeHTTPRequest(std::string const &host, uint16_t port, std::string const &body, myFields const &fields)
void testRPCRequests(boost::asio::yield_context &yield)
void testStatusRequest(boost::asio::yield_context &yield)
void testAmendmentWarning(boost::asio::yield_context &yield)
void testTruncatedWSUpgrade(boost::asio::yield_context &yield)
auto makeConfig(std::string const &proto, bool admin=true, bool credentials=false)
void doRequest(boost::asio::yield_context &yield, boost::beast::http::request< boost::beast::http::string_body > &&req, std::string const &host, uint16_t port, bool secure, boost::beast::http::response< boost::beast::http::string_body > &resp, boost::system::error_code &ec)
void doWSRequest(test::jtx::Env &env, boost::asio::yield_context &yield, bool secure, boost::beast::http::response< boost::beast::http::string_body > &resp, boost::system::error_code &ec)
void testWSRequests(boost::asio::yield_context &yield)
void testLimit(boost::asio::yield_context &yield, int limit)
void testWSHandoff(boost::asio::yield_context &yield)
void testNoRPC(boost::asio::yield_context &yield)
void testStatusNotOkay(boost::asio::yield_context &yield)
void testWSClientToHttpServer(boost::asio::yield_context &yield)
void run() override
Runs the suite.
auto makeAdminRequest(jtx::Env &env, std::string const &proto, std::string const &user, std::string const &password, bool subobject=false)
auto makeWSUpgrade(std::string const &host, uint16_t port)
void doHTTPRequest(test::jtx::Env &env, boost::asio::yield_context &yield, bool secure, boost::beast::http::response< boost::beast::http::string_body > &resp, boost::system::error_code &ec, std::string const &body="", myFields const &fields={})
void testAdminRequest(std::string const &proto, bool admin, bool credentials)
A transaction testing environment.
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
T emplace_back(T... args)
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
const char * getEnvLocalhostAddr()
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
std::unique_ptr< AbstractClient > makeJSONRPCClient(Config const &cfg, unsigned rpc_version)
Returns a client using JSON-RPC over HTTP/S.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
@ warnRPC_UNSUPPORTED_MAJORITY
@ warnRPC_AMENDMENT_BLOCKED
bool set(T &target, std::string const &name, Section const §ion)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
std::string base64_encode(std::uint8_t const *data, std::size_t len)
std::string to_string(base_uint< Bits, Tag > const &a)
T regex_search(T... args)