21#include <test/jtx/JSONRPCClient.h>
22#include <test/jtx/WSClient.h>
23#include <test/jtx/envconfig.h>
25#include <xrpld/app/ledger/LedgerMaster.h>
26#include <xrpld/app/misc/LoadFeeTrack.h>
27#include <xrpld/app/misc/NetworkOPs.h>
28#include <xrpld/rpc/ServerHandler.h>
30#include <xrpl/basics/base64.h>
31#include <xrpl/beast/test/yield_to.h>
32#include <xrpl/json/json_reader.h>
34#include <boost/algorithm/string/predicate.hpp>
35#include <boost/asio.hpp>
36#include <boost/asio/ssl.hpp>
37#include <boost/beast/core/multi_buffer.hpp>
38#include <boost/beast/http.hpp>
51 class myFields :
public boost::beast::http::fields
59 bool credentials =
false)
61 auto const section_name =
62 boost::starts_with(proto,
"h") ?
"port_rpc" :
"port_ws";
65 p->overwrite(section_name,
"protocol", proto);
67 p->overwrite(section_name,
"admin",
"");
71 (*p)[section_name].set(
"admin_password",
"p");
72 (*p)[section_name].set(
"admin_user",
"u");
76 boost::starts_with(proto,
"h") ?
"port_ws" :
"port_rpc",
78 boost::starts_with(proto,
"h") ?
"ws" :
"http");
85 (*p)[
"server"].append(
"port_alt");
87 (*p)[
"port_alt"].set(
"port",
"7099");
88 (*p)[
"port_alt"].set(
"protocol",
"http");
99 using namespace boost::beast::http;
100 request<string_body> req;
105 req.insert(
"User-Agent",
"test");
106 req.method(boost::beast::http::verb::get);
107 req.insert(
"Upgrade",
"websocket");
119 req.insert(
"Sec-WebSocket-Version",
"13");
120 req.insert(boost::beast::http::field::connection,
"upgrade");
132 using namespace boost::beast::http;
133 request<string_body> req;
137 for (
auto const& f : fields)
138 req.insert(f.name(), f.value());
140 req.insert(
"User-Agent",
"test");
143 req.method(boost::beast::http::verb::get);
147 req.method(boost::beast::http::verb::post);
148 req.insert(
"Content-Type",
"application/json; charset=UTF-8");
151 req.prepare_payload();
158 boost::asio::yield_context& yield,
159 boost::beast::http::request<boost::beast::http::string_body>&& req,
163 boost::beast::http::response<boost::beast::http::string_body>& resp,
164 boost::system::error_code& ec)
167 using namespace boost::beast::http;
169 ip::tcp::resolver r{ios};
170 boost::beast::multi_buffer sb;
172 auto it = r.async_resolve(
180 ssl::context ctx{ssl::context::sslv23};
181 ctx.set_verify_mode(ssl::verify_none);
182 ssl::stream<ip::tcp::socket> ss{ios, ctx};
183 async_connect(ss.next_layer(), it, yield[ec]);
186 ss.async_handshake(ssl::stream_base::client, yield[ec]);
189 boost::beast::http::async_write(ss, req, yield[ec]);
192 async_read(ss, sb, resp, yield[ec]);
198 ip::tcp::socket sock{ios};
199 async_connect(sock, it, yield[ec]);
202 boost::beast::http::async_write(sock, req, yield[ec]);
205 async_read(sock, sb, resp, yield[ec]);
216 boost::asio::yield_context& yield,
218 boost::beast::http::response<boost::beast::http::string_body>& resp,
219 boost::system::error_code& ec)
225 yield,
makeWSUpgrade(*ip, *port), *ip, *port, secure, resp, ec);
232 boost::asio::yield_context& yield,
234 boost::beast::http::response<boost::beast::http::string_body>& resp,
235 boost::system::error_code& ec,
259 bool subobject =
false)
266 jp[
"admin_user"] = user;
271 jpi[
"admin_password"] = password;
272 jp[
"admin_password"] = jpi;
276 jp[
"admin_password"] = password;
280 if (boost::starts_with(proto,
"h"))
283 jrr = jrc->invoke(
"ledger_accept", jp);
288 jrr = wsc->invoke(
"ledger_accept", jp);
301 testcase <<
"Admin request over " << proto <<
", config "
302 << (admin ?
"enabled" :
"disabled") <<
", credentials "
303 << (credentials ?
"" :
"not ") <<
"set";
308 auto const proto_ws = boost::starts_with(proto,
"w");
313 if (admin && credentials)
315 auto const user = env.
app()
316 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
319 auto const password =
321 .
config()[proto_ws ?
"port_ws" :
"port_rpc"]
326 env, proto, *user, *password +
"_")[jss::result];
328 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
330 jrr[
"error_message"] == proto_ws
332 :
"You don't have permission for this command.");
336 env, proto, *user, *password,
true)[jss::result];
338 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
340 jrr[
"error_message"] == proto_ws
342 :
"You don't have permission for this command.");
346 env, proto, *user +
"_", *password)[jss::result];
348 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
350 jrr[
"error_message"] == proto_ws
352 :
"You don't have permission for this command.");
357 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
359 jrr[
"error_message"] == proto_ws
361 :
"You don't have permission for this command.");
365 BEAST_EXPECT(jrr[
"status"] ==
"success");
371 BEAST_EXPECT(jrr[
"status"] ==
"success");
375 BEAST_EXPECT(jrr[
"status"] ==
"success");
382 jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
384 jrr[
"error_message"] == proto_ws
386 :
"You don't have permission for this command.");
393 testcase(
"WS client to http server fails");
396 cfg->section(
"port_ws").
set(
"protocol",
"http,https");
402 boost::system::error_code ec;
403 boost::beast::http::response<boost::beast::http::string_body> resp;
405 if (!BEAST_EXPECTS(!ec, ec.message()))
408 resp.result() == boost::beast::http::status::unauthorized);
413 boost::system::error_code ec;
414 boost::beast::http::response<boost::beast::http::string_body> resp;
416 if (!BEAST_EXPECTS(!ec, ec.message()))
419 resp.result() == boost::beast::http::status::unauthorized);
429 cfg->section(
"port_rpc").
set(
"protocol",
"ws2,wss2");
430 cfg->section(
"port_ws").
set(
"protocol",
"http");
436 boost::system::error_code ec;
437 boost::beast::http::response<boost::beast::http::string_body> resp;
439 if (!BEAST_EXPECTS(!ec, ec.message()))
441 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
446 boost::system::error_code ec;
447 boost::beast::http::response<boost::beast::http::string_body> resp;
449 if (!BEAST_EXPECTS(!ec, ec.message()))
451 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
458 testcase(
"Partial WS upgrade request");
461 using namespace boost::beast::http;
463 cfg->section(
"port_ws").
set(
"protocol",
"ws2");
471 boost::system::error_code ec;
472 response<string_body> resp;
476 auto req_string = boost::lexical_cast<std::string>(req);
477 req_string.erase(req_string.find_last_of(
"13"), std::string::npos);
480 ip::tcp::resolver r{ios};
481 boost::beast::multi_buffer sb;
483 auto it = r.async_resolve(
485 if (!BEAST_EXPECTS(!ec, ec.message()))
488 ip::tcp::socket sock{ios};
489 async_connect(sock, it, yield[ec]);
490 if (!BEAST_EXPECTS(!ec, ec.message()))
492 async_write(sock, boost::asio::buffer(req_string), yield[ec]);
493 if (!BEAST_EXPECTS(!ec, ec.message()))
497 async_read(sock, sb, resp, yield[ec]);
505 boost::asio::yield_context& yield)
510 testcase <<
"Connect fails: " << client_protocol <<
" client to "
511 << server_protocol <<
" server";
515 boost::beast::http::response<boost::beast::http::string_body> resp;
516 boost::system::error_code ec;
517 if (boost::starts_with(client_protocol,
"h"))
519 doHTTPRequest(env, yield, client_protocol ==
"https", resp, ec);
527 client_protocol ==
"wss" || client_protocol ==
"wss2",
535 testAuth(
bool secure, boost::asio::yield_context& yield)
537 testcase <<
"Server with authorization, "
538 << (secure ?
"secure" :
"non-secure");
540 using namespace test::jtx;
542 (*cfg)[
"port_rpc"].set(
"user",
"me");
543 (*cfg)[
"port_rpc"].set(
"password",
"secret");
544 (*cfg)[
"port_rpc"].set(
545 "protocol", secure ?
"https" :
"http");
547 (*cfg)[
"port_ws"].
set(
"protocol",
"http,ws");
552 jr[jss::method] =
"server_info";
553 boost::beast::http::response<boost::beast::http::string_body> resp;
554 boost::system::error_code ec;
556 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
559 auth.insert(
"Authorization",
"");
561 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
563 auth.set(
"Authorization",
"Basic NOT-VALID");
565 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
569 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
571 auto const user = env.
app()
583 auth.set(
"Authorization",
"Basic " + user +
":" +
pass);
585 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
590 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
591 BEAST_EXPECT(!resp.body().empty());
597 testcase <<
"Server with connection limit of " << limit;
599 using namespace test::jtx;
601 using namespace boost::beast::http;
612 boost::system::error_code ec;
614 ip::tcp::resolver r{ios};
617 jr[jss::method] =
"server_info";
619 auto it = r.async_resolve(
625 int connectionCount{1};
633 int testTo = (limit == 0) ? 50 : limit + 1;
634 while (connectionCount < testTo)
637 ip::tcp::socket{ios}, boost::beast::multi_buffer{}));
638 async_connect(clients.
back().first, it, yield[ec]);
641 async_write(clients.
back().first, req, yield[ec]);
647 for (
auto& [soc, buf] : clients)
649 boost::beast::http::response<boost::beast::http::string_body> resp;
650 async_read(soc, buf, resp, yield[ec]);
655 (limit == 0 || readCount < limit - 1) ? (!ec) :
bool(ec));
662 testcase(
"Connection with WS handoff");
664 using namespace test::jtx;
666 (*cfg)[
"port_ws"].set(
"protocol",
"wss");
674 boost::beast::http::response<boost::beast::http::string_body> resp;
675 boost::system::error_code ec;
678 resp.result() == boost::beast::http::status::switching_protocols);
680 resp.find(
"Upgrade") != resp.end() &&
681 resp[
"Upgrade"] ==
"websocket");
683 resp.find(
"Connection") != resp.end() &&
684 resp[
"Connection"] ==
"Upgrade");
690 testcase(
"Connection to port with no RPC enabled");
692 using namespace test::jtx;
699 boost::beast::http::response<boost::beast::http::string_body> resp;
700 boost::system::error_code ec;
711 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
712 BEAST_EXPECT(resp.body() ==
"Forbidden\r\n");
718 testcase(
"WS client sends assorted input");
720 using namespace test::jtx;
722 using namespace boost::beast::http;
729 boost::system::error_code ec;
732 ip::tcp::resolver r{ios};
734 auto it = r.async_resolve(
736 if (!BEAST_EXPECT(!ec))
739 ip::tcp::socket sock{ios};
740 async_connect(sock, it, yield[ec]);
741 if (!BEAST_EXPECT(!ec))
744 boost::beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
749 ws.async_write_some(
true, buffer(req), yield[ec]);
750 if (!BEAST_EXPECT(!ec))
753 boost::beast::multi_buffer sb;
754 ws.async_read(sb, yield[ec]);
755 if (!BEAST_EXPECT(!ec))
760 if (!BEAST_EXPECT(jr.
parse(
761 boost::lexical_cast<std::string>(
762 boost::beast::make_printable(sb.data())),
765 sb.consume(sb.size());
770 auto resp = sendAndParse(
"NOT JSON");
772 resp.isMember(jss::error) && resp[jss::error] ==
"jsonInvalid");
773 BEAST_EXPECT(!resp.isMember(jss::status));
778 jv[jss::command] =
"foo";
779 jv[jss::method] =
"bar";
782 resp.isMember(jss::error) &&
783 resp[jss::error] ==
"missingCommand");
785 resp.isMember(jss::status) && resp[jss::status] ==
"error");
790 jv[jss::command] =
"ping";
793 resp.isMember(jss::status) && resp[jss::status] ==
"success");
795 resp.isMember(jss::result) &&
796 resp[jss::result].isMember(jss::role) &&
797 resp[jss::result][jss::role] ==
"admin");
805 "Status request over WS and RPC with/without Amendment Warning");
808 using namespace boost::beast::http;
813 cfg->section(
"port_rpc").
set(
"protocol",
"http");
827 auto si = env.
rpc(
"server_info")[jss::result];
828 BEAST_EXPECT(si.isMember(jss::info));
829 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
832 BEAST_EXPECT(!si.isMember(jss::warnings));
836 si = env.
rpc(
"server_state")[jss::result];
837 BEAST_EXPECT(si.isMember(jss::state));
838 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
841 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
847 boost::system::error_code ec;
848 response<string_body> resp;
859 if (!BEAST_EXPECTS(!ec, ec.message()))
861 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
863 resp.body().find(
"connectivity is working.") != std::string::npos);
875 si = env.
rpc(
"server_info")[jss::result];
876 BEAST_EXPECT(si.isMember(jss::info));
877 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
879 si[jss::info].isMember(jss::warnings) &&
880 si[jss::info][jss::warnings].isArray() &&
881 si[jss::info][jss::warnings].size() == 1 &&
882 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
887 si = env.
rpc(
"server_state")[jss::result];
888 BEAST_EXPECT(si.isMember(jss::state));
889 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
891 si[jss::state].isMember(jss::warnings) &&
892 si[jss::state][jss::warnings].isArray() &&
893 si[jss::state][jss::warnings].size() == 1 &&
894 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
907 if (!BEAST_EXPECTS(!ec, ec.message()))
909 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
911 resp.body().find(
"connectivity is working.") != std::string::npos);
925 if (!BEAST_EXPECTS(!ec, ec.message()))
927 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
929 resp.body().find(
"connectivity is working.") != std::string::npos);
935 testcase(
"Status request over WS and RPC with/without Amendment Block");
938 using namespace boost::beast::http;
943 cfg->section(
"port_rpc").
set(
"protocol",
"http");
957 auto si = env.
rpc(
"server_info")[jss::result];
958 BEAST_EXPECT(si.isMember(jss::info));
959 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
962 BEAST_EXPECT(!si.isMember(jss::warnings));
966 si = env.
rpc(
"server_state")[jss::result];
967 BEAST_EXPECT(si.isMember(jss::state));
968 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
971 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
977 boost::system::error_code ec;
978 response<string_body> resp;
989 if (!BEAST_EXPECTS(!ec, ec.message()))
991 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
993 resp.body().find(
"connectivity is working.") != std::string::npos);
1005 si = env.
rpc(
"server_info")[jss::result];
1006 BEAST_EXPECT(si.isMember(jss::info));
1008 si[jss::info].isMember(jss::amendment_blocked) &&
1009 si[jss::info][jss::amendment_blocked] ==
true);
1011 si[jss::info].isMember(jss::warnings) &&
1012 si[jss::info][jss::warnings].isArray() &&
1013 si[jss::info][jss::warnings].size() == 1 &&
1014 si[jss::info][jss::warnings][0u][jss::id].asInt() ==
1018 si = env.
rpc(
"server_state")[jss::result];
1020 si[jss::state].isMember(jss::amendment_blocked) &&
1021 si[jss::state][jss::amendment_blocked] ==
true);
1023 si[jss::state].isMember(jss::warnings) &&
1024 si[jss::state][jss::warnings].isArray() &&
1025 si[jss::state][jss::warnings].size() == 1 &&
1026 si[jss::state][jss::warnings][0u][jss::id].asInt() ==
1040 if (!BEAST_EXPECTS(!ec, ec.message()))
1042 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
1044 resp.body().find(
"connectivity is working.") != std::string::npos);
1057 if (!BEAST_EXPECTS(!ec, ec.message()))
1060 resp.result() == boost::beast::http::status::internal_server_error);
1062 resp.body().find(
"cannot accept clients:") != std::string::npos);
1064 resp.body().find(
"Server version too old") != std::string::npos);
1070 testcase(
"RPC client sends assorted input");
1072 using namespace test::jtx;
1075 boost::system::error_code ec;
1077 boost::beast::http::response<boost::beast::http::string_body> resp;
1080 resp.result() == boost::beast::http::status::bad_request);
1081 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1085 boost::beast::http::response<boost::beast::http::string_body> resp;
1090 resp.result() == boost::beast::http::status::bad_request);
1091 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1095 boost::beast::http::response<boost::beast::http::string_body> resp;
1100 resp.result() == boost::beast::http::status::bad_request);
1101 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1105 boost::beast::http::response<boost::beast::http::string_body> resp;
1112 resp.result() == boost::beast::http::status::bad_request);
1113 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1117 boost::beast::http::response<boost::beast::http::string_body> resp;
1119 jv[jss::method] =
"batch";
1120 jv[jss::params] = 2;
1123 resp.result() == boost::beast::http::status::bad_request);
1124 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1128 boost::beast::http::response<boost::beast::http::string_body> resp;
1130 jv[jss::method] =
"batch";
1132 jv[jss::params][
"invalid"] = 3;
1135 resp.result() == boost::beast::http::status::bad_request);
1136 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1141 boost::beast::http::response<boost::beast::http::string_body> resp;
1145 resp.result() == boost::beast::http::status::bad_request);
1146 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1150 boost::beast::http::response<boost::beast::http::string_body> resp;
1151 jv[jss::method] = 1;
1154 resp.result() == boost::beast::http::status::bad_request);
1155 BEAST_EXPECT(resp.body() ==
"method is not string\r\n");
1159 boost::beast::http::response<boost::beast::http::string_body> resp;
1160 jv[jss::method] =
"";
1163 resp.result() == boost::beast::http::status::bad_request);
1164 BEAST_EXPECT(resp.body() ==
"method is empty\r\n");
1168 boost::beast::http::response<boost::beast::http::string_body> resp;
1169 jv[jss::method] =
"some_method";
1170 jv[jss::params] =
"params";
1173 resp.result() == boost::beast::http::status::bad_request);
1174 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1178 boost::beast::http::response<boost::beast::http::string_body> resp;
1180 jv[jss::params][0u] =
"not an object";
1183 resp.result() == boost::beast::http::status::bad_request);
1184 BEAST_EXPECT(resp.body() ==
"params unparseable\r\n");
1191 testcase(
"Server status not okay");
1193 using namespace test::jtx;
1195 cfg->ELB_SUPPORT =
true;
1202 boost::beast::http::response<boost::beast::http::string_body> resp;
1203 boost::system::error_code ec;
1206 resp.result() == boost::beast::http::status::internal_server_error);
1207 std::regex body{
"Server cannot accept clients"};
1215 for (
auto it : {
"http",
"ws",
"ws2"})
1222 yield_to([&](boost::asio::yield_context& yield) {
1251BEAST_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(Value const &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
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.
char const * getEnvLocalhostAddr()
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)