18 #include <ripple/app/main/LoadManager.h>
19 #include <ripple/app/misc/LoadFeeTrack.h>
20 #include <ripple/app/misc/NetworkOPs.h>
21 #include <ripple/beast/unit_test.h>
22 #include <ripple/core/ConfigSections.h>
23 #include <ripple/protocol/Feature.h>
24 #include <ripple/protocol/jss.h>
26 #include <test/jtx/WSClient.h>
27 #include <test/jtx/envconfig.h>
38 using namespace std::chrono_literals;
47 stream[jss::streams].append(
"server");
48 auto jv = wsc->invoke(
"subscribe", stream);
49 if (wsc->version() == 2)
52 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
54 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
55 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
57 BEAST_EXPECT(jv[jss::status] ==
"success");
69 for (
int i = 0; i < 5; ++i)
74 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
75 return jv[jss::type] ==
"serverStatus";
81 auto jv = wsc->invoke(
"unsubscribe", stream);
82 if (wsc->version() == 2)
85 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
87 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
88 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
90 BEAST_EXPECT(jv[jss::status] ==
"success");
96 for (
int i = 0; i < 5; ++i)
101 auto jvo = wsc->getMsg(10ms);
102 BEAST_EXPECTS(!jvo,
"getMsg: " +
to_string(jvo.value()));
109 using namespace std::chrono_literals;
118 stream[jss::streams].append(
"ledger");
119 auto jv = wsc->invoke(
"subscribe", stream);
120 if (wsc->version() == 2)
123 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
125 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
126 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
128 BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 2);
136 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
137 return jv[jss::ledger_index] == 3;
146 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
147 return jv[jss::ledger_index] == 4;
152 auto jv = wsc->invoke(
"unsubscribe", stream);
153 if (wsc->version() == 2)
156 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
158 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
159 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
161 BEAST_EXPECT(jv[jss::status] ==
"success");
167 using namespace std::chrono_literals;
176 stream[jss::streams].append(
"transactions");
177 auto jv = wsc->invoke(
"subscribe", stream);
178 if (wsc->version() == 2)
181 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
183 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
184 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
186 BEAST_EXPECT(jv[jss::status] ==
"success");
194 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
195 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
196 [
"NewFields"][jss::Account] ==
197 Account(
"alice").human();
201 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
202 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
203 [
"FinalFields"][jss::Account] ==
204 Account(
"alice").human();
211 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
212 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
213 [
"NewFields"][jss::Account] == Account(
"bob").human();
217 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
218 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
219 [
"FinalFields"][jss::Account] ==
220 Account(
"bob").human();
226 auto jv = wsc->invoke(
"unsubscribe", stream);
227 if (wsc->version() == 2)
230 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
232 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
233 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
235 BEAST_EXPECT(jv[jss::status] ==
"success");
242 stream[jss::accounts].append(
Account(
"alice").human());
243 auto jv = wsc->invoke(
"subscribe", stream);
244 if (wsc->version() == 2)
247 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
249 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
250 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
252 BEAST_EXPECT(jv[jss::status] ==
"success");
259 BEAST_EXPECT(!wsc->getMsg(10ms));
266 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
267 return jv[jss::meta][
"AffectedNodes"][1u][
"ModifiedNode"]
268 [
"FinalFields"][jss::Account] ==
269 Account(
"alice").human();
272 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
273 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
274 [
"NewFields"][
"LowLimit"][jss::issuer] ==
275 Account(
"alice").human();
280 auto jv = wsc->invoke(
"unsubscribe", stream);
281 if (wsc->version() == 2)
284 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
286 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
287 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
289 BEAST_EXPECT(jv[jss::status] ==
"success");
303 stream[jss::streams].append(
"manifests");
304 auto jv = wsc->invoke(
"subscribe", stream);
305 if (wsc->version() == 2)
308 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
310 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
311 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
313 BEAST_EXPECT(jv[jss::status] ==
"success");
317 auto jv = wsc->invoke(
"unsubscribe", stream);
318 if (wsc->version() == 2)
321 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
323 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
324 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
326 BEAST_EXPECT(jv[jss::status] ==
"success");
335 auto& cfg = env.app().config();
336 if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
338 auto const parsedseed =
339 parseBase58<Seed>(cfg.section(SECTION_VALIDATION_SEED).values()[0]);
340 if (!BEAST_EXPECT(parsedseed))
355 stream[jss::streams].append(
"validations");
356 auto jv = wsc->invoke(
"subscribe", stream);
357 if (wsc->version() == 2)
360 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
362 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
363 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
365 BEAST_EXPECT(jv[jss::status] ==
"success");
370 auto validValidationFields = [&env, &valPublicKey](
372 if (jv[jss::type] !=
"validationReceived")
375 if (jv[jss::validation_public_key].asString() != valPublicKey)
378 if (jv[jss::ledger_hash] !=
382 if (jv[jss::ledger_index] !=
389 if (jv[jss::full] !=
true)
392 if (jv.isMember(jss::load_fee))
395 if (!jv.isMember(jss::signature))
398 if (!jv.isMember(jss::signing_time))
401 if (!jv.isMember(jss::cookie))
404 if (!jv.isMember(jss::validated_hash))
409 (env.closed()->info().seq + 1) % 256 == 0;
425 while (env.closed()->info().seq < 300)
428 using namespace std::chrono_literals;
429 BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
434 auto jv = wsc->invoke(
"unsubscribe", stream);
435 if (wsc->version() == 2)
438 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
440 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
441 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
443 BEAST_EXPECT(jv[jss::status] ==
"success");
450 testcase(
"Subscribe by url");
454 jv[jss::url] =
"http://localhost/events";
455 jv[jss::url_username] =
"admin";
456 jv[jss::url_password] =
"password";
458 jv[jss::streams][0u] =
"validations";
459 auto jr = env.rpc(
"json",
"subscribe",
to_string(jv))[jss::result];
460 BEAST_EXPECT(jr[jss::status] ==
"success");
462 jv[jss::streams][0u] =
"ledger";
463 jr = env.rpc(
"json",
"subscribe",
to_string(jv))[jss::result];
464 BEAST_EXPECT(jr[jss::status] ==
"success");
466 jr = env.rpc(
"json",
"unsubscribe",
to_string(jv))[jss::result];
467 BEAST_EXPECT(jr[jss::status] ==
"success");
469 jv[jss::streams][0u] =
"validations";
470 jr = env.rpc(
"json",
"unsubscribe",
to_string(jv))[jss::result];
471 BEAST_EXPECT(jr[jss::status] ==
"success");
478 auto const method = subscribe ?
"subscribe" :
"unsubscribe";
479 testcase <<
"Error cases for " << method;
485 auto jr = env.rpc(
"json", method,
"{}")[jss::result];
486 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
487 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
492 jv[jss::url] =
"not-a-url";
493 jv[jss::username] =
"admin";
494 jv[jss::password] =
"password";
495 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
498 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
499 BEAST_EXPECT(jr[jss::error_message] ==
"Failed to parse url.");
507 jv[jss::url] =
"ftp://scheme.not.supported.tld";
508 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
511 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
513 jr[jss::error_message] ==
514 "Only http and https is supported.");
521 jv[jss::url] =
"no-url";
523 env_nonadmin.rpc(
"json", method,
to_string(jv))[jss::result];
524 BEAST_EXPECT(jr[jss::error] ==
"noPermission");
526 jr[jss::error_message] ==
527 "You don't have permission for this command.");
539 for (
auto const& f : {jss::accounts_proposed, jss::accounts})
541 for (
auto const& nonArray : nonArrays)
545 auto jr = wsc->invoke(method, jv)[jss::result];
546 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
547 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
553 auto jr = wsc->invoke(method, jv)[jss::result];
554 BEAST_EXPECT(jr[jss::error] ==
"actMalformed");
555 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
559 for (
auto const& nonArray : nonArrays)
562 jv[jss::books] = nonArray;
563 auto jr = wsc->invoke(method, jv)[jss::result];
564 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
565 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
571 jv[jss::books][0u] = 1;
572 auto jr = wsc->invoke(method, jv)[jss::result];
573 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
574 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
583 auto jr = wsc->invoke(method, jv)[jss::result];
584 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
586 jr[jss::error_message] ==
"Source currency is malformed.");
595 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"ZZZZ";
596 auto jr = wsc->invoke(method, jv)[jss::result];
597 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
599 jr[jss::error_message] ==
"Source currency is malformed.");
608 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
609 jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
610 auto jr = wsc->invoke(method, jv)[jss::result];
611 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
613 jr[jss::error_message] ==
"Source issuer is malformed.");
622 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
623 jv[jss::books][0u][jss::taker_pays][jss::issuer] =
625 auto jr = wsc->invoke(method, jv)[jss::result];
626 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
628 jr[jss::error_message] ==
"Source issuer is malformed.");
635 jv[jss::books][0u][jss::taker_pays] =
636 Account{
"gateway"}[
"USD"](1).value().getJson(
639 auto jr = wsc->invoke(method, jv)[jss::result];
642 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
644 jr[jss::error_message] ==
645 "Destination amount/currency/issuer is malformed.");
652 jv[jss::books][0u][jss::taker_pays] =
653 Account{
"gateway"}[
"USD"](1).value().getJson(
655 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"ZZZZ";
656 auto jr = wsc->invoke(method, jv)[jss::result];
659 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
661 jr[jss::error_message] ==
662 "Destination amount/currency/issuer is malformed.");
669 jv[jss::books][0u][jss::taker_pays] =
670 Account{
"gateway"}[
"USD"](1).value().getJson(
672 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
673 jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
674 auto jr = wsc->invoke(method, jv)[jss::result];
675 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
677 jr[jss::error_message] ==
"Destination issuer is malformed.");
684 jv[jss::books][0u][jss::taker_pays] =
685 Account{
"gateway"}[
"USD"](1).value().getJson(
687 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
688 jv[jss::books][0u][jss::taker_gets][jss::issuer] =
690 auto jr = wsc->invoke(method, jv)[jss::result];
691 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
693 jr[jss::error_message] ==
"Destination issuer is malformed.");
700 jv[jss::books][0u][jss::taker_pays] =
701 Account{
"gateway"}[
"USD"](1).value().getJson(
703 jv[jss::books][0u][jss::taker_gets] =
704 Account{
"gateway"}[
"USD"](1).value().getJson(
706 auto jr = wsc->invoke(method, jv)[jss::result];
707 BEAST_EXPECT(jr[jss::error] ==
"badMarket");
708 BEAST_EXPECT(jr[jss::error_message] ==
"No such market.");
711 for (
auto const& nonArray : nonArrays)
714 jv[jss::streams] = nonArray;
715 auto jr = wsc->invoke(method, jv)[jss::result];
716 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
717 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
723 jv[jss::streams][0u] = 1;
724 auto jr = wsc->invoke(method, jv)[jss::result];
725 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
726 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
732 jv[jss::streams][0u] =
"not_a_stream";
733 auto jr = wsc->invoke(method, jv)[jss::result];
734 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
735 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
742 testcase(
"HistoryTxStream");
744 using namespace std::chrono_literals;
757 auto goodSubRPC = [](
Json::Value const& subReply) ->
bool {
758 return subReply.isMember(jss::result) &&
759 subReply[jss::result].isMember(jss::status) &&
760 subReply[jss::result][jss::status] == jss::success;
771 bool first_flag =
false;
773 for (
int i = 0; i < numReplies; ++i)
776 auto reply = wsc.
getMsg(100ms);
780 if (r.isMember(jss::account_history_tx_index))
781 idx = r[jss::account_history_tx_index].asInt();
782 if (r.isMember(jss::account_history_tx_first))
784 if (r.isMember(jss::transaction) &&
785 r[jss::transaction].isMember(jss::hash))
788 idx, r[jss::transaction][jss::hash].asString());
792 return {
false, first_flag};
795 return {
true, first_flag};
802 auto sendPayments = [](
Env& env,
810 for (
int i = 0; i < newTxns; ++i)
812 auto& from = (i % 2 == 0) ? a : b;
813 auto& to = (i % 2 == 0) ? b : a;
820 for (
int i = 0; i < ledgersToClose; ++i)
830 auto hashCompare = [](IdxHashVec
const& accountVec,
831 IdxHashVec
const& txHistoryVec,
832 bool sizeCompare) ->
bool {
833 if (accountVec.empty() || txHistoryVec.empty())
835 if (sizeCompare && accountVec.size() != (txHistoryVec.size()))
839 for (
auto const& tx : txHistoryVec)
841 txHistoryMap.
emplace(tx.second, tx.first);
845 if (i >= accountVec.size())
847 auto it = txHistoryMap.
find(accountVec[i].second);
848 if (it == txHistoryMap.
end())
853 auto firstHistoryIndex = getHistoryIndex(0);
854 if (!firstHistoryIndex)
856 for (
std::size_t i = 1; i < accountVec.size(); ++i)
858 if (
auto idx = getHistoryIndex(i);
859 !idx || *idx != *firstHistoryIndex + i)
878 request[jss::account_history_tx_stream][jss::account] =
880 auto jv = wscTxHistory->invoke(
"subscribe", request);
881 if (!BEAST_EXPECT(goodSubRPC(jv)))
883 jv = wscTxHistory->invoke(
"subscribe", request);
884 BEAST_EXPECT(!goodSubRPC(jv));
889 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
891 jv = wscTxHistory->invoke(
"unsubscribe", request);
892 if (!BEAST_EXPECT(goodSubRPC(jv)))
895 sendPayments(env, env.
master, alice, 1, 1, 123456);
898 auto r = getTxHash(*wscTxHistory, vec, 1);
899 if (!BEAST_EXPECT(r.first && r.second))
905 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
907 jv = wscTxHistory->invoke(
"unsubscribe", request);
908 BEAST_EXPECT(goodSubRPC(jv));
910 sendPayments(env, env.
master, alice, 1, 1);
911 r = getTxHash(*wscTxHistory, vec, 1);
912 BEAST_EXPECT(!r.first);
924 request[jss::account_history_tx_stream][jss::account] =
925 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
926 auto jv = wscTxHistory->invoke(
"subscribe", request);
927 if (!BEAST_EXPECT(goodSubRPC(jv)))
929 IdxHashVec genesisFullHistoryVec;
931 !getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first))
938 sendPayments(env, env.
master, bob, 1, 1, 654321);
940 auto r = getTxHash(*wscTxHistory, genesisFullHistoryVec, 1);
941 if (!BEAST_EXPECT(r.first && r.second))
944 request[jss::account_history_tx_stream][jss::account] = bob.
human();
945 jv = wscTxHistory->invoke(
"subscribe", request);
946 if (!BEAST_EXPECT(goodSubRPC(jv)))
948 IdxHashVec bobFullHistoryVec;
949 r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
950 if (!BEAST_EXPECT(r.first && r.second))
953 bobFullHistoryVec.back().second ==
954 genesisFullHistoryVec.back().second);
959 jv = wscTxHistory->invoke(
"unsubscribe", request);
960 if (!BEAST_EXPECT(goodSubRPC(jv)))
962 request[jss::account_history_tx_stream][jss::account] =
963 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
964 jv = wscTxHistory->invoke(
"unsubscribe", request);
965 BEAST_EXPECT(goodSubRPC(jv));
971 sendPayments(env, env.
master, bob, 30, 300);
973 request[jss::account_history_tx_stream][jss::account] = bob.
human();
974 jv = wscTxHistory->invoke(
"subscribe", request);
976 bobFullHistoryVec.
clear();
978 getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second);
979 jv = wscTxHistory->invoke(
"unsubscribe", request);
981 request[jss::account_history_tx_stream][jss::account] =
982 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
983 jv = wscTxHistory->invoke(
"subscribe", request);
984 genesisFullHistoryVec.
clear();
986 getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
987 jv = wscTxHistory->invoke(
"unsubscribe", request);
990 bobFullHistoryVec.back().second ==
991 genesisFullHistoryVec.back().second);
1004 env.
fund(
XRP(222222), accounts);
1010 stream[jss::accounts].append(alice.
human());
1011 auto jv = wscAccount->invoke(
"subscribe", stream);
1013 sendPayments(env, alice, bob, 5, 1);
1014 sendPayments(env, alice, bob, 5, 1);
1015 IdxHashVec accountVec;
1016 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1022 request[jss::account_history_tx_stream][jss::account] =
1024 jv = wscTxHistory->invoke(
"subscribe", request);
1027 IdxHashVec txHistoryVec;
1028 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1030 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1035 IdxHashVec initFundTxns;
1037 getTxHash(*wscTxHistory, initFundTxns, 10).second))
1042 sendPayments(env, alice, bob, 10, 1);
1043 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1045 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1047 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1049 wscTxHistory->invoke(
"unsubscribe", request);
1050 wscAccount->invoke(
"unsubscribe", stream);
1059 auto const USD_a = alice[
"USD"];
1062 env.
fund(
XRP(333333), accounts);
1063 env.
trust(USD_a(20000), carol);
1066 auto mixedPayments = [&]() ->
int {
1067 sendPayments(env, alice, carol, 1, 0);
1068 env(
pay(alice, carol, USD_a(100)));
1076 request[jss::account_history_tx_stream][jss::account] =
1079 auto jv = ws->invoke(
"subscribe", request);
1083 getTxHash(*ws, tempVec, 100);
1086 auto count = mixedPayments();
1088 if (!BEAST_EXPECT(getTxHash(*ws, vec1, count).first))
1090 ws->invoke(
"unsubscribe", request);
1099 env.
fund(
XRP(444444), accounts);
1103 auto oneRound = [&](
int numPayments) {
1104 return sendPayments(env, alice, carol, numPayments, 300);
1110 request[jss::account_history_tx_stream][jss::account] =
1113 auto jv = wscLong->invoke(
"subscribe", request);
1117 getTxHash(*wscLong, tempVec, 100);
1121 for (
int kk = 2; kk < 10; ++kk)
1123 auto count = oneRound(kk);
1125 if (!BEAST_EXPECT(getTxHash(*wscLong, vec1, count).first))
1130 auto jv = wscShort->invoke(
"subscribe", request);
1132 if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first))
1134 if (!BEAST_EXPECT(hashCompare(vec1, vec2,
true)))
1136 wscShort->invoke(
"unsubscribe", request);
1144 using namespace test::jtx;