2#include <test/jtx/JSONRPCClient.h>
3#include <test/jtx/WSClient.h>
4#include <test/jtx/envconfig.h>
6#include <xrpld/app/ledger/LedgerMaster.h>
7#include <xrpld/rpc/ServerHandler.h>
9#include <xrpl/basics/base64.h>
10#include <xrpl/beast/test/yield_to.h>
11#include <xrpl/json/json_reader.h>
12#include <xrpl/server/LoadFeeTrack.h>
13#include <xrpl/server/NetworkOPs.h>
15#include <boost/algorithm/string/predicate.hpp>
16#include <boost/asio.hpp>
17#include <boost/asio/io_context.hpp>
18#include <boost/asio/ssl.hpp>
19#include <boost/beast/core/multi_buffer.hpp>
20#include <boost/beast/http.hpp>
32 class myFields :
public boost::beast::http::fields
39 auto const section_name = boost::starts_with(proto,
"h") ?
"port_rpc" :
"port_ws";
42 p->overwrite(section_name,
"protocol", proto);
44 p->overwrite(section_name,
"admin",
"");
48 (*p)[section_name].set(
"admin_password",
"p");
49 (*p)[section_name].set(
"admin_user",
"u");
53 boost::starts_with(proto,
"h") ?
"port_ws" :
"port_rpc",
55 boost::starts_with(proto,
"h") ?
"ws" :
"http");
62 (*p)[
"server"].append(
"port_alt");
64 (*p)[
"port_alt"].set(
"port",
"7099");
65 (*p)[
"port_alt"].set(
"protocol",
"http");
76 using namespace boost::beast::http;
77 request<string_body> req;
82 req.insert(
"User-Agent",
"test");
83 req.method(boost::beast::http::verb::get);
84 req.insert(
"Upgrade",
"websocket");
93 req.insert(
"Sec-WebSocket-Key",
base64_encode(key.data(), key.size()));
95 req.insert(
"Sec-WebSocket-Version",
"13");
96 req.insert(boost::beast::http::field::connection,
"upgrade");
108 using namespace boost::beast::http;
109 request<string_body> req;
113 for (
auto const& f : fields)
114 req.insert(f.name(), f.value());
116 req.insert(
"User-Agent",
"test");
119 req.method(boost::beast::http::verb::get);
123 req.method(boost::beast::http::verb::post);
124 req.insert(
"Content-Type",
"application/json; charset=UTF-8");
127 req.prepare_payload();
134 boost::asio::yield_context& yield,
135 boost::beast::http::request<boost::beast::http::string_body>
const& req,
139 boost::beast::http::response<boost::beast::http::string_body>& resp,
140 boost::system::error_code& ec)
143 using namespace boost::beast::http;
145 ip::tcp::resolver r{ios};
146 boost::beast::multi_buffer sb;
155 ssl::context ctx{ssl::context::sslv23};
156 ctx.set_verify_mode(ssl::verify_none);
157 ssl::stream<ip::tcp::socket> ss{ios, ctx};
158 async_connect(ss.next_layer(), it, yield[ec]);
161 ss.async_handshake(ssl::stream_base::client, yield[ec]);
164 boost::beast::http::async_write(ss, req, yield[ec]);
167 async_read(ss, sb, resp, yield[ec]);
173 ip::tcp::socket sock{ios};
174 async_connect(sock, it, yield[ec]);
177 boost::beast::http::async_write(sock, req, yield[ec]);
180 async_read(sock, sb, resp, yield[ec]);
191 boost::asio::yield_context& yield,
193 boost::beast::http::response<boost::beast::http::string_body>& resp,
194 boost::system::error_code& ec)
206 boost::asio::yield_context& yield,
208 boost::beast::http::response<boost::beast::http::string_body>& resp,
209 boost::system::error_code& ec,
226 bool subobject =
false)
233 jp[
"admin_user"] = user;
238 jpi[
"admin_password"] = password;
239 jp[
"admin_password"] = jpi;
243 jp[
"admin_password"] = password;
247 if (boost::starts_with(proto,
"h"))
250 jrr = jrc->invoke(
"ledger_accept", jp);
255 jrr = wsc->invoke(
"ledger_accept", jp);
268 testcase <<
"Admin request over " << proto <<
", config "
269 << (admin ?
"enabled" :
"disabled") <<
", credentials "
270 << (credentials ?
"" :
"not ") <<
"set";
275 auto const proto_ws = boost::starts_with(proto,
"w");
280 if (admin && credentials)
286 auto const password =
293 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
295 jrr[
"error_message"] == proto_ws ?
"Bad credentials."
296 :
"You don't have permission for this command.");
301 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
303 jrr[
"error_message"] == proto_ws ?
"Bad credentials."
304 :
"You don't have permission for this command.");
309 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
311 jrr[
"error_message"] == proto_ws ?
"Bad credentials."
312 :
"You don't have permission for this command.");
316 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
318 jrr[
"error_message"] == proto_ws ?
"Bad credentials."
319 :
"You don't have permission for this command.");
324 BEAST_EXPECT(jrr[
"status"] ==
"success");
330 BEAST_EXPECT(jrr[
"status"] ==
"success");
334 BEAST_EXPECT(jrr[
"status"] ==
"success");
340 BEAST_EXPECT(jrr[
"error"] == proto_ws ?
"forbidden" :
"noPermission");
342 jrr[
"error_message"] == proto_ws ?
"Bad credentials."
343 :
"You don't have permission for this command.");
350 testcase(
"WS client to http server fails");
353 cfg->section(
"port_ws").
set(
"protocol",
"http,https");
359 boost::system::error_code ec;
360 boost::beast::http::response<boost::beast::http::string_body> resp;
362 if (!BEAST_EXPECTS(!ec, ec.message()))
364 BEAST_EXPECT(resp.result() == boost::beast::http::status::unauthorized);
369 boost::system::error_code ec;
370 boost::beast::http::response<boost::beast::http::string_body> resp;
372 if (!BEAST_EXPECTS(!ec, ec.message()))
374 BEAST_EXPECT(resp.result() == boost::beast::http::status::unauthorized);
384 cfg->section(
"port_rpc").
set(
"protocol",
"ws2,wss2");
385 cfg->section(
"port_ws").
set(
"protocol",
"http");
391 boost::system::error_code ec;
392 boost::beast::http::response<boost::beast::http::string_body> resp;
394 if (!BEAST_EXPECTS(!ec, ec.message()))
396 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
401 boost::system::error_code ec;
402 boost::beast::http::response<boost::beast::http::string_body> resp;
404 if (!BEAST_EXPECTS(!ec, ec.message()))
406 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
413 testcase(
"Partial WS upgrade request");
416 using namespace boost::beast::http;
418 cfg->section(
"port_ws").
set(
"protocol",
"ws2");
425 boost::system::error_code ec;
426 response<string_body> resp;
430 auto req_string = boost::lexical_cast<std::string>(req);
431 req_string.erase(req_string.find_last_of(
"13"), std::string::npos);
434 ip::tcp::resolver r{ios};
435 boost::beast::multi_buffer sb;
437 auto it = r.async_resolve(
439 if (!BEAST_EXPECTS(!ec, ec.message()))
442 ip::tcp::socket sock{ios};
443 async_connect(sock, it, yield[ec]);
444 if (!BEAST_EXPECTS(!ec, ec.message()))
446 async_write(sock, boost::asio::buffer(req_string), yield[ec]);
447 if (!BEAST_EXPECTS(!ec, ec.message()))
451 async_read(sock, sb, resp, yield[ec]);
459 boost::asio::yield_context& yield)
464 testcase <<
"Connect fails: " << client_protocol <<
" client to " << server_protocol
469 boost::beast::http::response<boost::beast::http::string_body> resp;
470 boost::system::error_code ec;
471 if (boost::starts_with(client_protocol,
"h"))
473 doHTTPRequest(env, yield, client_protocol ==
"https", resp, ec);
479 env, yield, client_protocol ==
"wss" || client_protocol ==
"wss2", resp, ec);
485 testAuth(
bool secure, boost::asio::yield_context& yield)
487 testcase <<
"Server with authorization, " << (secure ?
"secure" :
"non-secure");
489 using namespace test::jtx;
491 (*cfg)[
"port_rpc"].set(
"user",
"me");
492 (*cfg)[
"port_rpc"].set(
"password",
"secret");
493 (*cfg)[
"port_rpc"].set(
"protocol", secure ?
"https" :
"http");
495 (*cfg)[
"port_ws"].set(
"protocol",
"http,ws");
500 jr[jss::method] =
"server_info";
501 boost::beast::http::response<boost::beast::http::string_body> resp;
502 boost::system::error_code ec;
504 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
507 auth.insert(
"Authorization",
"");
509 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
511 auth.set(
"Authorization",
"Basic NOT-VALID");
513 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
517 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
526 auth.set(
"Authorization",
"Basic " + user +
":" +
pass);
528 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
533 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
534 BEAST_EXPECT(!resp.body().empty());
540 testcase <<
"Server with connection limit of " << limit;
542 using namespace test::jtx;
544 using namespace boost::beast::http;
556 boost::system::error_code ec;
558 ip::tcp::resolver r{ios};
561 jr[jss::method] =
"server_info";
567 int connectionCount{1};
575 int const testTo = (limit == 0) ? 50 : limit + 1;
576 while (connectionCount < testTo)
579 std::make_pair(ip::tcp::socket{ios}, boost::beast::multi_buffer{}));
580 async_connect(clients.
back().first, it, yield[ec]);
583 async_write(clients.
back().first, req, yield[ec]);
589 for (
auto& [soc, buf] : clients)
591 boost::beast::http::response<boost::beast::http::string_body> resp;
592 async_read(soc, buf, resp, yield[ec]);
596 BEAST_EXPECT((limit == 0 || readCount < limit - 1) ? (!ec) : bool(ec));
603 testcase(
"Connection with WS handoff");
605 using namespace test::jtx;
607 (*cfg)[
"port_ws"].set(
"protocol",
"wss");
615 boost::beast::http::response<boost::beast::http::string_body> resp;
616 boost::system::error_code ec;
618 BEAST_EXPECT(resp.result() == boost::beast::http::status::switching_protocols);
619 BEAST_EXPECT(resp.contains(
"Upgrade") && resp[
"Upgrade"] ==
"websocket");
620 BEAST_EXPECT(resp.contains(
"Connection") && boost::iequals(resp[
"Connection"],
"upgrade"));
626 testcase(
"Connection to port with no RPC enabled");
628 using namespace test::jtx;
635 boost::beast::http::response<boost::beast::http::string_body> resp;
636 boost::system::error_code ec;
640 BEAST_EXPECT(resp.result() == boost::beast::http::status::forbidden);
641 BEAST_EXPECT(resp.body() ==
"Forbidden\r\n");
647 testcase(
"WS client sends assorted input");
649 using namespace test::jtx;
651 using namespace boost::beast::http;
658 boost::system::error_code ec;
661 ip::tcp::resolver r{ios};
664 if (!BEAST_EXPECT(!ec))
667 ip::tcp::socket sock{ios};
668 async_connect(sock, it, yield[ec]);
669 if (!BEAST_EXPECT(!ec))
672 boost::beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
677 ws.async_write_some(
true, buffer(req), yield[ec]);
678 if (!BEAST_EXPECT(!ec))
681 boost::beast::multi_buffer sb;
682 ws.async_read(sb, yield[ec]);
683 if (!BEAST_EXPECT(!ec))
688 if (!BEAST_EXPECT(jr.
parse(
689 boost::lexical_cast<std::string>(boost::beast::make_printable(sb.data())),
692 sb.consume(sb.size());
697 auto resp = sendAndParse(
"NOT JSON");
698 BEAST_EXPECT(resp.isMember(jss::error) && resp[jss::error] ==
"jsonInvalid");
699 BEAST_EXPECT(!resp.isMember(jss::status));
704 jv[jss::command] =
"foo";
705 jv[jss::method] =
"bar";
707 BEAST_EXPECT(resp.isMember(jss::error) && resp[jss::error] ==
"missingCommand");
708 BEAST_EXPECT(resp.isMember(jss::status) && resp[jss::status] ==
"error");
713 jv[jss::command] =
"ping";
715 BEAST_EXPECT(resp.isMember(jss::status) && resp[jss::status] ==
"success");
717 resp.isMember(jss::result) && resp[jss::result].isMember(jss::role) &&
718 resp[jss::result][jss::role] ==
"admin");
725 testcase(
"Status request over WS and RPC with/without Amendment Warning");
728 using namespace boost::beast::http;
733 cfg->section(
"port_rpc").
set(
"protocol",
"http");
747 auto si = env.
rpc(
"server_info")[jss::result];
748 BEAST_EXPECT(si.isMember(jss::info));
749 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
751 BEAST_EXPECT(!si.isMember(jss::warnings));
755 si = env.
rpc(
"server_state")[jss::result];
756 BEAST_EXPECT(si.isMember(jss::state));
757 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
759 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
764 boost::system::error_code ec;
765 response<string_body> resp;
779 if (!BEAST_EXPECTS(!ec, ec.message()))
781 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
782 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
793 si = env.
rpc(
"server_info")[jss::result];
794 BEAST_EXPECT(si.isMember(jss::info));
795 BEAST_EXPECT(!si[jss::info].isMember(jss::amendment_blocked));
797 si[jss::info].isMember(jss::warnings) && si[jss::info][jss::warnings].isArray() &&
798 si[jss::info][jss::warnings].size() == 1 &&
803 si = env.
rpc(
"server_state")[jss::result];
804 BEAST_EXPECT(si.isMember(jss::state));
805 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
807 si[jss::state].isMember(jss::warnings) && si[jss::state][jss::warnings].isArray() &&
808 si[jss::state][jss::warnings].size() == 1 &&
824 if (!BEAST_EXPECTS(!ec, ec.message()))
826 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
827 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
844 if (!BEAST_EXPECTS(!ec, ec.message()))
846 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
847 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
853 testcase(
"Status request over WS and RPC with/without Amendment Block");
856 using namespace boost::beast::http;
861 cfg->section(
"port_rpc").
set(
"protocol",
"http");
875 auto 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 BEAST_EXPECT(!si.isMember(jss::warnings));
883 si = env.
rpc(
"server_state")[jss::result];
884 BEAST_EXPECT(si.isMember(jss::state));
885 BEAST_EXPECT(!si[jss::state].isMember(jss::amendment_blocked));
887 BEAST_EXPECT(!si[jss::state].isMember(jss::warnings));
892 boost::system::error_code ec;
893 response<string_body> resp;
907 if (!BEAST_EXPECTS(!ec, ec.message()))
909 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
910 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
921 si = env.
rpc(
"server_info")[jss::result];
922 BEAST_EXPECT(si.isMember(jss::info));
924 si[jss::info].isMember(jss::amendment_blocked) &&
925 si[jss::info][jss::amendment_blocked] ==
true);
927 si[jss::info].isMember(jss::warnings) && si[jss::info][jss::warnings].isArray() &&
928 si[jss::info][jss::warnings].size() == 1 &&
932 si = env.
rpc(
"server_state")[jss::result];
934 si[jss::state].isMember(jss::amendment_blocked) &&
935 si[jss::state][jss::amendment_blocked] ==
true);
937 si[jss::state].isMember(jss::warnings) && si[jss::state][jss::warnings].isArray() &&
938 si[jss::state][jss::warnings].size() == 1 &&
955 if (!BEAST_EXPECTS(!ec, ec.message()))
957 BEAST_EXPECT(resp.result() == boost::beast::http::status::ok);
958 BEAST_EXPECT(resp.body().find(
"connectivity is working.") != std::string::npos);
974 if (!BEAST_EXPECTS(!ec, ec.message()))
976 BEAST_EXPECT(resp.result() == boost::beast::http::status::internal_server_error);
977 BEAST_EXPECT(resp.body().find(
"cannot accept clients:") != std::string::npos);
978 BEAST_EXPECT(resp.body().find(
"Server version too old") != std::string::npos);
984 testcase(
"RPC client sends assorted input");
986 using namespace test::jtx;
989 boost::system::error_code ec;
991 boost::beast::http::response<boost::beast::http::string_body> resp;
993 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
994 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
998 boost::beast::http::response<boost::beast::http::string_body> resp;
1002 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1003 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1007 boost::beast::http::response<boost::beast::http::string_body> resp;
1011 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1012 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1016 boost::beast::http::response<boost::beast::http::string_body> resp;
1022 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1023 BEAST_EXPECT(resp.body() ==
"Unable to parse request: \r\n");
1027 boost::beast::http::response<boost::beast::http::string_body> resp;
1029 jv[jss::method] =
"batch";
1030 jv[jss::params] = 2;
1032 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1033 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1037 boost::beast::http::response<boost::beast::http::string_body> resp;
1039 jv[jss::method] =
"batch";
1041 jv[jss::params][
"invalid"] = 3;
1043 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1044 BEAST_EXPECT(resp.body() ==
"Malformed batch request\r\n");
1049 boost::beast::http::response<boost::beast::http::string_body> resp;
1052 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1053 BEAST_EXPECT(resp.body() ==
"Null method\r\n");
1057 boost::beast::http::response<boost::beast::http::string_body> resp;
1058 jv[jss::method] = 1;
1060 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1061 BEAST_EXPECT(resp.body() ==
"method is not string\r\n");
1065 boost::beast::http::response<boost::beast::http::string_body> resp;
1066 jv[jss::method] =
"";
1068 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1069 BEAST_EXPECT(resp.body() ==
"method is empty\r\n");
1073 boost::beast::http::response<boost::beast::http::string_body> resp;
1074 jv[jss::method] =
"some_method";
1075 jv[jss::params] =
"params";
1077 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1078 BEAST_EXPECT(resp.body() ==
"params unparsable\r\n");
1082 boost::beast::http::response<boost::beast::http::string_body> resp;
1084 jv[jss::params][0u] =
"not an object";
1086 BEAST_EXPECT(resp.result() == boost::beast::http::status::bad_request);
1087 BEAST_EXPECT(resp.body() ==
"params unparsable\r\n");
1094 testcase(
"Server status not okay");
1096 using namespace test::jtx;
1098 cfg->ELB_SUPPORT =
true;
1105 boost::beast::http::response<boost::beast::http::string_body> resp;
1106 boost::system::error_code ec;
1108 BEAST_EXPECT(resp.result() == boost::beast::http::status::internal_server_error);
1109 std::regex const body{
"Server cannot accept clients"};
1117 for (
auto it : {
"http",
"ws",
"ws2"})
1124 yield_to([&](boost::asio::yield_context& yield) {
1153BEAST_DEFINE_TESTSUITE(ServerStatus, server,
xrpl);
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.
boost::asio::io_context & get_io_context()
Return the io_context associated with the object.
void yield_to(F0 &&f0, FN &&... fn)
Run one or more functions, each in a coroutine.
void pass()
Record a successful test condition.
testcase_t testcase
Memberspace for declaring test cases.
virtual Config & config()=0
Section & section(std::string const &name)
Returns the section with the given name.
virtual void setAmendmentBlocked()=0
virtual bool beginConsensus(uint256 const &netLCL, std::unique_ptr< std::stringstream > const &clog)=0
virtual Json::Value getConsensusInfo()=0
virtual void setAmendmentWarned()=0
std::optional< T > get(std::string const &name) const
virtual NetworkOPs & getOPs()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual LedgerMaster & getLedgerMaster()=0
void testWSClientToHttpServer(boost::asio::yield_context &yield)
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 testAmendmentBlock(boost::asio::yield_context &yield)
static auto makeWSUpgrade(std::string const &host, uint16_t port)
void testNoRPC(boost::asio::yield_context &yield)
void testWSHandoff(boost::asio::yield_context &yield)
void testTruncatedWSUpgrade(boost::asio::yield_context &yield)
void testAdminRequest(std::string const &proto, bool admin, bool credentials)
void testRPCRequests(boost::asio::yield_context &yield)
void testStatusNotOkay(boost::asio::yield_context &yield)
static auto makeAdminRequest(jtx::Env &env, std::string const &proto, std::string const &user, std::string const &password, bool subobject=false)
void run() override
Runs the suite.
void testAmendmentWarning(boost::asio::yield_context &yield)
void doRequest(boost::asio::yield_context &yield, boost::beast::http::request< boost::beast::http::string_body > const &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 testAuth(bool secure, boost::asio::yield_context &yield)
static auto makeHTTPRequest(std::string const &host, uint16_t port, std::string const &body, myFields const &fields)
void testLimit(boost::asio::yield_context &yield, int limit)
void testWSRequests(boost::asio::yield_context &yield)
static auto makeConfig(std::string const &proto, bool admin=true, bool credentials=false)
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 testCantConnect(std::string const &client_protocol, std::string const &server_protocol, boost::asio::yield_context &yield)
void testStatusRequest(boost::asio::yield_context &yield)
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
std::shared_ptr< ReadView const > closed()
Returns the last closed 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 > envconfig()
creates and initializes a default configuration for jtx::Env
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
char const * getEnvLocalhostAddr()
std::unique_ptr< AbstractClient > makeJSONRPCClient(Config const &cfg, unsigned rpc_version)
Returns a client using JSON-RPC over HTTP/S.
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.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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 to_string(base_uint< Bits, Tag > const &a)
std::string base64_encode(std::uint8_t const *data, std::size_t len)
@ warnRPC_AMENDMENT_BLOCKED
@ warnRPC_UNSUPPORTED_MAJORITY
T regex_search(T... args)