189 using namespace std::chrono_literals;
192 auto baseFee = env.
current()->fees().base.drops();
199 stream[jss::streams].append(
"transactions");
200 auto jv = wsc->invoke(
"subscribe", stream);
201 if (wsc->version() == 2)
203 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
204 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
205 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
207 BEAST_EXPECT(jv[jss::status] ==
"success");
215 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
216 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"]
217 [jss::Account] == Account(
"alice").human() &&
218 jv[jss::transaction][jss::TransactionType] == jss::Payment &&
219 jv[jss::transaction][jss::DeliverMax] ==
220 std::to_string(10000000000 + baseFee) &&
221 jv[jss::transaction][jss::Fee] == std::to_string(baseFee) &&
222 jv[jss::transaction][jss::Sequence] == 1;
226 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
227 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"][
"FinalFields"]
228 [jss::Account] == Account(
"alice").human();
235 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
236 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"]
238 == Account(
"bob").human() &&
239 jv[jss::transaction][jss::TransactionType]
241 jv[jss::transaction][jss::DeliverMax]
242 == std::to_string(10000000000 + baseFee) &&
243 jv[jss::transaction][jss::Fee]
244 == std::to_string(baseFee) &&
245 jv[jss::transaction][jss::Sequence]
250 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
251 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"][
"FinalFields"]
252 [jss::Account] == Account(
"bob").human();
258 auto jv = wsc->invoke(
"unsubscribe", stream);
259 if (wsc->version() == 2)
261 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
262 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
263 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
265 BEAST_EXPECT(jv[jss::status] ==
"success");
272 stream[jss::accounts].append(
Account(
"alice").human());
273 auto jv = wsc->invoke(
"subscribe", stream);
274 if (wsc->version() == 2)
276 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
277 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
278 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
280 BEAST_EXPECT(jv[jss::status] ==
"success");
287 BEAST_EXPECT(!wsc->getMsg(10ms));
294 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
295 return jv[jss::meta][
"AffectedNodes"][1u][
"ModifiedNode"][
"FinalFields"]
296 [jss::Account] == Account(
"alice").human();
299 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
300 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"][
"LowLimit"]
301 [jss::issuer] == Account(
"alice").human();
306 auto jv = wsc->invoke(
"unsubscribe", stream);
307 if (wsc->version() == 2)
309 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
310 BEAST_EXPECT(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");
319 testcase(
"transactions API version 2");
321 using namespace std::chrono_literals;
324 cfg->fees.referenceFee = 10;
333 stream[jss::api_version] = 2;
335 stream[jss::streams].append(
"transactions");
336 auto jv = wsc->invoke(
"subscribe", stream);
337 if (wsc->version() == 2)
339 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
340 BEAST_EXPECT(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");
347 env.fund(
XRP(10000),
"alice");
348 BEAST_EXPECT(env.syncClose());
351 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
352 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"]
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"][
"FinalFields"]
377 [jss::Account] == Account(
"alice").human();
383 auto jv = wsc->invoke(
"unsubscribe", stream);
384 if (wsc->version() == 2)
386 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
387 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
388 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
390 BEAST_EXPECT(jv[jss::status] ==
"success");
436 auto const parsedseed =
438 if (BEAST_EXPECT(parsedseed); not parsedseed.has_value())
452 stream[jss::streams].append(
"validations");
453 auto jv = wsc->invoke(
"subscribe", stream);
454 if (wsc->version() == 2)
456 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
457 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
458 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
460 BEAST_EXPECT(jv[jss::status] ==
"success");
465 auto validValidationFields = [&env, &valPublicKey](
json::Value const& jv) {
466 if (jv[jss::type] !=
"validationReceived")
469 if (jv[jss::validation_public_key].asString() != valPublicKey)
481 if (jv[jss::full] !=
true)
484 if (jv.isMember(jss::load_fee))
487 if (!jv.isMember(jss::signature))
490 if (!jv.isMember(jss::signing_time))
493 if (!jv.isMember(jss::cookie))
496 if (!jv.isMember(jss::validated_hash))
500 if (!jv.isMember(jss::network_id) || jv[jss::network_id] != netID)
520 while (env.
closed()->header().seq < 300)
523 using namespace std::chrono_literals;
524 BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
529 auto jv = wsc->invoke(
"unsubscribe", stream);
530 if (wsc->version() == 2)
532 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
533 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
534 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
536 BEAST_EXPECT(jv[jss::status] ==
"success");
572 auto const method = subscribe ?
"subscribe" :
"unsubscribe";
573 testcase <<
"Error cases for " << method;
579 auto const jr = env.
rpc(
"json", method,
"{}")[jss::result];
580 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
581 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
586 jv[jss::url] =
"not-a-url";
587 jv[jss::username] =
"admin";
588 jv[jss::password] =
"password";
589 auto const jr = env.
rpc(
"json", method,
to_string(jv))[jss::result];
592 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
593 BEAST_EXPECT(jr[jss::error_message] ==
"Failed to parse url.");
601 jv[jss::url] =
"ftp://scheme.not.supported.tld";
602 auto const jr = env.
rpc(
"json", method,
to_string(jv))[jss::result];
605 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
606 BEAST_EXPECT(jr[jss::error_message] ==
"Only http and https is supported.");
613 jv[jss::url] =
"no-url";
614 auto const jr = envNonadmin.
rpc(
"json", method,
to_string(jv))[jss::result];
615 BEAST_EXPECT(jr[jss::error] ==
"noPermission");
616 BEAST_EXPECT(jr[jss::error_message] ==
"You don't have permission for this command.");
628 for (
auto const& f : {jss::accounts_proposed, jss::accounts})
630 for (
auto const& nonArray : nonArrays)
634 auto const jr = wsc->invoke(method, jv)[jss::result];
635 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
636 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
642 auto const jr = wsc->invoke(method, jv)[jss::result];
643 BEAST_EXPECT(jr[jss::error] ==
"actMalformed");
644 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
648 for (
auto const& nonArray : nonArrays)
651 jv[jss::books] = nonArray;
652 auto const jr = wsc->invoke(method, jv)[jss::result];
653 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
654 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
660 jv[jss::books][0u] = 1;
661 auto const jr = wsc->invoke(method, jv)[jss::result];
662 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
663 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
672 auto const jr = wsc->invoke(method, jv)[jss::result];
674 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
675 BEAST_EXPECT(jr[jss::error_message] ==
"Source currency is malformed.");
684 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"ZZZZ";
685 auto const jr = wsc->invoke(method, jv)[jss::result];
686 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
687 BEAST_EXPECT(jr[jss::error_message] ==
"Source currency is malformed.");
696 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
697 jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
698 auto const jr = wsc->invoke(method, jv)[jss::result];
699 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
700 BEAST_EXPECT(jr[jss::error_message] ==
"Source issuer is malformed.");
709 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
710 jv[jss::books][0u][jss::taker_pays][jss::issuer] =
Account{
"gateway"}.
human() +
"DEAD";
711 auto const jr = wsc->invoke(method, jv)[jss::result];
712 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
713 BEAST_EXPECT(jr[jss::error_message] ==
"Source issuer is malformed.");
720 jv[jss::books][0u][jss::taker_pays] =
723 auto const jr = wsc->invoke(method, jv)[jss::result];
725 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
727 jr[jss::error_message] ==
"Destination amount/currency/issuer is malformed.");
734 jv[jss::books][0u][jss::taker_pays] =
736 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"ZZZZ";
737 auto const jr = wsc->invoke(method, jv)[jss::result];
740 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
742 jr[jss::error_message] ==
"Destination amount/currency/issuer is malformed.");
749 jv[jss::books][0u][jss::taker_pays] =
751 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
752 jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
753 auto const jr = wsc->invoke(method, jv)[jss::result];
754 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
755 BEAST_EXPECT(jr[jss::error_message] ==
"Destination issuer is malformed.");
762 jv[jss::books][0u][jss::taker_pays] =
764 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
765 jv[jss::books][0u][jss::taker_gets][jss::issuer] =
Account{
"gateway"}.
human() +
"DEAD";
766 auto const jr = wsc->invoke(method, jv)[jss::result];
767 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
768 BEAST_EXPECT(jr[jss::error_message] ==
"Destination issuer is malformed.");
775 jv[jss::books][0u][jss::taker_pays] =
777 jv[jss::books][0u][jss::taker_gets] =
779 auto const jr = wsc->invoke(method, jv)[jss::result];
780 BEAST_EXPECT(jr[jss::error] ==
"badMarket");
781 BEAST_EXPECT(jr[jss::error_message] ==
"No such market.");
784 for (
auto const& nonArray : nonArrays)
787 jv[jss::streams] = nonArray;
788 auto const jr = wsc->invoke(method, jv)[jss::result];
789 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
790 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
796 jv[jss::streams][0u] = 1;
797 auto const jr = wsc->invoke(method, jv)[jss::result];
798 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
799 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
805 jv[jss::streams][0u] =
"not_a_stream";
806 auto const jr = wsc->invoke(method, jv)[jss::result];
807 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
808 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
818 jv[jss::books][0u][jss::taker_pays] =
820 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"XRP";
821 jv[jss::books][0u][jss::taker] = 1;
822 auto const jr = wsc->invoke(method, jv)[jss::result];
823 BEAST_EXPECTS(jr[jss::error] ==
"actMalformed", jr.toStyledString());
824 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
832 jv[jss::books][0u][jss::taker_pays] =
834 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"XRP";
835 jv[jss::books][0u][jss::taker] =
"not_an_account";
836 auto const jr = wsc->invoke(method, jv)[jss::result];
837 BEAST_EXPECTS(jr[jss::error] ==
"actMalformed", jr.toStyledString());
838 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
846 jv[jss::books][0u][jss::taker_pays] =
848 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"XRP";
849 jv[jss::books][0u][jss::taker] =
Account{
"alice"}.
human() +
"DEAD";
850 auto const jr = wsc->invoke(method, jv)[jss::result];
851 BEAST_EXPECTS(jr[jss::error] ==
"actMalformed", jr.toStyledString());
852 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
862 using namespace std::chrono_literals;
875 auto goodSubRPC = [](
json::Value const& subReply) ->
bool {
876 return subReply.isMember(jss::result) && subReply[jss::result].isMember(jss::status) &&
877 subReply[jss::result][jss::status] == jss::success;
890 bool firstFlag =
false;
892 for (
int i = 0; i < numReplies; ++i)
895 auto reply = wsc.
getMsg(timeout);
899 if (r.isMember(jss::account_history_tx_index))
900 idx = r[jss::account_history_tx_index].asInt();
901 if (r.isMember(jss::account_history_tx_first))
903 bool const boundary = r.isMember(jss::account_history_boundary);
904 int const ledgerIdx = r[jss::ledger_index].asInt();
905 if (r.isMember(jss::transaction) && r[jss::transaction].isMember(jss::hash))
907 auto t{r[jss::transaction]};
908 v.emplace_back(idx, t[jss::hash].asString(), boundary, ledgerIdx);
912 return {
false, firstFlag};
915 return {
true, firstFlag};
922 auto sendPayments = [
this](
931 for (
int i = 0; i < newTxns; ++i)
933 auto& from = (i % 2 == 0) ? a : b;
934 auto& to = (i % 2 == 0) ? b : a;
940 for (
int i = 0; i < ledgersToClose; ++i)
950 auto hashCompare = [](IdxHashVec
const& accountVec,
951 IdxHashVec
const& txHistoryVec,
952 bool sizeCompare) ->
bool {
953 if (accountVec.empty() || txHistoryVec.empty())
955 if (sizeCompare && accountVec.size() != (txHistoryVec.size()))
959 for (
auto const& tx : txHistoryVec)
961 txHistoryMap.
emplace(std::get<1>(tx), std::get<0>(tx));
965 if (i >= accountVec.size())
967 auto it = txHistoryMap.
find(std::get<1>(accountVec[i]));
968 if (it == txHistoryMap.
end())
973 auto firstHistoryIndex = getHistoryIndex(0);
974 if (!firstHistoryIndex)
976 for (
std::size_t i = 1; i < accountVec.size(); ++i)
978 if (
auto idx = getHistoryIndex(i); !idx || *idx != *firstHistoryIndex + i)
1014 auto checkBoundary = [](IdxHashVec
const& vec,
bool ) {
1015 size_t const numTx = vec.size();
1016 for (
size_t i = 0; i < numTx; ++i)
1018 auto [idx, hash, boundary, ledger] = vec[i];
1019 if ((i + 1 == numTx || ledger != std::get<3>(vec[i + 1])) != boundary)
1038 request[jss::account_history_tx_stream][jss::account] = alice.
human();
1039 auto jv = wscTxHistory->invoke(
"subscribe", request);
1040 if (!BEAST_EXPECT(goodSubRPC(jv)))
1043 jv = wscTxHistory->invoke(
"subscribe", request);
1044 BEAST_EXPECT(!goodSubRPC(jv));
1049 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
true;
1050 jv = wscTxHistory->invoke(
"unsubscribe", request);
1051 if (!BEAST_EXPECT(goodSubRPC(jv)))
1054 sendPayments(env, env.
master, alice, 1, 1, 123456);
1057 auto r = getTxHash(*wscTxHistory, vec, 1);
1058 if (!BEAST_EXPECT(r.first && r.second))
1064 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
false;
1065 jv = wscTxHistory->invoke(
"unsubscribe", request);
1066 BEAST_EXPECT(goodSubRPC(jv));
1068 sendPayments(env, env.
master, alice, 1, 1);
1069 r = getTxHash(*wscTxHistory, vec, 1, 10ms);
1070 BEAST_EXPECT(!r.first);
1081 request[jss::account_history_tx_stream][jss::account] =
1082 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1083 auto jv = wscTxHistory->invoke(
"subscribe", request);
1084 if (!BEAST_EXPECT(goodSubRPC(jv)))
1086 IdxHashVec genesisFullHistoryVec;
1088 if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1, 10ms).first))
1095 sendPayments(env, env.
master, bob, 1, 1, 654321);
1097 auto r = getTxHash(*wscTxHistory, genesisFullHistoryVec, 1);
1098 if (!BEAST_EXPECT(r.first && r.second))
1101 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1102 jv = wscTxHistory->invoke(
"subscribe", request);
1103 if (!BEAST_EXPECT(goodSubRPC(jv)))
1105 IdxHashVec bobFullHistoryVec;
1107 r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
1108 if (!BEAST_EXPECT(r.first && r.second))
1111 std::get<1>(bobFullHistoryVec.back()) == std::get<1>(genesisFullHistoryVec.back()));
1116 jv = wscTxHistory->invoke(
"unsubscribe", request);
1117 if (!BEAST_EXPECT(goodSubRPC(jv)))
1119 request[jss::account_history_tx_stream][jss::account] =
1120 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1121 jv = wscTxHistory->invoke(
"unsubscribe", request);
1122 BEAST_EXPECT(goodSubRPC(jv));
1128 sendPayments(env, env.
master, bob, 30, 300);
1130 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1131 jv = wscTxHistory->invoke(
"subscribe", request);
1133 bobFullHistoryVec.
clear();
1134 BEAST_EXPECT(getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second);
1135 jv = wscTxHistory->invoke(
"unsubscribe", request);
1137 request[jss::account_history_tx_stream][jss::account] =
1138 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1139 jv = wscTxHistory->invoke(
"subscribe", request);
1140 genesisFullHistoryVec.
clear();
1142 BEAST_EXPECT(getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
1143 jv = wscTxHistory->invoke(
"unsubscribe", request);
1146 std::get<1>(bobFullHistoryVec.back()) == std::get<1>(genesisFullHistoryVec.back()));
1159 env.
fund(
XRP(222222), accounts);
1165 stream[jss::accounts].append(alice.
human());
1166 auto jv = wscAccount->invoke(
"subscribe", stream);
1168 sendPayments(env, alice, bob, 5, 1);
1169 sendPayments(env, alice, bob, 5, 1);
1170 IdxHashVec accountVec;
1171 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1177 request[jss::account_history_tx_stream][jss::account] = alice.
human();
1178 jv = wscTxHistory->invoke(
"subscribe", request);
1181 IdxHashVec txHistoryVec;
1182 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1184 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1189 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1194 IdxHashVec initFundTxns;
1195 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, initFundTxns, 10).second) ||
1196 !BEAST_EXPECT(checkBoundary(initFundTxns,
false)))
1201 sendPayments(env, alice, bob, 10, 1);
1202 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1204 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1206 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1211 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1214 wscTxHistory->invoke(
"unsubscribe", request);
1215 wscAccount->invoke(
"unsubscribe", stream);
1224 auto const usdA = alice[
"USD"];
1227 env.
fund(
XRP(333333), accounts);
1228 env.
trust(usdA(20000), carol);
1231 auto mixedPayments = [&]() ->
int {
1232 sendPayments(env, alice, carol, 1, 0);
1233 env(
pay(alice, carol, usdA(100)));
1241 request[jss::account_history_tx_stream][jss::account] = carol.
human();
1243 auto jv = ws->invoke(
"subscribe", request);
1248 getTxHash(*ws, tempVec, 100, 1000ms);
1251 auto count = mixedPayments();
1253 if (!BEAST_EXPECT(getTxHash(*ws, vec1, count).first))
1255 ws->invoke(
"unsubscribe", request);
1264 env.
fund(
XRP(444444), accounts);
1268 auto oneRound = [&](
int numPayments) {
1269 return sendPayments(env, alice, carol, numPayments, 300);
1275 request[jss::account_history_tx_stream][jss::account] = carol.
human();
1277 auto jv = wscLong->invoke(
"subscribe", request);
1282 getTxHash(*wscLong, tempVec, 100, 1000ms);
1286 for (
int kk = 2; kk < 10; ++kk)
1288 auto count = oneRound(kk);
1290 if (!BEAST_EXPECT(getTxHash(*wscLong, vec1, count).first))
1295 auto jv = wscShort->invoke(
"subscribe", request);
1297 if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first))
1299 if (!BEAST_EXPECT(hashCompare(vec1, vec2,
true)))
1301 wscShort->invoke(
"unsubscribe", request);
1369 testcase(
"Test synthetic fields from Subscribe response");
1372 using namespace std::chrono_literals;
1376 Account const broker{
"broker"};
1379 env.
fund(
XRP(10000), alice, bob, broker);
1385 stream[jss::streams].append(
"transactions");
1386 auto jv = wsc->invoke(
"subscribe", stream);
1391 auto verifyNFTokenID = [&](
uint256 const& actualNftID) {
1392 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1394 BEAST_EXPECT(nftID.parseHex(jv[jss::meta][jss::nftoken_id].asString()));
1395 return nftID == actualNftID;
1402 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1403 std::vector<uint256> metaIDs;
1405 jv[jss::meta][jss::nftoken_ids].begin(),
1406 jv[jss::meta][jss::nftoken_ids].end(),
1407 std::back_inserter(metaIDs),
1408 [this](json::Value id) {
1410 BEAST_EXPECT(nftID.parseHex(id.asString()));
1418 BEAST_EXPECT(metaIDs.size() == actualNftIDs.size());
1422 for (
size_t i = 0; i < metaIDs.size(); ++i)
1423 BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
1430 auto verifyNFTokenOfferID = [&](
uint256 const& offerID) {
1431 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1432 uint256 metaOfferID;
1433 BEAST_EXPECT(metaOfferID.parseHex(jv[jss::meta][jss::offer_id].asString()));
1434 return metaOfferID == offerID;
1443 env(
token::mint(alice, 0u), Txflags(tfTransferable));
1444 BEAST_EXPECT(env.syncClose());
1445 verifyNFTokenID(nftId1);
1448 env(
token::mint(alice, 0u), Txflags(tfTransferable));
1449 BEAST_EXPECT(env.syncClose());
1450 verifyNFTokenID(nftId2);
1457 BEAST_EXPECT(env.syncClose());
1458 verifyNFTokenOfferID(aliceOfferIndex1);
1462 BEAST_EXPECT(env.syncClose());
1463 verifyNFTokenOfferID(aliceOfferIndex2);
1469 BEAST_EXPECT(env.syncClose());
1470 verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
1476 BEAST_EXPECT(env.syncClose());
1477 verifyNFTokenOfferID(bobBuyOfferIndex);
1482 BEAST_EXPECT(env.syncClose());
1483 verifyNFTokenID(nftId1);
1490 env(
token::mint(alice, 0u), Txflags(tfTransferable));
1491 BEAST_EXPECT(env.syncClose());
1492 verifyNFTokenID(nftId);
1497 token::Destination(broker),
1498 Txflags(tfSellNFToken));
1499 BEAST_EXPECT(env.syncClose());
1500 verifyNFTokenOfferID(offerAliceToBroker);
1505 BEAST_EXPECT(env.syncClose());
1506 verifyNFTokenOfferID(offerBobToBroker);
1510 BEAST_EXPECT(env.syncClose());
1511 verifyNFTokenID(nftId);
1519 env(
token::mint(alice, 0u), Txflags(tfTransferable));
1520 BEAST_EXPECT(env.syncClose());
1521 verifyNFTokenID(nftId);
1526 BEAST_EXPECT(env.syncClose());
1527 verifyNFTokenOfferID(aliceOfferIndex1);
1531 BEAST_EXPECT(env.syncClose());
1532 verifyNFTokenOfferID(aliceOfferIndex2);
1537 BEAST_EXPECT(env.syncClose());
1538 verifyNFTokenIDsInCancelOffer({nftId});
1541 if (features[featureNFTokenMintOffer])
1543 uint256 const aliceMintWithOfferIndex1 =
1546 BEAST_EXPECT(env.syncClose());
1547 verifyNFTokenOfferID(aliceMintWithOfferIndex1);