19#include <test/jtx/WSClient.h>
20#include <test/jtx/envconfig.h>
22#include <xrpld/app/main/LoadManager.h>
23#include <xrpld/app/misc/LoadFeeTrack.h>
24#include <xrpld/app/misc/NetworkOPs.h>
25#include <xrpld/core/ConfigSections.h>
27#include <xrpl/beast/unit_test.h>
28#include <xrpl/json/json_value.h>
29#include <xrpl/protocol/Feature.h>
30#include <xrpl/protocol/jss.h>
43 using namespace std::chrono_literals;
52 stream[jss::streams].append(
"server");
53 auto jv = wsc->invoke(
"subscribe", stream);
54 if (wsc->version() == 2)
57 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
59 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
60 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
62 BEAST_EXPECT(jv[jss::status] ==
"success");
74 for (
int i = 0; i < 5; ++i)
79 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
80 return jv[jss::type] ==
"serverStatus";
86 auto jv = wsc->invoke(
"unsubscribe", stream);
87 if (wsc->version() == 2)
90 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
92 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
93 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
95 BEAST_EXPECT(jv[jss::status] ==
"success");
101 for (
int i = 0; i < 5; ++i)
106 auto jvo = wsc->getMsg(10ms);
107 BEAST_EXPECTS(!jvo,
"getMsg: " +
to_string(jvo.value()));
114 using namespace std::chrono_literals;
123 stream[jss::streams].append(
"ledger");
124 auto jv = wsc->invoke(
"subscribe", stream);
125 if (wsc->version() == 2)
128 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
130 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
131 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
133 BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 2);
141 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
142 return jv[jss::ledger_index] == 3;
151 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
152 return jv[jss::ledger_index] == 4;
157 auto jv = wsc->invoke(
"unsubscribe", stream);
158 if (wsc->version() == 2)
161 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
163 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
164 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
166 BEAST_EXPECT(jv[jss::status] ==
"success");
172 using namespace std::chrono_literals;
175 auto baseFee = env.
current()->fees().base.drops();
182 stream[jss::streams].append(
"transactions");
183 auto jv = wsc->invoke(
"subscribe", stream);
184 if (wsc->version() == 2)
187 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
189 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
190 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
192 BEAST_EXPECT(jv[jss::status] ==
"success");
200 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
201 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
202 [
"NewFields"][jss::Account]
203 == Account(
"alice").human() &&
204 jv[jss::transaction][jss::TransactionType]
206 jv[jss::transaction][jss::DeliverMax]
207 == std::to_string(10000000000 + baseFee) &&
208 jv[jss::transaction][jss::Fee]
209 == std::to_string(baseFee) &&
210 jv[jss::transaction][jss::Sequence]
215 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
216 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
217 [
"FinalFields"][jss::Account] ==
218 Account(
"alice").human();
225 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
226 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
227 [
"NewFields"][jss::Account]
228 == Account(
"bob").human() &&
229 jv[jss::transaction][jss::TransactionType]
231 jv[jss::transaction][jss::DeliverMax]
232 == std::to_string(10000000000 + baseFee) &&
233 jv[jss::transaction][jss::Fee]
234 == std::to_string(baseFee) &&
235 jv[jss::transaction][jss::Sequence]
240 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
241 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
242 [
"FinalFields"][jss::Account] ==
243 Account(
"bob").human();
249 auto jv = wsc->invoke(
"unsubscribe", stream);
250 if (wsc->version() == 2)
253 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
255 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
256 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
258 BEAST_EXPECT(jv[jss::status] ==
"success");
265 stream[jss::accounts].append(
Account(
"alice").human());
266 auto jv = wsc->invoke(
"subscribe", stream);
267 if (wsc->version() == 2)
270 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
272 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
273 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
275 BEAST_EXPECT(jv[jss::status] ==
"success");
282 BEAST_EXPECT(!wsc->getMsg(10ms));
289 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
290 return jv[jss::meta][
"AffectedNodes"][1u][
"ModifiedNode"]
291 [
"FinalFields"][jss::Account] ==
292 Account(
"alice").human();
295 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
296 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
297 [
"NewFields"][
"LowLimit"][jss::issuer] ==
298 Account(
"alice").human();
303 auto jv = wsc->invoke(
"unsubscribe", stream);
304 if (wsc->version() == 2)
307 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
309 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
310 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
312 BEAST_EXPECT(jv[jss::status] ==
"success");
318 testcase(
"transactions API version 2");
320 using namespace std::chrono_literals;
323 cfg->FEES.reference_fee = 10;
331 stream[jss::api_version] = 2;
333 stream[jss::streams].append(
"transactions");
334 auto jv = wsc->invoke(
"subscribe", stream);
335 if (wsc->version() == 2)
338 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
340 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
341 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
343 BEAST_EXPECT(jv[jss::status] ==
"success");
351 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
352 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
353 [
"NewFields"][jss::Account]
354 == Account(
"alice").human() &&
355 jv[jss::close_time_iso]
356 ==
"2000-01-01T00:00:10Z" &&
357 jv[jss::validated] == true &&
358 jv[jss::ledger_hash] ==
359 "0F1A9E0C109ADEF6DA2BDE19217C12BBEC57174CBDBD212B0EBDC1CEDB"
361 !jv[jss::inLedger] &&
362 jv[jss::ledger_index] == 3 &&
363 jv[jss::tx_json][jss::TransactionType]
365 jv[jss::tx_json][jss::DeliverMax]
367 !jv[jss::tx_json].isMember(jss::Amount) &&
368 jv[jss::tx_json][jss::Fee]
370 jv[jss::tx_json][jss::Sequence]
375 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
376 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
377 [
"FinalFields"][jss::Account] ==
378 Account(
"alice").human();
384 auto jv = wsc->invoke(
"unsubscribe", stream);
385 if (wsc->version() == 2)
388 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
390 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
391 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
393 BEAST_EXPECT(jv[jss::status] ==
"success");
408 stream[jss::streams].append(
"manifests");
409 auto jv = wsc->invoke(
"subscribe", stream);
410 if (wsc->version() == 2)
413 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
415 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
416 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
418 BEAST_EXPECT(jv[jss::status] ==
"success");
422 auto jv = wsc->invoke(
"unsubscribe", stream);
423 if (wsc->version() == 2)
426 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
428 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
429 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
431 BEAST_EXPECT(jv[jss::status] ==
"success");
440 auto& cfg = env.app().config();
441 if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
443 auto const parsedseed =
444 parseBase58<Seed>(cfg.section(SECTION_VALIDATION_SEED).values()[0]);
445 if (!BEAST_EXPECT(parsedseed))
460 stream[jss::streams].append(
"validations");
461 auto jv = wsc->invoke(
"subscribe", stream);
462 if (wsc->version() == 2)
465 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
467 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
468 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
470 BEAST_EXPECT(jv[jss::status] ==
"success");
475 auto validValidationFields = [&env, &valPublicKey](
477 if (jv[jss::type] !=
"validationReceived")
480 if (jv[jss::validation_public_key].asString() != valPublicKey)
483 if (jv[jss::ledger_hash] !=
487 if (jv[jss::ledger_index] !=
494 if (jv[jss::full] !=
true)
497 if (jv.isMember(jss::load_fee))
500 if (!jv.isMember(jss::signature))
503 if (!jv.isMember(jss::signing_time))
506 if (!jv.isMember(jss::cookie))
509 if (!jv.isMember(jss::validated_hash))
514 (env.closed()->info().seq + 1) % 256 == 0;
530 while (env.closed()->info().seq < 300)
533 using namespace std::chrono_literals;
534 BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
539 auto jv = wsc->invoke(
"unsubscribe", stream);
540 if (wsc->version() == 2)
543 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
545 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
546 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
548 BEAST_EXPECT(jv[jss::status] ==
"success");
559 jv[jss::url] =
"http://localhost/events";
560 jv[jss::url_username] =
"admin";
561 jv[jss::url_password] =
"password";
563 jv[jss::streams][0u] =
"validations";
564 auto jr = env.rpc(
"json",
"subscribe",
to_string(jv))[jss::result];
565 BEAST_EXPECT(jr[jss::status] ==
"success");
567 jv[jss::streams][0u] =
"ledger";
568 jr = env.rpc(
"json",
"subscribe",
to_string(jv))[jss::result];
569 BEAST_EXPECT(jr[jss::status] ==
"success");
571 jr = env.rpc(
"json",
"unsubscribe",
to_string(jv))[jss::result];
572 BEAST_EXPECT(jr[jss::status] ==
"success");
574 jv[jss::streams][0u] =
"validations";
575 jr = env.rpc(
"json",
"unsubscribe",
to_string(jv))[jss::result];
576 BEAST_EXPECT(jr[jss::status] ==
"success");
583 auto const method = subscribe ?
"subscribe" :
"unsubscribe";
584 testcase <<
"Error cases for " << method;
590 auto jr = env.rpc(
"json", method,
"{}")[jss::result];
591 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
592 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
597 jv[jss::url] =
"not-a-url";
598 jv[jss::username] =
"admin";
599 jv[jss::password] =
"password";
600 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
603 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
604 BEAST_EXPECT(jr[jss::error_message] ==
"Failed to parse url.");
612 jv[jss::url] =
"ftp://scheme.not.supported.tld";
613 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
616 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
618 jr[jss::error_message] ==
619 "Only http and https is supported.");
626 jv[jss::url] =
"no-url";
628 env_nonadmin.rpc(
"json", method,
to_string(jv))[jss::result];
629 BEAST_EXPECT(jr[jss::error] ==
"noPermission");
631 jr[jss::error_message] ==
632 "You don't have permission for this command.");
644 for (
auto const& f : {jss::accounts_proposed, jss::accounts})
646 for (
auto const& nonArray : nonArrays)
650 auto jr = wsc->invoke(method, jv)[jss::result];
651 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
652 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
658 auto jr = wsc->invoke(method, jv)[jss::result];
659 BEAST_EXPECT(jr[jss::error] ==
"actMalformed");
660 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
664 for (
auto const& nonArray : nonArrays)
667 jv[jss::books] = nonArray;
668 auto jr = wsc->invoke(method, jv)[jss::result];
669 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
670 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
676 jv[jss::books][0u] = 1;
677 auto jr = wsc->invoke(method, jv)[jss::result];
678 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
679 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
688 auto jr = wsc->invoke(method, jv)[jss::result];
689 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
691 jr[jss::error_message] ==
"Source currency is malformed.");
700 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"ZZZZ";
701 auto jr = wsc->invoke(method, jv)[jss::result];
702 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
704 jr[jss::error_message] ==
"Source currency is malformed.");
713 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
714 jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
715 auto jr = wsc->invoke(method, jv)[jss::result];
716 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
718 jr[jss::error_message] ==
"Source issuer is malformed.");
727 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
728 jv[jss::books][0u][jss::taker_pays][jss::issuer] =
730 auto jr = wsc->invoke(method, jv)[jss::result];
731 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
733 jr[jss::error_message] ==
"Source issuer is malformed.");
740 jv[jss::books][0u][jss::taker_pays] =
741 Account{
"gateway"}[
"USD"](1).value().getJson(
744 auto jr = wsc->invoke(method, jv)[jss::result];
747 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
749 jr[jss::error_message] ==
750 "Destination amount/currency/issuer is malformed.");
757 jv[jss::books][0u][jss::taker_pays] =
758 Account{
"gateway"}[
"USD"](1).value().getJson(
760 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"ZZZZ";
761 auto jr = wsc->invoke(method, jv)[jss::result];
764 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
766 jr[jss::error_message] ==
767 "Destination amount/currency/issuer is malformed.");
774 jv[jss::books][0u][jss::taker_pays] =
775 Account{
"gateway"}[
"USD"](1).value().getJson(
777 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
778 jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
779 auto jr = wsc->invoke(method, jv)[jss::result];
780 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
782 jr[jss::error_message] ==
"Destination issuer is malformed.");
789 jv[jss::books][0u][jss::taker_pays] =
790 Account{
"gateway"}[
"USD"](1).value().getJson(
792 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
793 jv[jss::books][0u][jss::taker_gets][jss::issuer] =
795 auto jr = wsc->invoke(method, jv)[jss::result];
796 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
798 jr[jss::error_message] ==
"Destination issuer is malformed.");
805 jv[jss::books][0u][jss::taker_pays] =
806 Account{
"gateway"}[
"USD"](1).value().getJson(
808 jv[jss::books][0u][jss::taker_gets] =
809 Account{
"gateway"}[
"USD"](1).value().getJson(
811 auto jr = wsc->invoke(method, jv)[jss::result];
812 BEAST_EXPECT(jr[jss::error] ==
"badMarket");
813 BEAST_EXPECT(jr[jss::error_message] ==
"No such market.");
816 for (
auto const& nonArray : nonArrays)
819 jv[jss::streams] = nonArray;
820 auto jr = wsc->invoke(method, jv)[jss::result];
821 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
822 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
828 jv[jss::streams][0u] = 1;
829 auto jr = wsc->invoke(method, jv)[jss::result];
830 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
831 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
837 jv[jss::streams][0u] =
"not_a_stream";
838 auto jr = wsc->invoke(method, jv)[jss::result];
839 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
840 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
849 using namespace std::chrono_literals;
862 auto goodSubRPC = [](
Json::Value const& subReply) ->
bool {
863 return subReply.isMember(jss::result) &&
864 subReply[jss::result].isMember(jss::status) &&
865 subReply[jss::result][jss::status] == jss::success;
876 bool first_flag =
false;
878 for (
int i = 0; i < numReplies; ++i)
881 auto reply = wsc.
getMsg(100ms);
885 if (r.isMember(jss::account_history_tx_index))
886 idx = r[jss::account_history_tx_index].asInt();
887 if (r.isMember(jss::account_history_tx_first))
889 bool boundary = r.isMember(jss::account_history_boundary);
890 int ledger_idx = r[jss::ledger_index].asInt();
891 if (r.isMember(jss::transaction) &&
892 r[jss::transaction].isMember(jss::hash))
894 auto t{r[jss::transaction]};
896 idx, t[jss::hash].asString(), boundary, ledger_idx);
900 return {
false, first_flag};
903 return {
true, first_flag};
910 auto sendPayments = [](
Env& env,
918 for (
int i = 0; i < newTxns; ++i)
920 auto& from = (i % 2 == 0) ? a : b;
921 auto& to = (i % 2 == 0) ? b : a;
928 for (
int i = 0; i < ledgersToClose; ++i)
938 auto hashCompare = [](IdxHashVec
const& accountVec,
939 IdxHashVec
const& txHistoryVec,
940 bool sizeCompare) ->
bool {
941 if (accountVec.empty() || txHistoryVec.empty())
943 if (sizeCompare && accountVec.size() != (txHistoryVec.size()))
947 for (
auto const& tx : txHistoryVec)
949 txHistoryMap.
emplace(std::get<1>(tx), std::get<0>(tx));
953 if (i >= accountVec.size())
955 auto it = txHistoryMap.
find(std::get<1>(accountVec[i]));
956 if (it == txHistoryMap.
end())
961 auto firstHistoryIndex = getHistoryIndex(0);
962 if (!firstHistoryIndex)
964 for (
std::size_t i = 1; i < accountVec.size(); ++i)
966 if (
auto idx = getHistoryIndex(i);
967 !idx || *idx != *firstHistoryIndex + i)
1003 auto checkBoundary = [](IdxHashVec
const& vec,
bool ) {
1004 size_t num_tx = vec.size();
1005 for (
size_t i = 0; i < num_tx; ++i)
1007 auto [idx, hash, boundary, ledger] = vec[i];
1008 if ((i + 1 == num_tx || ledger != std::get<3>(vec[i + 1])) !=
1028 request[jss::account_history_tx_stream][jss::account] =
1030 auto jv = wscTxHistory->invoke(
"subscribe", request);
1031 if (!BEAST_EXPECT(goodSubRPC(jv)))
1034 jv = wscTxHistory->invoke(
"subscribe", request);
1035 BEAST_EXPECT(!goodSubRPC(jv));
1040 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
1042 jv = wscTxHistory->invoke(
"unsubscribe", request);
1043 if (!BEAST_EXPECT(goodSubRPC(jv)))
1046 sendPayments(env, env.
master, alice, 1, 1, 123456);
1049 auto r = getTxHash(*wscTxHistory, vec, 1);
1050 if (!BEAST_EXPECT(r.first && r.second))
1056 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
1058 jv = wscTxHistory->invoke(
"unsubscribe", request);
1059 BEAST_EXPECT(goodSubRPC(jv));
1061 sendPayments(env, env.
master, alice, 1, 1);
1062 r = getTxHash(*wscTxHistory, vec, 1);
1063 BEAST_EXPECT(!r.first);
1074 request[jss::account_history_tx_stream][jss::account] =
1075 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1076 auto jv = wscTxHistory->invoke(
"subscribe", request);
1077 if (!BEAST_EXPECT(goodSubRPC(jv)))
1079 IdxHashVec genesisFullHistoryVec;
1081 !getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first))
1088 sendPayments(env, env.
master, bob, 1, 1, 654321);
1090 auto r = getTxHash(*wscTxHistory, genesisFullHistoryVec, 1);
1091 if (!BEAST_EXPECT(r.first && r.second))
1094 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1095 jv = wscTxHistory->invoke(
"subscribe", request);
1096 if (!BEAST_EXPECT(goodSubRPC(jv)))
1098 IdxHashVec bobFullHistoryVec;
1099 r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
1100 if (!BEAST_EXPECT(r.first && r.second))
1103 std::get<1>(bobFullHistoryVec.back()) ==
1104 std::get<1>(genesisFullHistoryVec.back()));
1109 jv = wscTxHistory->invoke(
"unsubscribe", request);
1110 if (!BEAST_EXPECT(goodSubRPC(jv)))
1112 request[jss::account_history_tx_stream][jss::account] =
1113 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1114 jv = wscTxHistory->invoke(
"unsubscribe", request);
1115 BEAST_EXPECT(goodSubRPC(jv));
1121 sendPayments(env, env.
master, bob, 30, 300);
1123 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1124 jv = wscTxHistory->invoke(
"subscribe", request);
1126 bobFullHistoryVec.
clear();
1128 getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second);
1129 jv = wscTxHistory->invoke(
"unsubscribe", request);
1131 request[jss::account_history_tx_stream][jss::account] =
1132 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1133 jv = wscTxHistory->invoke(
"subscribe", request);
1134 genesisFullHistoryVec.
clear();
1136 getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
1137 jv = wscTxHistory->invoke(
"unsubscribe", request);
1140 std::get<1>(bobFullHistoryVec.back()) ==
1141 std::get<1>(genesisFullHistoryVec.back()));
1154 env.
fund(
XRP(222222), accounts);
1160 stream[jss::accounts].append(alice.
human());
1161 auto jv = wscAccount->invoke(
"subscribe", stream);
1163 sendPayments(env, alice, bob, 5, 1);
1164 sendPayments(env, alice, bob, 5, 1);
1165 IdxHashVec accountVec;
1166 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1172 request[jss::account_history_tx_stream][jss::account] =
1174 jv = wscTxHistory->invoke(
"subscribe", request);
1177 IdxHashVec txHistoryVec;
1178 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1180 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1185 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1190 IdxHashVec initFundTxns;
1192 getTxHash(*wscTxHistory, initFundTxns, 10).second) ||
1193 !BEAST_EXPECT(checkBoundary(initFundTxns,
false)))
1198 sendPayments(env, alice, bob, 10, 1);
1199 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1201 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1203 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1208 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1211 wscTxHistory->invoke(
"unsubscribe", request);
1212 wscAccount->invoke(
"unsubscribe", stream);
1221 auto const USD_a = alice[
"USD"];
1224 env.
fund(
XRP(333333), accounts);
1225 env.
trust(USD_a(20000), carol);
1228 auto mixedPayments = [&]() ->
int {
1229 sendPayments(env, alice, carol, 1, 0);
1230 env(
pay(alice, carol, USD_a(100)));
1238 request[jss::account_history_tx_stream][jss::account] =
1241 auto jv = ws->invoke(
"subscribe", request);
1245 getTxHash(*ws, tempVec, 100);
1248 auto count = mixedPayments();
1250 if (!BEAST_EXPECT(getTxHash(*ws, vec1, count).first))
1252 ws->invoke(
"unsubscribe", request);
1261 env.
fund(
XRP(444444), accounts);
1265 auto oneRound = [&](
int numPayments) {
1266 return sendPayments(env, alice, carol, numPayments, 300);
1272 request[jss::account_history_tx_stream][jss::account] =
1275 auto jv = wscLong->invoke(
"subscribe", request);
1279 getTxHash(*wscLong, tempVec, 100);
1283 for (
int kk = 2; kk < 10; ++kk)
1285 auto count = oneRound(kk);
1287 if (!BEAST_EXPECT(getTxHash(*wscLong, vec1, count).first))
1292 auto jv = wscShort->invoke(
"subscribe", request);
1294 if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first))
1296 if (!BEAST_EXPECT(hashCompare(vec1, vec2,
true)))
1298 wscShort->invoke(
"unsubscribe", request);
1306 using namespace test::jtx;
1324BEAST_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.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
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.