19#include <test/jtx/WSClient.h>
20#include <test/jtx/envconfig.h>
21#include <xrpld/app/main/LoadManager.h>
22#include <xrpld/app/misc/LoadFeeTrack.h>
23#include <xrpld/app/misc/NetworkOPs.h>
24#include <xrpld/core/ConfigSections.h>
25#include <xrpl/beast/unit_test.h>
26#include <xrpl/json/json_value.h>
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/jss.h>
41 using namespace std::chrono_literals;
50 stream[jss::streams].append(
"server");
51 auto jv = wsc->invoke(
"subscribe", stream);
52 if (wsc->version() == 2)
55 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
57 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
58 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
60 BEAST_EXPECT(jv[jss::status] ==
"success");
72 for (
int i = 0; i < 5; ++i)
77 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
78 return jv[jss::type] ==
"serverStatus";
84 auto jv = wsc->invoke(
"unsubscribe", stream);
85 if (wsc->version() == 2)
88 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
90 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
91 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
93 BEAST_EXPECT(jv[jss::status] ==
"success");
99 for (
int i = 0; i < 5; ++i)
104 auto jvo = wsc->getMsg(10ms);
105 BEAST_EXPECTS(!jvo,
"getMsg: " +
to_string(jvo.value()));
112 using namespace std::chrono_literals;
121 stream[jss::streams].append(
"ledger");
122 auto jv = wsc->invoke(
"subscribe", stream);
123 if (wsc->version() == 2)
126 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
128 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
129 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
131 BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 2);
139 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
140 return jv[jss::ledger_index] == 3;
149 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
150 return jv[jss::ledger_index] == 4;
155 auto jv = wsc->invoke(
"unsubscribe", stream);
156 if (wsc->version() == 2)
159 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
161 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
162 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
164 BEAST_EXPECT(jv[jss::status] ==
"success");
170 using namespace std::chrono_literals;
179 stream[jss::streams].append(
"transactions");
180 auto jv = wsc->invoke(
"subscribe", stream);
181 if (wsc->version() == 2)
184 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
186 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
187 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
189 BEAST_EXPECT(jv[jss::status] ==
"success");
197 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
198 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
199 [
"NewFields"][jss::Account]
200 == Account(
"alice").human() &&
201 jv[jss::transaction][jss::TransactionType]
203 jv[jss::transaction][jss::DeliverMax]
205 jv[jss::transaction][jss::Fee]
207 jv[jss::transaction][jss::Sequence]
212 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
213 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
214 [
"FinalFields"][jss::Account] ==
215 Account(
"alice").human();
222 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
223 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
224 [
"NewFields"][jss::Account]
225 == Account(
"bob").human() &&
226 jv[jss::transaction][jss::TransactionType]
228 jv[jss::transaction][jss::DeliverMax]
230 jv[jss::transaction][jss::Fee]
232 jv[jss::transaction][jss::Sequence]
237 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
238 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
239 [
"FinalFields"][jss::Account] ==
240 Account(
"bob").human();
246 auto jv = wsc->invoke(
"unsubscribe", stream);
247 if (wsc->version() == 2)
250 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
252 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
253 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
255 BEAST_EXPECT(jv[jss::status] ==
"success");
262 stream[jss::accounts].append(
Account(
"alice").human());
263 auto jv = wsc->invoke(
"subscribe", stream);
264 if (wsc->version() == 2)
267 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
269 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
270 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
272 BEAST_EXPECT(jv[jss::status] ==
"success");
279 BEAST_EXPECT(!wsc->getMsg(10ms));
286 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
287 return jv[jss::meta][
"AffectedNodes"][1u][
"ModifiedNode"]
288 [
"FinalFields"][jss::Account] ==
289 Account(
"alice").human();
292 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
293 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
294 [
"NewFields"][
"LowLimit"][jss::issuer] ==
295 Account(
"alice").human();
300 auto jv = wsc->invoke(
"unsubscribe", stream);
301 if (wsc->version() == 2)
304 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
306 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
307 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
309 BEAST_EXPECT(jv[jss::status] ==
"success");
315 testcase(
"transactions API version 2");
317 using namespace std::chrono_literals;
325 stream[jss::api_version] = 2;
327 stream[jss::streams].append(
"transactions");
328 auto jv = wsc->invoke(
"subscribe", stream);
329 if (wsc->version() == 2)
332 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
334 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
335 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
337 BEAST_EXPECT(jv[jss::status] ==
"success");
345 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
346 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
347 [
"NewFields"][jss::Account]
348 == Account(
"alice").human() &&
349 jv[jss::close_time_iso]
350 ==
"2000-01-01T00:00:10Z" &&
351 jv[jss::validated] == true &&
352 jv[jss::ledger_hash] ==
353 "0F1A9E0C109ADEF6DA2BDE19217C12BBEC57174CBDBD212B0EBDC1CEDB"
355 !jv[jss::inLedger] &&
356 jv[jss::ledger_index] == 3 &&
357 jv[jss::tx_json][jss::TransactionType]
359 jv[jss::tx_json][jss::DeliverMax]
361 !jv[jss::tx_json].isMember(jss::Amount) &&
362 jv[jss::tx_json][jss::Fee]
364 jv[jss::tx_json][jss::Sequence]
369 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
370 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
371 [
"FinalFields"][jss::Account] ==
372 Account(
"alice").human();
378 auto jv = wsc->invoke(
"unsubscribe", stream);
379 if (wsc->version() == 2)
382 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
384 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
385 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
387 BEAST_EXPECT(jv[jss::status] ==
"success");
402 stream[jss::streams].append(
"manifests");
403 auto jv = wsc->invoke(
"subscribe", stream);
404 if (wsc->version() == 2)
407 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
409 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
410 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
412 BEAST_EXPECT(jv[jss::status] ==
"success");
416 auto jv = wsc->invoke(
"unsubscribe", stream);
417 if (wsc->version() == 2)
420 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
422 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
423 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
425 BEAST_EXPECT(jv[jss::status] ==
"success");
434 auto& cfg = env.app().config();
435 if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
437 auto const parsedseed =
438 parseBase58<Seed>(cfg.section(SECTION_VALIDATION_SEED).values()[0]);
439 if (!BEAST_EXPECT(parsedseed))
454 stream[jss::streams].append(
"validations");
455 auto jv = wsc->invoke(
"subscribe", stream);
456 if (wsc->version() == 2)
459 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
461 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
462 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
464 BEAST_EXPECT(jv[jss::status] ==
"success");
469 auto validValidationFields = [&env, &valPublicKey](
471 if (jv[jss::type] !=
"validationReceived")
474 if (jv[jss::validation_public_key].asString() != valPublicKey)
477 if (jv[jss::ledger_hash] !=
481 if (jv[jss::ledger_index] !=
488 if (jv[jss::full] !=
true)
491 if (jv.isMember(jss::load_fee))
494 if (!jv.isMember(jss::signature))
497 if (!jv.isMember(jss::signing_time))
500 if (!jv.isMember(jss::cookie))
503 if (!jv.isMember(jss::validated_hash))
508 (env.closed()->info().seq + 1) % 256 == 0;
524 while (env.closed()->info().seq < 300)
527 using namespace std::chrono_literals;
528 BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
533 auto jv = wsc->invoke(
"unsubscribe", stream);
534 if (wsc->version() == 2)
537 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
539 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
540 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
542 BEAST_EXPECT(jv[jss::status] ==
"success");
553 jv[jss::url] =
"http://localhost/events";
554 jv[jss::url_username] =
"admin";
555 jv[jss::url_password] =
"password";
557 jv[jss::streams][0u] =
"validations";
558 auto jr = env.rpc(
"json",
"subscribe",
to_string(jv))[jss::result];
559 BEAST_EXPECT(jr[jss::status] ==
"success");
561 jv[jss::streams][0u] =
"ledger";
562 jr = env.rpc(
"json",
"subscribe",
to_string(jv))[jss::result];
563 BEAST_EXPECT(jr[jss::status] ==
"success");
565 jr = env.rpc(
"json",
"unsubscribe",
to_string(jv))[jss::result];
566 BEAST_EXPECT(jr[jss::status] ==
"success");
568 jv[jss::streams][0u] =
"validations";
569 jr = env.rpc(
"json",
"unsubscribe",
to_string(jv))[jss::result];
570 BEAST_EXPECT(jr[jss::status] ==
"success");
577 auto const method = subscribe ?
"subscribe" :
"unsubscribe";
578 testcase <<
"Error cases for " << method;
584 auto jr = env.rpc(
"json", method,
"{}")[jss::result];
585 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
586 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
591 jv[jss::url] =
"not-a-url";
592 jv[jss::username] =
"admin";
593 jv[jss::password] =
"password";
594 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
597 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
598 BEAST_EXPECT(jr[jss::error_message] ==
"Failed to parse url.");
606 jv[jss::url] =
"ftp://scheme.not.supported.tld";
607 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
610 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
612 jr[jss::error_message] ==
613 "Only http and https is supported.");
620 jv[jss::url] =
"no-url";
622 env_nonadmin.rpc(
"json", method,
to_string(jv))[jss::result];
623 BEAST_EXPECT(jr[jss::error] ==
"noPermission");
625 jr[jss::error_message] ==
626 "You don't have permission for this command.");
638 for (
auto const& f : {jss::accounts_proposed, jss::accounts})
640 for (
auto const& nonArray : nonArrays)
644 auto jr = wsc->invoke(method, jv)[jss::result];
645 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
646 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
652 auto jr = wsc->invoke(method, jv)[jss::result];
653 BEAST_EXPECT(jr[jss::error] ==
"actMalformed");
654 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
658 for (
auto const& nonArray : nonArrays)
661 jv[jss::books] = nonArray;
662 auto jr = wsc->invoke(method, jv)[jss::result];
663 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
664 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
670 jv[jss::books][0u] = 1;
671 auto jr = wsc->invoke(method, jv)[jss::result];
672 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
673 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
682 auto jr = wsc->invoke(method, jv)[jss::result];
683 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
685 jr[jss::error_message] ==
"Source currency is malformed.");
694 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"ZZZZ";
695 auto jr = wsc->invoke(method, jv)[jss::result];
696 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
698 jr[jss::error_message] ==
"Source currency is malformed.");
707 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
708 jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
709 auto jr = wsc->invoke(method, jv)[jss::result];
710 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
712 jr[jss::error_message] ==
"Source issuer is malformed.");
721 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
722 jv[jss::books][0u][jss::taker_pays][jss::issuer] =
724 auto jr = wsc->invoke(method, jv)[jss::result];
725 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
727 jr[jss::error_message] ==
"Source issuer is malformed.");
734 jv[jss::books][0u][jss::taker_pays] =
735 Account{
"gateway"}[
"USD"](1).value().getJson(
738 auto jr = wsc->invoke(method, jv)[jss::result];
741 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
743 jr[jss::error_message] ==
744 "Destination amount/currency/issuer is malformed.");
751 jv[jss::books][0u][jss::taker_pays] =
752 Account{
"gateway"}[
"USD"](1).value().getJson(
754 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"ZZZZ";
755 auto jr = wsc->invoke(method, jv)[jss::result];
758 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
760 jr[jss::error_message] ==
761 "Destination amount/currency/issuer is malformed.");
768 jv[jss::books][0u][jss::taker_pays] =
769 Account{
"gateway"}[
"USD"](1).value().getJson(
771 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
772 jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
773 auto jr = wsc->invoke(method, jv)[jss::result];
774 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
776 jr[jss::error_message] ==
"Destination issuer is malformed.");
783 jv[jss::books][0u][jss::taker_pays] =
784 Account{
"gateway"}[
"USD"](1).value().getJson(
786 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
787 jv[jss::books][0u][jss::taker_gets][jss::issuer] =
789 auto jr = wsc->invoke(method, jv)[jss::result];
790 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
792 jr[jss::error_message] ==
"Destination issuer is malformed.");
799 jv[jss::books][0u][jss::taker_pays] =
800 Account{
"gateway"}[
"USD"](1).value().getJson(
802 jv[jss::books][0u][jss::taker_gets] =
803 Account{
"gateway"}[
"USD"](1).value().getJson(
805 auto jr = wsc->invoke(method, jv)[jss::result];
806 BEAST_EXPECT(jr[jss::error] ==
"badMarket");
807 BEAST_EXPECT(jr[jss::error_message] ==
"No such market.");
810 for (
auto const& nonArray : nonArrays)
813 jv[jss::streams] = nonArray;
814 auto jr = wsc->invoke(method, jv)[jss::result];
815 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
816 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
822 jv[jss::streams][0u] = 1;
823 auto jr = wsc->invoke(method, jv)[jss::result];
824 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
825 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
831 jv[jss::streams][0u] =
"not_a_stream";
832 auto jr = wsc->invoke(method, jv)[jss::result];
833 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
834 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
843 using namespace std::chrono_literals;
856 auto goodSubRPC = [](
Json::Value const& subReply) ->
bool {
857 return subReply.isMember(jss::result) &&
858 subReply[jss::result].isMember(jss::status) &&
859 subReply[jss::result][jss::status] == jss::success;
870 bool first_flag =
false;
872 for (
int i = 0; i < numReplies; ++i)
875 auto reply = wsc.
getMsg(100ms);
879 if (r.isMember(jss::account_history_tx_index))
880 idx = r[jss::account_history_tx_index].asInt();
881 if (r.isMember(jss::account_history_tx_first))
883 bool boundary = r.isMember(jss::account_history_boundary);
884 int ledger_idx = r[jss::ledger_index].asInt();
885 if (r.isMember(jss::transaction) &&
886 r[jss::transaction].isMember(jss::hash))
888 auto t{r[jss::transaction]};
890 idx, t[jss::hash].asString(), boundary, ledger_idx);
894 return {
false, first_flag};
897 return {
true, first_flag};
904 auto sendPayments = [](
Env& env,
912 for (
int i = 0; i < newTxns; ++i)
914 auto& from = (i % 2 == 0) ? a : b;
915 auto& to = (i % 2 == 0) ? b : a;
922 for (
int i = 0; i < ledgersToClose; ++i)
932 auto hashCompare = [](IdxHashVec
const& accountVec,
933 IdxHashVec
const& txHistoryVec,
934 bool sizeCompare) ->
bool {
935 if (accountVec.empty() || txHistoryVec.empty())
937 if (sizeCompare && accountVec.size() != (txHistoryVec.size()))
941 for (
auto const& tx : txHistoryVec)
943 txHistoryMap.
emplace(std::get<1>(tx), std::get<0>(tx));
947 if (i >= accountVec.size())
949 auto it = txHistoryMap.
find(std::get<1>(accountVec[i]));
950 if (it == txHistoryMap.
end())
955 auto firstHistoryIndex = getHistoryIndex(0);
956 if (!firstHistoryIndex)
958 for (
std::size_t i = 1; i < accountVec.size(); ++i)
960 if (
auto idx = getHistoryIndex(i);
961 !idx || *idx != *firstHistoryIndex + i)
997 auto checkBoundary = [](IdxHashVec
const& vec,
bool ) {
998 size_t num_tx = vec.size();
999 for (
size_t i = 0; i < num_tx; ++i)
1001 auto [idx, hash, boundary, ledger] = vec[i];
1002 if ((i + 1 == num_tx || ledger != std::get<3>(vec[i + 1])) !=
1022 request[jss::account_history_tx_stream][jss::account] =
1024 auto jv = wscTxHistory->invoke(
"subscribe", request);
1025 if (!BEAST_EXPECT(goodSubRPC(jv)))
1028 jv = wscTxHistory->invoke(
"subscribe", request);
1029 BEAST_EXPECT(!goodSubRPC(jv));
1034 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
1036 jv = wscTxHistory->invoke(
"unsubscribe", request);
1037 if (!BEAST_EXPECT(goodSubRPC(jv)))
1040 sendPayments(env, env.
master, alice, 1, 1, 123456);
1043 auto r = getTxHash(*wscTxHistory, vec, 1);
1044 if (!BEAST_EXPECT(r.first && r.second))
1050 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
1052 jv = wscTxHistory->invoke(
"unsubscribe", request);
1053 BEAST_EXPECT(goodSubRPC(jv));
1055 sendPayments(env, env.
master, alice, 1, 1);
1056 r = getTxHash(*wscTxHistory, vec, 1);
1057 BEAST_EXPECT(!r.first);
1068 request[jss::account_history_tx_stream][jss::account] =
1069 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1070 auto jv = wscTxHistory->invoke(
"subscribe", request);
1071 if (!BEAST_EXPECT(goodSubRPC(jv)))
1073 IdxHashVec genesisFullHistoryVec;
1075 !getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first))
1082 sendPayments(env, env.
master, bob, 1, 1, 654321);
1084 auto r = getTxHash(*wscTxHistory, genesisFullHistoryVec, 1);
1085 if (!BEAST_EXPECT(r.first && r.second))
1088 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1089 jv = wscTxHistory->invoke(
"subscribe", request);
1090 if (!BEAST_EXPECT(goodSubRPC(jv)))
1092 IdxHashVec bobFullHistoryVec;
1093 r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
1094 if (!BEAST_EXPECT(r.first && r.second))
1097 std::get<1>(bobFullHistoryVec.back()) ==
1098 std::get<1>(genesisFullHistoryVec.back()));
1103 jv = wscTxHistory->invoke(
"unsubscribe", request);
1104 if (!BEAST_EXPECT(goodSubRPC(jv)))
1106 request[jss::account_history_tx_stream][jss::account] =
1107 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1108 jv = wscTxHistory->invoke(
"unsubscribe", request);
1109 BEAST_EXPECT(goodSubRPC(jv));
1115 sendPayments(env, env.
master, bob, 30, 300);
1117 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1118 jv = wscTxHistory->invoke(
"subscribe", request);
1120 bobFullHistoryVec.
clear();
1122 getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second);
1123 jv = wscTxHistory->invoke(
"unsubscribe", request);
1125 request[jss::account_history_tx_stream][jss::account] =
1126 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1127 jv = wscTxHistory->invoke(
"subscribe", request);
1128 genesisFullHistoryVec.
clear();
1130 getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
1131 jv = wscTxHistory->invoke(
"unsubscribe", request);
1134 std::get<1>(bobFullHistoryVec.back()) ==
1135 std::get<1>(genesisFullHistoryVec.back()));
1148 env.
fund(
XRP(222222), accounts);
1154 stream[jss::accounts].append(alice.
human());
1155 auto jv = wscAccount->invoke(
"subscribe", stream);
1157 sendPayments(env, alice, bob, 5, 1);
1158 sendPayments(env, alice, bob, 5, 1);
1159 IdxHashVec accountVec;
1160 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1166 request[jss::account_history_tx_stream][jss::account] =
1168 jv = wscTxHistory->invoke(
"subscribe", request);
1171 IdxHashVec txHistoryVec;
1172 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1174 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1179 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1184 IdxHashVec initFundTxns;
1186 getTxHash(*wscTxHistory, initFundTxns, 10).second) ||
1187 !BEAST_EXPECT(checkBoundary(initFundTxns,
false)))
1192 sendPayments(env, alice, bob, 10, 1);
1193 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1195 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1197 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1202 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1205 wscTxHistory->invoke(
"unsubscribe", request);
1206 wscAccount->invoke(
"unsubscribe", stream);
1215 auto const USD_a = alice[
"USD"];
1218 env.
fund(
XRP(333333), accounts);
1219 env.
trust(USD_a(20000), carol);
1222 auto mixedPayments = [&]() ->
int {
1223 sendPayments(env, alice, carol, 1, 0);
1224 env(
pay(alice, carol, USD_a(100)));
1232 request[jss::account_history_tx_stream][jss::account] =
1235 auto jv = ws->invoke(
"subscribe", request);
1239 getTxHash(*ws, tempVec, 100);
1242 auto count = mixedPayments();
1244 if (!BEAST_EXPECT(getTxHash(*ws, vec1, count).first))
1246 ws->invoke(
"unsubscribe", request);
1255 env.
fund(
XRP(444444), accounts);
1259 auto oneRound = [&](
int numPayments) {
1260 return sendPayments(env, alice, carol, numPayments, 300);
1266 request[jss::account_history_tx_stream][jss::account] =
1269 auto jv = wscLong->invoke(
"subscribe", request);
1273 getTxHash(*wscLong, tempVec, 100);
1277 for (
int kk = 2; kk < 10; ++kk)
1279 auto count = oneRound(kk);
1281 if (!BEAST_EXPECT(getTxHash(*wscLong, vec1, count).first))
1286 auto jv = wscShort->invoke(
"subscribe", request);
1288 if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first))
1290 if (!BEAST_EXPECT(hashCompare(vec1, vec2,
true)))
1292 wscShort->invoke(
"unsubscribe", request);
1300 using namespace test::jtx;
1318BEAST_DEFINE_TESTSUITE(Subscribe, app,
ripple);
void clear()
Remove all object members and array elements.
testcase_t testcase
Memberspace for declaring test cases.
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual NetworkOPs & getOPs()=0
virtual LoadManager & getLoadManager()=0
virtual void reportFeeChange()=0
void testTransactions_APIv2()
void run() override
Runs the suite.
void testHistoryTxStream()
void testTransactions_APIv1()
void testValidations(FeatureBitset features)
void testSubErrors(bool subscribe)
virtual std::optional< Json::Value > getMsg(std::chrono::milliseconds const &timeout=std::chrono::milliseconds{ 0})=0
Retrieve a message.
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Env & apply(JsonValue &&jv, FN const &... fN)
Apply funclets and submit.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
void memoize(Account const &account)
Associate AccountID with account.
Set the regular signature on a JTx.
@ arrayValue
array value (ordered list)
@ intValue
signed integer value
@ objectValue
object value (collection of name/value pairs).
@ uintValue
unsigned integer value
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
static autofill_t const autofill
std::unique_ptr< Config > no_admin(std::unique_ptr< Config >)
adjust config so no admin ports are enabled
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
XRP_t const XRP
Converts to XRP Issue or STAmount.
FeatureBitset supported_amendments()
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.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
SecretKey generateSecretKey(KeyType type, Seed const &seed)
Generate a new secret key deterministically.
bool isFlagLedger(LedgerIndex seq)
Returns true if the given ledgerIndex is a flag ledgerIndex.
constexpr std::uint32_t vfFullyCanonicalSig
std::string to_string(base_uint< Bits, Tag > const &a)
constexpr std::uint32_t vfFullValidation
Set the sequence number on a JTx.