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;
181 stream[jss::streams].append(
"transactions");
182 auto jv = wsc->invoke(
"subscribe", stream);
183 if (wsc->version() == 2)
186 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
188 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
189 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
191 BEAST_EXPECT(jv[jss::status] ==
"success");
199 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
200 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
201 [
"NewFields"][jss::Account]
202 == Account(
"alice").human() &&
203 jv[jss::transaction][jss::TransactionType]
205 jv[jss::transaction][jss::DeliverMax]
207 jv[jss::transaction][jss::Fee]
209 jv[jss::transaction][jss::Sequence]
214 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
215 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
216 [
"FinalFields"][jss::Account] ==
217 Account(
"alice").human();
224 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
225 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
226 [
"NewFields"][jss::Account]
227 == Account(
"bob").human() &&
228 jv[jss::transaction][jss::TransactionType]
230 jv[jss::transaction][jss::DeliverMax]
232 jv[jss::transaction][jss::Fee]
234 jv[jss::transaction][jss::Sequence]
239 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
240 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
241 [
"FinalFields"][jss::Account] ==
242 Account(
"bob").human();
248 auto jv = wsc->invoke(
"unsubscribe", stream);
249 if (wsc->version() == 2)
252 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
254 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
255 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
257 BEAST_EXPECT(jv[jss::status] ==
"success");
264 stream[jss::accounts].append(
Account(
"alice").human());
265 auto jv = wsc->invoke(
"subscribe", stream);
266 if (wsc->version() == 2)
269 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
271 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
272 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
274 BEAST_EXPECT(jv[jss::status] ==
"success");
281 BEAST_EXPECT(!wsc->getMsg(10ms));
288 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
289 return jv[jss::meta][
"AffectedNodes"][1u][
"ModifiedNode"]
290 [
"FinalFields"][jss::Account] ==
291 Account(
"alice").human();
294 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
295 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
296 [
"NewFields"][
"LowLimit"][jss::issuer] ==
297 Account(
"alice").human();
302 auto jv = wsc->invoke(
"unsubscribe", stream);
303 if (wsc->version() == 2)
306 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
308 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
309 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
311 BEAST_EXPECT(jv[jss::status] ==
"success");
317 testcase(
"transactions API version 2");
319 using namespace std::chrono_literals;
327 stream[jss::api_version] = 2;
329 stream[jss::streams].append(
"transactions");
330 auto jv = wsc->invoke(
"subscribe", stream);
331 if (wsc->version() == 2)
334 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
336 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
337 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
339 BEAST_EXPECT(jv[jss::status] ==
"success");
347 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
348 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
349 [
"NewFields"][jss::Account]
350 == Account(
"alice").human() &&
351 jv[jss::close_time_iso]
352 ==
"2000-01-01T00:00:10Z" &&
353 jv[jss::validated] == true &&
354 jv[jss::ledger_hash] ==
355 "0F1A9E0C109ADEF6DA2BDE19217C12BBEC57174CBDBD212B0EBDC1CEDB"
357 !jv[jss::inLedger] &&
358 jv[jss::ledger_index] == 3 &&
359 jv[jss::tx_json][jss::TransactionType]
361 jv[jss::tx_json][jss::DeliverMax]
363 !jv[jss::tx_json].isMember(jss::Amount) &&
364 jv[jss::tx_json][jss::Fee]
366 jv[jss::tx_json][jss::Sequence]
371 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
372 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
373 [
"FinalFields"][jss::Account] ==
374 Account(
"alice").human();
380 auto jv = wsc->invoke(
"unsubscribe", stream);
381 if (wsc->version() == 2)
384 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
386 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
387 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
389 BEAST_EXPECT(jv[jss::status] ==
"success");
404 stream[jss::streams].append(
"manifests");
405 auto jv = wsc->invoke(
"subscribe", stream);
406 if (wsc->version() == 2)
409 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
411 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
412 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
414 BEAST_EXPECT(jv[jss::status] ==
"success");
418 auto jv = wsc->invoke(
"unsubscribe", stream);
419 if (wsc->version() == 2)
422 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
424 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
425 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
427 BEAST_EXPECT(jv[jss::status] ==
"success");
436 auto& cfg = env.app().config();
437 if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
439 auto const parsedseed =
440 parseBase58<Seed>(cfg.section(SECTION_VALIDATION_SEED).values()[0]);
441 if (!BEAST_EXPECT(parsedseed))
456 stream[jss::streams].append(
"validations");
457 auto jv = wsc->invoke(
"subscribe", stream);
458 if (wsc->version() == 2)
461 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
463 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
464 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
466 BEAST_EXPECT(jv[jss::status] ==
"success");
471 auto validValidationFields = [&env, &valPublicKey](
473 if (jv[jss::type] !=
"validationReceived")
476 if (jv[jss::validation_public_key].asString() != valPublicKey)
479 if (jv[jss::ledger_hash] !=
483 if (jv[jss::ledger_index] !=
490 if (jv[jss::full] !=
true)
493 if (jv.isMember(jss::load_fee))
496 if (!jv.isMember(jss::signature))
499 if (!jv.isMember(jss::signing_time))
502 if (!jv.isMember(jss::cookie))
505 if (!jv.isMember(jss::validated_hash))
510 (env.closed()->info().seq + 1) % 256 == 0;
526 while (env.closed()->info().seq < 300)
529 using namespace std::chrono_literals;
530 BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
535 auto jv = wsc->invoke(
"unsubscribe", stream);
536 if (wsc->version() == 2)
539 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
541 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
542 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
544 BEAST_EXPECT(jv[jss::status] ==
"success");
555 jv[jss::url] =
"http://localhost/events";
556 jv[jss::url_username] =
"admin";
557 jv[jss::url_password] =
"password";
559 jv[jss::streams][0u] =
"validations";
560 auto jr = env.rpc(
"json",
"subscribe",
to_string(jv))[jss::result];
561 BEAST_EXPECT(jr[jss::status] ==
"success");
563 jv[jss::streams][0u] =
"ledger";
564 jr = env.rpc(
"json",
"subscribe",
to_string(jv))[jss::result];
565 BEAST_EXPECT(jr[jss::status] ==
"success");
567 jr = env.rpc(
"json",
"unsubscribe",
to_string(jv))[jss::result];
568 BEAST_EXPECT(jr[jss::status] ==
"success");
570 jv[jss::streams][0u] =
"validations";
571 jr = env.rpc(
"json",
"unsubscribe",
to_string(jv))[jss::result];
572 BEAST_EXPECT(jr[jss::status] ==
"success");
579 auto const method = subscribe ?
"subscribe" :
"unsubscribe";
580 testcase <<
"Error cases for " << method;
586 auto jr = env.rpc(
"json", method,
"{}")[jss::result];
587 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
588 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
593 jv[jss::url] =
"not-a-url";
594 jv[jss::username] =
"admin";
595 jv[jss::password] =
"password";
596 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
599 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
600 BEAST_EXPECT(jr[jss::error_message] ==
"Failed to parse url.");
608 jv[jss::url] =
"ftp://scheme.not.supported.tld";
609 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
612 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
614 jr[jss::error_message] ==
615 "Only http and https is supported.");
622 jv[jss::url] =
"no-url";
624 env_nonadmin.rpc(
"json", method,
to_string(jv))[jss::result];
625 BEAST_EXPECT(jr[jss::error] ==
"noPermission");
627 jr[jss::error_message] ==
628 "You don't have permission for this command.");
640 for (
auto const& f : {jss::accounts_proposed, jss::accounts})
642 for (
auto const& nonArray : nonArrays)
646 auto jr = wsc->invoke(method, jv)[jss::result];
647 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
648 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
654 auto jr = wsc->invoke(method, jv)[jss::result];
655 BEAST_EXPECT(jr[jss::error] ==
"actMalformed");
656 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
660 for (
auto const& nonArray : nonArrays)
663 jv[jss::books] = nonArray;
664 auto jr = wsc->invoke(method, jv)[jss::result];
665 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
666 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
672 jv[jss::books][0u] = 1;
673 auto jr = wsc->invoke(method, jv)[jss::result];
674 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
675 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
684 auto jr = wsc->invoke(method, jv)[jss::result];
685 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
687 jr[jss::error_message] ==
"Source currency is malformed.");
696 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"ZZZZ";
697 auto jr = wsc->invoke(method, jv)[jss::result];
698 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
700 jr[jss::error_message] ==
"Source currency is malformed.");
709 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
710 jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
711 auto jr = wsc->invoke(method, jv)[jss::result];
712 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
714 jr[jss::error_message] ==
"Source issuer is malformed.");
723 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
724 jv[jss::books][0u][jss::taker_pays][jss::issuer] =
726 auto jr = wsc->invoke(method, jv)[jss::result];
727 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
729 jr[jss::error_message] ==
"Source issuer is malformed.");
736 jv[jss::books][0u][jss::taker_pays] =
737 Account{
"gateway"}[
"USD"](1).value().getJson(
740 auto jr = wsc->invoke(method, jv)[jss::result];
743 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
745 jr[jss::error_message] ==
746 "Destination amount/currency/issuer is malformed.");
753 jv[jss::books][0u][jss::taker_pays] =
754 Account{
"gateway"}[
"USD"](1).value().getJson(
756 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"ZZZZ";
757 auto jr = wsc->invoke(method, jv)[jss::result];
760 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
762 jr[jss::error_message] ==
763 "Destination amount/currency/issuer is malformed.");
770 jv[jss::books][0u][jss::taker_pays] =
771 Account{
"gateway"}[
"USD"](1).value().getJson(
773 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
774 jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
775 auto jr = wsc->invoke(method, jv)[jss::result];
776 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
778 jr[jss::error_message] ==
"Destination issuer is malformed.");
785 jv[jss::books][0u][jss::taker_pays] =
786 Account{
"gateway"}[
"USD"](1).value().getJson(
788 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
789 jv[jss::books][0u][jss::taker_gets][jss::issuer] =
791 auto jr = wsc->invoke(method, jv)[jss::result];
792 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
794 jr[jss::error_message] ==
"Destination issuer is malformed.");
801 jv[jss::books][0u][jss::taker_pays] =
802 Account{
"gateway"}[
"USD"](1).value().getJson(
804 jv[jss::books][0u][jss::taker_gets] =
805 Account{
"gateway"}[
"USD"](1).value().getJson(
807 auto jr = wsc->invoke(method, jv)[jss::result];
808 BEAST_EXPECT(jr[jss::error] ==
"badMarket");
809 BEAST_EXPECT(jr[jss::error_message] ==
"No such market.");
812 for (
auto const& nonArray : nonArrays)
815 jv[jss::streams] = nonArray;
816 auto jr = wsc->invoke(method, jv)[jss::result];
817 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
818 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
824 jv[jss::streams][0u] = 1;
825 auto jr = wsc->invoke(method, jv)[jss::result];
826 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
827 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
833 jv[jss::streams][0u] =
"not_a_stream";
834 auto jr = wsc->invoke(method, jv)[jss::result];
835 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
836 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
845 using namespace std::chrono_literals;
858 auto goodSubRPC = [](
Json::Value const& subReply) ->
bool {
859 return subReply.isMember(jss::result) &&
860 subReply[jss::result].isMember(jss::status) &&
861 subReply[jss::result][jss::status] == jss::success;
872 bool first_flag =
false;
874 for (
int i = 0; i < numReplies; ++i)
877 auto reply = wsc.
getMsg(100ms);
881 if (r.isMember(jss::account_history_tx_index))
882 idx = r[jss::account_history_tx_index].asInt();
883 if (r.isMember(jss::account_history_tx_first))
885 bool boundary = r.isMember(jss::account_history_boundary);
886 int ledger_idx = r[jss::ledger_index].asInt();
887 if (r.isMember(jss::transaction) &&
888 r[jss::transaction].isMember(jss::hash))
890 auto t{r[jss::transaction]};
892 idx, t[jss::hash].asString(), boundary, ledger_idx);
896 return {
false, first_flag};
899 return {
true, first_flag};
906 auto sendPayments = [](
Env& env,
914 for (
int i = 0; i < newTxns; ++i)
916 auto& from = (i % 2 == 0) ? a : b;
917 auto& to = (i % 2 == 0) ? b : a;
924 for (
int i = 0; i < ledgersToClose; ++i)
934 auto hashCompare = [](IdxHashVec
const& accountVec,
935 IdxHashVec
const& txHistoryVec,
936 bool sizeCompare) ->
bool {
937 if (accountVec.empty() || txHistoryVec.empty())
939 if (sizeCompare && accountVec.size() != (txHistoryVec.size()))
943 for (
auto const& tx : txHistoryVec)
945 txHistoryMap.
emplace(std::get<1>(tx), std::get<0>(tx));
949 if (i >= accountVec.size())
951 auto it = txHistoryMap.
find(std::get<1>(accountVec[i]));
952 if (it == txHistoryMap.
end())
957 auto firstHistoryIndex = getHistoryIndex(0);
958 if (!firstHistoryIndex)
960 for (
std::size_t i = 1; i < accountVec.size(); ++i)
962 if (
auto idx = getHistoryIndex(i);
963 !idx || *idx != *firstHistoryIndex + i)
999 auto checkBoundary = [](IdxHashVec
const& vec,
bool ) {
1000 size_t num_tx = vec.size();
1001 for (
size_t i = 0; i < num_tx; ++i)
1003 auto [idx, hash, boundary, ledger] = vec[i];
1004 if ((i + 1 == num_tx || ledger != std::get<3>(vec[i + 1])) !=
1024 request[jss::account_history_tx_stream][jss::account] =
1026 auto jv = wscTxHistory->invoke(
"subscribe", request);
1027 if (!BEAST_EXPECT(goodSubRPC(jv)))
1030 jv = wscTxHistory->invoke(
"subscribe", request);
1031 BEAST_EXPECT(!goodSubRPC(jv));
1036 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
1038 jv = wscTxHistory->invoke(
"unsubscribe", request);
1039 if (!BEAST_EXPECT(goodSubRPC(jv)))
1042 sendPayments(env, env.
master, alice, 1, 1, 123456);
1045 auto r = getTxHash(*wscTxHistory, vec, 1);
1046 if (!BEAST_EXPECT(r.first && r.second))
1052 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
1054 jv = wscTxHistory->invoke(
"unsubscribe", request);
1055 BEAST_EXPECT(goodSubRPC(jv));
1057 sendPayments(env, env.
master, alice, 1, 1);
1058 r = getTxHash(*wscTxHistory, vec, 1);
1059 BEAST_EXPECT(!r.first);
1070 request[jss::account_history_tx_stream][jss::account] =
1071 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1072 auto jv = wscTxHistory->invoke(
"subscribe", request);
1073 if (!BEAST_EXPECT(goodSubRPC(jv)))
1075 IdxHashVec genesisFullHistoryVec;
1077 !getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first))
1084 sendPayments(env, env.
master, bob, 1, 1, 654321);
1086 auto r = getTxHash(*wscTxHistory, genesisFullHistoryVec, 1);
1087 if (!BEAST_EXPECT(r.first && r.second))
1090 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1091 jv = wscTxHistory->invoke(
"subscribe", request);
1092 if (!BEAST_EXPECT(goodSubRPC(jv)))
1094 IdxHashVec bobFullHistoryVec;
1095 r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
1096 if (!BEAST_EXPECT(r.first && r.second))
1099 std::get<1>(bobFullHistoryVec.back()) ==
1100 std::get<1>(genesisFullHistoryVec.back()));
1105 jv = wscTxHistory->invoke(
"unsubscribe", request);
1106 if (!BEAST_EXPECT(goodSubRPC(jv)))
1108 request[jss::account_history_tx_stream][jss::account] =
1109 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1110 jv = wscTxHistory->invoke(
"unsubscribe", request);
1111 BEAST_EXPECT(goodSubRPC(jv));
1117 sendPayments(env, env.
master, bob, 30, 300);
1119 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1120 jv = wscTxHistory->invoke(
"subscribe", request);
1122 bobFullHistoryVec.
clear();
1124 getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second);
1125 jv = wscTxHistory->invoke(
"unsubscribe", request);
1127 request[jss::account_history_tx_stream][jss::account] =
1128 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1129 jv = wscTxHistory->invoke(
"subscribe", request);
1130 genesisFullHistoryVec.
clear();
1132 getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
1133 jv = wscTxHistory->invoke(
"unsubscribe", request);
1136 std::get<1>(bobFullHistoryVec.back()) ==
1137 std::get<1>(genesisFullHistoryVec.back()));
1150 env.
fund(
XRP(222222), accounts);
1156 stream[jss::accounts].append(alice.
human());
1157 auto jv = wscAccount->invoke(
"subscribe", stream);
1159 sendPayments(env, alice, bob, 5, 1);
1160 sendPayments(env, alice, bob, 5, 1);
1161 IdxHashVec accountVec;
1162 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1168 request[jss::account_history_tx_stream][jss::account] =
1170 jv = wscTxHistory->invoke(
"subscribe", request);
1173 IdxHashVec txHistoryVec;
1174 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1176 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1181 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1186 IdxHashVec initFundTxns;
1188 getTxHash(*wscTxHistory, initFundTxns, 10).second) ||
1189 !BEAST_EXPECT(checkBoundary(initFundTxns,
false)))
1194 sendPayments(env, alice, bob, 10, 1);
1195 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1197 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1199 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1204 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1207 wscTxHistory->invoke(
"unsubscribe", request);
1208 wscAccount->invoke(
"unsubscribe", stream);
1217 auto const USD_a = alice[
"USD"];
1220 env.
fund(
XRP(333333), accounts);
1221 env.
trust(USD_a(20000), carol);
1224 auto mixedPayments = [&]() ->
int {
1225 sendPayments(env, alice, carol, 1, 0);
1226 env(
pay(alice, carol, USD_a(100)));
1234 request[jss::account_history_tx_stream][jss::account] =
1237 auto jv = ws->invoke(
"subscribe", request);
1241 getTxHash(*ws, tempVec, 100);
1244 auto count = mixedPayments();
1246 if (!BEAST_EXPECT(getTxHash(*ws, vec1, count).first))
1248 ws->invoke(
"unsubscribe", request);
1257 env.
fund(
XRP(444444), accounts);
1261 auto oneRound = [&](
int numPayments) {
1262 return sendPayments(env, alice, carol, numPayments, 300);
1268 request[jss::account_history_tx_stream][jss::account] =
1271 auto jv = wscLong->invoke(
"subscribe", request);
1275 getTxHash(*wscLong, tempVec, 100);
1279 for (
int kk = 2; kk < 10; ++kk)
1281 auto count = oneRound(kk);
1283 if (!BEAST_EXPECT(getTxHash(*wscLong, vec1, count).first))
1288 auto jv = wscShort->invoke(
"subscribe", request);
1290 if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first))
1292 if (!BEAST_EXPECT(hashCompare(vec1, vec2,
true)))
1294 wscShort->invoke(
"unsubscribe", request);
1302 using namespace test::jtx;
1320BEAST_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.