191 using namespace std::chrono_literals;
194 auto baseFee = env.
current()->fees().base.drops();
201 stream[jss::streams].append(
"transactions");
202 auto jv = wsc->invoke(
"subscribe", stream);
203 if (wsc->version() == 2)
205 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
206 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
207 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
209 BEAST_EXPECT(jv[jss::status] ==
"success");
217 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
218 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"]
219 [jss::Account] == Account(
"alice").human() &&
220 jv[jss::transaction][jss::TransactionType] == jss::Payment &&
221 jv[jss::transaction][jss::DeliverMax] ==
222 std::to_string(10000000000 + baseFee) &&
223 jv[jss::transaction][jss::Fee] == std::to_string(baseFee) &&
224 jv[jss::transaction][jss::Sequence] == 1;
228 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
229 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"][
"FinalFields"]
230 [jss::Account] == Account(
"alice").human();
237 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
238 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"]
240 == Account(
"bob").human() &&
241 jv[jss::transaction][jss::TransactionType]
243 jv[jss::transaction][jss::DeliverMax]
244 == std::to_string(10000000000 + baseFee) &&
245 jv[jss::transaction][jss::Fee]
246 == std::to_string(baseFee) &&
247 jv[jss::transaction][jss::Sequence]
252 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
253 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"][
"FinalFields"]
254 [jss::Account] == Account(
"bob").human();
260 auto jv = wsc->invoke(
"unsubscribe", stream);
261 if (wsc->version() == 2)
263 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
264 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
265 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
267 BEAST_EXPECT(jv[jss::status] ==
"success");
274 stream[jss::accounts].append(
Account(
"alice").human());
275 auto jv = wsc->invoke(
"subscribe", stream);
276 if (wsc->version() == 2)
278 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
279 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
280 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
282 BEAST_EXPECT(jv[jss::status] ==
"success");
289 BEAST_EXPECT(!wsc->getMsg(10ms));
296 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
297 return jv[jss::meta][
"AffectedNodes"][1u][
"ModifiedNode"][
"FinalFields"]
298 [jss::Account] == Account(
"alice").human();
301 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
302 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"][
"LowLimit"]
303 [jss::issuer] == Account(
"alice").human();
308 auto jv = wsc->invoke(
"unsubscribe", stream);
309 if (wsc->version() == 2)
311 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
312 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
313 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
315 BEAST_EXPECT(jv[jss::status] ==
"success");
321 testcase(
"transactions API version 2");
323 using namespace std::chrono_literals;
326 cfg->fees.referenceFee = 10;
335 stream[jss::api_version] = 2;
337 stream[jss::streams].append(
"transactions");
338 auto jv = wsc->invoke(
"subscribe", stream);
339 if (wsc->version() == 2)
341 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
342 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
343 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
345 BEAST_EXPECT(jv[jss::status] ==
"success");
349 env.fund(
XRP(10000),
"alice");
350 BEAST_EXPECT(env.syncClose());
353 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
354 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"]
356 == Account(
"alice").human() &&
357 jv[jss::close_time_iso]
358 ==
"2000-01-01T00:00:10Z" &&
359 jv[jss::validated] == true &&
360 jv[jss::ledger_hash] ==
361 "0F1A9E0C109ADEF6DA2BDE19217C12BBEC57174CBDBD212B0EBDC1CEDB"
363 !jv[jss::inLedger] &&
364 jv[jss::ledger_index] == 3 &&
365 jv[jss::tx_json][jss::TransactionType]
367 jv[jss::tx_json][jss::DeliverMax]
369 !jv[jss::tx_json].isMember(jss::Amount) &&
370 jv[jss::tx_json][jss::Fee]
372 jv[jss::tx_json][jss::Sequence]
377 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
378 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"][
"FinalFields"]
379 [jss::Account] == Account(
"alice").human();
385 auto jv = wsc->invoke(
"unsubscribe", stream);
386 if (wsc->version() == 2)
388 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
389 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
390 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
392 BEAST_EXPECT(jv[jss::status] ==
"success");
438 auto const parsedseed =
440 if (BEAST_EXPECT(parsedseed); not parsedseed.has_value())
454 stream[jss::streams].append(
"validations");
455 auto jv = wsc->invoke(
"subscribe", stream);
456 if (wsc->version() == 2)
458 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
459 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
460 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
462 BEAST_EXPECT(jv[jss::status] ==
"success");
467 auto validValidationFields = [&env, &valPublicKey](
json::Value const& jv) {
468 if (jv[jss::type] !=
"validationReceived")
471 if (jv[jss::validation_public_key].asString() != valPublicKey)
483 if (jv[jss::full] !=
true)
486 if (jv.isMember(jss::load_fee))
489 if (!jv.isMember(jss::signature))
492 if (!jv.isMember(jss::signing_time))
495 if (!jv.isMember(jss::cookie))
498 if (!jv.isMember(jss::validated_hash))
502 if (!jv.isMember(jss::network_id) || jv[jss::network_id] != netID)
522 while (env.
closed()->header().seq < 300)
525 using namespace std::chrono_literals;
526 BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
531 auto jv = wsc->invoke(
"unsubscribe", stream);
532 if (wsc->version() == 2)
534 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
535 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
536 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
538 BEAST_EXPECT(jv[jss::status] ==
"success");
574 auto const method = subscribe ?
"subscribe" :
"unsubscribe";
575 testcase <<
"Error cases for " << method;
581 auto const jr = env.
rpc(
"json", method,
"{}")[jss::result];
582 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
583 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
588 jv[jss::url] =
"not-a-url";
589 jv[jss::username] =
"admin";
590 jv[jss::password] =
"password";
591 auto const jr = env.
rpc(
"json", method,
to_string(jv))[jss::result];
594 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
595 BEAST_EXPECT(jr[jss::error_message] ==
"Failed to parse url.");
603 jv[jss::url] =
"ftp://scheme.not.supported.tld";
604 auto const jr = env.
rpc(
"json", method,
to_string(jv))[jss::result];
607 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
608 BEAST_EXPECT(jr[jss::error_message] ==
"Only http and https is supported.");
615 jv[jss::url] =
"no-url";
616 auto const jr = envNonadmin.
rpc(
"json", method,
to_string(jv))[jss::result];
617 BEAST_EXPECT(jr[jss::error] ==
"noPermission");
618 BEAST_EXPECT(jr[jss::error_message] ==
"You don't have permission for this command.");
630 for (
auto const& f : {jss::accounts_proposed, jss::accounts})
632 for (
auto const& nonArray : nonArrays)
636 auto const jr = wsc->invoke(method, jv)[jss::result];
637 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
638 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
644 auto const jr = wsc->invoke(method, jv)[jss::result];
645 BEAST_EXPECT(jr[jss::error] ==
"actMalformed");
646 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
650 for (
auto const& nonArray : nonArrays)
653 jv[jss::books] = nonArray;
654 auto const jr = wsc->invoke(method, jv)[jss::result];
655 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
656 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
662 jv[jss::books][0u] = 1;
663 auto const jr = wsc->invoke(method, jv)[jss::result];
664 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
665 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
674 auto const jr = wsc->invoke(method, jv)[jss::result];
676 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
677 BEAST_EXPECT(jr[jss::error_message] ==
"Source currency is malformed.");
686 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"ZZZZ";
687 auto const jr = wsc->invoke(method, jv)[jss::result];
688 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
689 BEAST_EXPECT(jr[jss::error_message] ==
"Source currency is malformed.");
698 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
699 jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
700 auto const jr = wsc->invoke(method, jv)[jss::result];
701 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
702 BEAST_EXPECT(jr[jss::error_message] ==
"Source issuer is malformed.");
711 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
712 jv[jss::books][0u][jss::taker_pays][jss::issuer] =
Account{
"gateway"}.
human() +
"DEAD";
713 auto const jr = wsc->invoke(method, jv)[jss::result];
714 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
715 BEAST_EXPECT(jr[jss::error_message] ==
"Source issuer is malformed.");
722 jv[jss::books][0u][jss::taker_pays] =
725 auto const jr = wsc->invoke(method, jv)[jss::result];
727 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
729 jr[jss::error_message] ==
"Destination amount/currency/issuer is malformed.");
736 jv[jss::books][0u][jss::taker_pays] =
738 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"ZZZZ";
739 auto const jr = wsc->invoke(method, jv)[jss::result];
742 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
744 jr[jss::error_message] ==
"Destination amount/currency/issuer is malformed.");
751 jv[jss::books][0u][jss::taker_pays] =
753 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
754 jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
755 auto const jr = wsc->invoke(method, jv)[jss::result];
756 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
757 BEAST_EXPECT(jr[jss::error_message] ==
"Destination issuer is malformed.");
764 jv[jss::books][0u][jss::taker_pays] =
766 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
767 jv[jss::books][0u][jss::taker_gets][jss::issuer] =
Account{
"gateway"}.
human() +
"DEAD";
768 auto const jr = wsc->invoke(method, jv)[jss::result];
769 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
770 BEAST_EXPECT(jr[jss::error_message] ==
"Destination issuer is malformed.");
777 jv[jss::books][0u][jss::taker_pays] =
779 jv[jss::books][0u][jss::taker_gets] =
781 auto const jr = wsc->invoke(method, jv)[jss::result];
782 BEAST_EXPECT(jr[jss::error] ==
"badMarket");
783 BEAST_EXPECT(jr[jss::error_message] ==
"No such market.");
786 for (
auto const& nonArray : nonArrays)
789 jv[jss::streams] = nonArray;
790 auto const jr = wsc->invoke(method, jv)[jss::result];
791 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
792 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
798 jv[jss::streams][0u] = 1;
799 auto const jr = wsc->invoke(method, jv)[jss::result];
800 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
801 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
807 jv[jss::streams][0u] =
"not_a_stream";
808 auto const jr = wsc->invoke(method, jv)[jss::result];
809 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
810 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
820 jv[jss::books][0u][jss::taker_pays] =
822 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"XRP";
823 jv[jss::books][0u][jss::taker] = 1;
824 auto const jr = wsc->invoke(method, jv)[jss::result];
825 BEAST_EXPECTS(jr[jss::error] ==
"actMalformed", jr.toStyledString());
826 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
834 jv[jss::books][0u][jss::taker_pays] =
836 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"XRP";
837 jv[jss::books][0u][jss::taker] =
"not_an_account";
838 auto const jr = wsc->invoke(method, jv)[jss::result];
839 BEAST_EXPECTS(jr[jss::error] ==
"actMalformed", jr.toStyledString());
840 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
848 jv[jss::books][0u][jss::taker_pays] =
850 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"XRP";
851 jv[jss::books][0u][jss::taker] =
Account{
"alice"}.
human() +
"DEAD";
852 auto const jr = wsc->invoke(method, jv)[jss::result];
853 BEAST_EXPECTS(jr[jss::error] ==
"actMalformed", jr.toStyledString());
854 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
864 using namespace std::chrono_literals;
877 auto goodSubRPC = [](
json::Value const& subReply) ->
bool {
878 return subReply.isMember(jss::result) && subReply[jss::result].isMember(jss::status) &&
879 subReply[jss::result][jss::status] == jss::success;
892 bool firstFlag =
false;
894 for (
int i = 0; i < numReplies; ++i)
897 auto reply = wsc.
getMsg(timeout);
901 if (r.isMember(jss::account_history_tx_index))
902 idx = r[jss::account_history_tx_index].asInt();
903 if (r.isMember(jss::account_history_tx_first))
905 bool const boundary = r.isMember(jss::account_history_boundary);
906 int const ledgerIdx = r[jss::ledger_index].asInt();
907 if (r.isMember(jss::transaction) && r[jss::transaction].isMember(jss::hash))
909 auto t{r[jss::transaction]};
910 v.emplace_back(idx, t[jss::hash].asString(), boundary, ledgerIdx);
914 return {
false, firstFlag};
917 return {
true, firstFlag};
924 auto sendPayments = [
this](
933 for (
int i = 0; i < newTxns; ++i)
935 auto& from = (i % 2 == 0) ? a : b;
936 auto& to = (i % 2 == 0) ? b : a;
942 for (
int i = 0; i < ledgersToClose; ++i)
952 auto hashCompare = [](IdxHashVec
const& accountVec,
953 IdxHashVec
const& txHistoryVec,
954 bool sizeCompare) ->
bool {
955 if (accountVec.empty() || txHistoryVec.empty())
957 if (sizeCompare && accountVec.size() != (txHistoryVec.size()))
961 for (
auto const& tx : txHistoryVec)
963 txHistoryMap.
emplace(std::get<1>(tx), std::get<0>(tx));
967 if (i >= accountVec.size())
969 auto it = txHistoryMap.
find(std::get<1>(accountVec[i]));
970 if (it == txHistoryMap.
end())
975 auto firstHistoryIndex = getHistoryIndex(0);
976 if (!firstHistoryIndex)
978 for (
std::size_t i = 1; i < accountVec.size(); ++i)
980 if (
auto idx = getHistoryIndex(i); !idx || *idx != *firstHistoryIndex + i)
1016 auto checkBoundary = [](IdxHashVec
const& vec,
bool ) {
1017 size_t const numTx = vec.size();
1018 for (
size_t i = 0; i < numTx; ++i)
1020 auto [idx, hash, boundary, ledger] = vec[i];
1021 if ((i + 1 == numTx || ledger != std::get<3>(vec[i + 1])) != boundary)
1040 request[jss::account_history_tx_stream][jss::account] = alice.
human();
1041 auto jv = wscTxHistory->invoke(
"subscribe", request);
1042 if (!BEAST_EXPECT(goodSubRPC(jv)))
1045 jv = wscTxHistory->invoke(
"subscribe", request);
1046 BEAST_EXPECT(!goodSubRPC(jv));
1051 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
true;
1052 jv = wscTxHistory->invoke(
"unsubscribe", request);
1053 if (!BEAST_EXPECT(goodSubRPC(jv)))
1056 sendPayments(env, env.
master, alice, 1, 1, 123456);
1059 auto r = getTxHash(*wscTxHistory, vec, 1);
1060 if (!BEAST_EXPECT(r.first && r.second))
1066 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
false;
1067 jv = wscTxHistory->invoke(
"unsubscribe", request);
1068 BEAST_EXPECT(goodSubRPC(jv));
1070 sendPayments(env, env.
master, alice, 1, 1);
1071 r = getTxHash(*wscTxHistory, vec, 1, 10ms);
1072 BEAST_EXPECT(!r.first);
1083 request[jss::account_history_tx_stream][jss::account] =
1084 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1085 auto jv = wscTxHistory->invoke(
"subscribe", request);
1086 if (!BEAST_EXPECT(goodSubRPC(jv)))
1088 IdxHashVec genesisFullHistoryVec;
1090 if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1, 10ms).first))
1097 sendPayments(env, env.
master, bob, 1, 1, 654321);
1099 auto r = getTxHash(*wscTxHistory, genesisFullHistoryVec, 1);
1100 if (!BEAST_EXPECT(r.first && r.second))
1103 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1104 jv = wscTxHistory->invoke(
"subscribe", request);
1105 if (!BEAST_EXPECT(goodSubRPC(jv)))
1107 IdxHashVec bobFullHistoryVec;
1109 r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
1110 if (!BEAST_EXPECT(r.first && r.second))
1113 std::get<1>(bobFullHistoryVec.back()) == std::get<1>(genesisFullHistoryVec.back()));
1118 jv = wscTxHistory->invoke(
"unsubscribe", request);
1119 if (!BEAST_EXPECT(goodSubRPC(jv)))
1121 request[jss::account_history_tx_stream][jss::account] =
1122 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1123 jv = wscTxHistory->invoke(
"unsubscribe", request);
1124 BEAST_EXPECT(goodSubRPC(jv));
1130 sendPayments(env, env.
master, bob, 30, 300);
1132 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1133 jv = wscTxHistory->invoke(
"subscribe", request);
1135 bobFullHistoryVec.
clear();
1136 BEAST_EXPECT(getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second);
1137 jv = wscTxHistory->invoke(
"unsubscribe", request);
1139 request[jss::account_history_tx_stream][jss::account] =
1140 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1141 jv = wscTxHistory->invoke(
"subscribe", request);
1142 genesisFullHistoryVec.
clear();
1144 BEAST_EXPECT(getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
1145 jv = wscTxHistory->invoke(
"unsubscribe", request);
1148 std::get<1>(bobFullHistoryVec.back()) == std::get<1>(genesisFullHistoryVec.back()));
1161 env.
fund(
XRP(222222), accounts);
1167 stream[jss::accounts].append(alice.
human());
1168 auto jv = wscAccount->invoke(
"subscribe", stream);
1170 sendPayments(env, alice, bob, 5, 1);
1171 sendPayments(env, alice, bob, 5, 1);
1172 IdxHashVec accountVec;
1173 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1179 request[jss::account_history_tx_stream][jss::account] = alice.
human();
1180 jv = wscTxHistory->invoke(
"subscribe", request);
1183 IdxHashVec txHistoryVec;
1184 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1186 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1191 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1196 IdxHashVec initFundTxns;
1197 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, initFundTxns, 10).second) ||
1198 !BEAST_EXPECT(checkBoundary(initFundTxns,
false)))
1203 sendPayments(env, alice, bob, 10, 1);
1204 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1206 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1208 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1213 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1216 wscTxHistory->invoke(
"unsubscribe", request);
1217 wscAccount->invoke(
"unsubscribe", stream);
1226 auto const usdA = alice[
"USD"];
1229 env.
fund(
XRP(333333), accounts);
1230 env.
trust(usdA(20000), carol);
1233 auto mixedPayments = [&]() ->
int {
1234 sendPayments(env, alice, carol, 1, 0);
1235 env(
pay(alice, carol, usdA(100)));
1243 request[jss::account_history_tx_stream][jss::account] = carol.
human();
1245 auto jv = ws->invoke(
"subscribe", request);
1250 getTxHash(*ws, tempVec, 100, 1000ms);
1253 auto count = mixedPayments();
1255 if (!BEAST_EXPECT(getTxHash(*ws, vec1, count).first))
1257 ws->invoke(
"unsubscribe", request);
1266 env.
fund(
XRP(444444), accounts);
1270 auto oneRound = [&](
int numPayments) {
1271 return sendPayments(env, alice, carol, numPayments, 300);
1277 request[jss::account_history_tx_stream][jss::account] = carol.
human();
1279 auto jv = wscLong->invoke(
"subscribe", request);
1284 getTxHash(*wscLong, tempVec, 100, 1000ms);
1288 for (
int kk = 2; kk < 10; ++kk)
1290 auto count = oneRound(kk);
1292 if (!BEAST_EXPECT(getTxHash(*wscLong, vec1, count).first))
1297 auto jv = wscShort->invoke(
"subscribe", request);
1299 if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first))
1301 if (!BEAST_EXPECT(hashCompare(vec1, vec2,
true)))
1303 wscShort->invoke(
"unsubscribe", request);
1371 testcase(
"Test synthetic fields from Subscribe response");
1374 using namespace std::chrono_literals;
1378 Account const broker{
"broker"};
1381 env.
fund(
XRP(10000), alice, bob, broker);
1387 stream[jss::streams].append(
"transactions");
1388 auto jv = wsc->invoke(
"subscribe", stream);
1393 auto verifyNFTokenID = [&](
uint256 const& actualNftID) {
1394 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1396 BEAST_EXPECT(nftID.parseHex(jv[jss::meta][jss::nftoken_id].asString()));
1397 return nftID == actualNftID;
1404 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1405 std::vector<uint256> metaIDs;
1407 jv[jss::meta][jss::nftoken_ids].begin(),
1408 jv[jss::meta][jss::nftoken_ids].end(),
1409 std::back_inserter(metaIDs),
1410 [this](json::Value id) {
1412 BEAST_EXPECT(nftID.parseHex(id.asString()));
1420 BEAST_EXPECT(metaIDs.size() == actualNftIDs.size());
1424 for (
size_t i = 0; i < metaIDs.size(); ++i)
1425 BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
1432 auto verifyNFTokenOfferID = [&](
uint256 const& offerID) {
1433 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1434 uint256 metaOfferID;
1435 BEAST_EXPECT(metaOfferID.parseHex(jv[jss::meta][jss::offer_id].asString()));
1436 return metaOfferID == offerID;
1445 env(
token::mint(alice, 0u), Txflags(tfTransferable));
1446 BEAST_EXPECT(env.syncClose());
1447 verifyNFTokenID(nftId1);
1450 env(
token::mint(alice, 0u), Txflags(tfTransferable));
1451 BEAST_EXPECT(env.syncClose());
1452 verifyNFTokenID(nftId2);
1457 uint256 const aliceOfferIndex1 =
1460 BEAST_EXPECT(env.syncClose());
1461 verifyNFTokenOfferID(aliceOfferIndex1);
1463 uint256 const aliceOfferIndex2 =
1466 BEAST_EXPECT(env.syncClose());
1467 verifyNFTokenOfferID(aliceOfferIndex2);
1473 BEAST_EXPECT(env.syncClose());
1474 verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
1478 auto const bobBuyOfferIndex =
1481 BEAST_EXPECT(env.syncClose());
1482 verifyNFTokenOfferID(bobBuyOfferIndex);
1487 BEAST_EXPECT(env.syncClose());
1488 verifyNFTokenID(nftId1);
1495 env(
token::mint(alice, 0u), Txflags(tfTransferable));
1496 BEAST_EXPECT(env.syncClose());
1497 verifyNFTokenID(nftId);
1500 uint256 const offerAliceToBroker =
1503 token::Destination(broker),
1504 Txflags(tfSellNFToken));
1505 BEAST_EXPECT(env.syncClose());
1506 verifyNFTokenOfferID(offerAliceToBroker);
1509 uint256 const offerBobToBroker =
1512 BEAST_EXPECT(env.syncClose());
1513 verifyNFTokenOfferID(offerBobToBroker);
1517 BEAST_EXPECT(env.syncClose());
1518 verifyNFTokenID(nftId);
1526 env(
token::mint(alice, 0u), Txflags(tfTransferable));
1527 BEAST_EXPECT(env.syncClose());
1528 verifyNFTokenID(nftId);
1531 uint256 const aliceOfferIndex1 =
1534 BEAST_EXPECT(env.syncClose());
1535 verifyNFTokenOfferID(aliceOfferIndex1);
1537 uint256 const aliceOfferIndex2 =
1540 BEAST_EXPECT(env.syncClose());
1541 verifyNFTokenOfferID(aliceOfferIndex2);
1546 BEAST_EXPECT(env.syncClose());
1547 verifyNFTokenIDsInCancelOffer({nftId});
1550 if (features[featureNFTokenMintOffer])
1552 uint256 const aliceMintWithOfferIndex1 =
1555 BEAST_EXPECT(env.syncClose());
1556 verifyNFTokenOfferID(aliceMintWithOfferIndex1);