18 #include <ripple/beast/unit_test.h>
19 #include <ripple/protocol/Indexes.h>
20 #include <ripple/protocol/jss.h>
21 #include <ripple/rpc/impl/Tuning.h>
23 #include <test/jtx/WSClient.h>
37 auto key = view->succ(uBookBase, uBookEnd);
40 auto sleOfferDir = view->read(
keylet::page(key.value()));
42 unsigned int bookEntry;
60 testcase(
"One Side Empty Book");
61 using namespace std::chrono_literals;
65 auto USD =
Account(
"alice")[
"USD"];
74 j[jss::snapshot] =
true;
75 j[jss::taker_gets][jss::currency] =
"XRP";
76 j[jss::taker_pays][jss::currency] =
"USD";
77 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
80 auto jv = wsc->invoke(
"subscribe", books);
81 if (wsc->version() == 2)
84 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
86 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
87 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
89 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
92 jv[jss::result].isMember(jss::offers) &&
93 jv[jss::result][jss::offers].size() == 0);
94 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
95 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
100 env(offer(
"alice",
XRP(700), USD(100)),
105 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
106 auto const& t = jv[jss::transaction];
107 return t[jss::TransactionType] == jss::OfferCreate &&
109 USD(100).value().getJson(JsonOptions::none) &&
111 XRP(700).value().getJson(JsonOptions::none);
119 BEAST_EXPECT(!wsc->getMsg(10ms));
123 auto jv = wsc->invoke(
"unsubscribe", books);
124 BEAST_EXPECT(jv[jss::status] ==
"success");
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);
138 testcase(
"One Side Offers In Book");
139 using namespace std::chrono_literals;
143 auto USD =
Account(
"alice")[
"USD"];
159 j[jss::snapshot] =
true;
160 j[jss::taker_gets][jss::currency] =
"XRP";
161 j[jss::taker_pays][jss::currency] =
"USD";
162 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
165 auto jv = wsc->invoke(
"subscribe", books);
166 if (wsc->version() == 2)
169 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
171 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
172 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
174 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
177 jv[jss::result].isMember(jss::offers) &&
178 jv[jss::result][jss::offers].size() == 1);
180 jv[jss::result][jss::offers][0u][jss::TakerGets] ==
183 jv[jss::result][jss::offers][0u][jss::TakerPays] ==
185 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
186 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
191 env(offer(
"alice",
XRP(700), USD(100)),
196 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
197 auto const& t = jv[jss::transaction];
198 return t[jss::TransactionType] == jss::OfferCreate &&
200 USD(100).value().getJson(JsonOptions::none) &&
202 XRP(700).value().getJson(JsonOptions::none);
210 BEAST_EXPECT(!wsc->getMsg(10ms));
214 auto jv = wsc->invoke(
"unsubscribe", books);
215 BEAST_EXPECT(jv[jss::status] ==
"success");
216 if (wsc->version() == 2)
219 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
221 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
222 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
229 testcase(
"Both Sides Empty Book");
230 using namespace std::chrono_literals;
234 auto USD =
Account(
"alice")[
"USD"];
243 j[jss::snapshot] =
true;
245 j[jss::taker_gets][jss::currency] =
"XRP";
246 j[jss::taker_pays][jss::currency] =
"USD";
247 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
250 auto jv = wsc->invoke(
"subscribe", books);
251 if (wsc->version() == 2)
254 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
256 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
257 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
259 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
262 jv[jss::result].isMember(jss::asks) &&
263 jv[jss::result][jss::asks].size() == 0);
265 jv[jss::result].isMember(jss::bids) &&
266 jv[jss::result][jss::bids].size() == 0);
267 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
272 env(offer(
"alice",
XRP(700), USD(100)),
277 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
278 auto const& t = jv[jss::transaction];
279 return t[jss::TransactionType] == jss::OfferCreate &&
281 USD(100).value().getJson(JsonOptions::none) &&
283 XRP(700).value().getJson(JsonOptions::none);
293 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
294 auto const& t = jv[jss::transaction];
295 return t[jss::TransactionType] == jss::OfferCreate &&
297 XRP(75).value().getJson(JsonOptions::none) &&
299 USD(100).value().getJson(JsonOptions::none);
304 auto jv = wsc->invoke(
"unsubscribe", books);
305 BEAST_EXPECT(jv[jss::status] ==
"success");
306 if (wsc->version() == 2)
309 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
311 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
312 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
319 testcase(
"Both Sides Offers In Book");
320 using namespace std::chrono_literals;
324 auto USD =
Account(
"alice")[
"USD"];
340 j[jss::snapshot] =
true;
342 j[jss::taker_gets][jss::currency] =
"XRP";
343 j[jss::taker_pays][jss::currency] =
"USD";
344 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
347 auto jv = wsc->invoke(
"subscribe", books);
348 if (wsc->version() == 2)
351 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
353 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
354 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
356 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
359 jv[jss::result].isMember(jss::asks) &&
360 jv[jss::result][jss::asks].size() == 1);
362 jv[jss::result].isMember(jss::bids) &&
363 jv[jss::result][jss::bids].size() == 1);
365 jv[jss::result][jss::asks][0u][jss::TakerGets] ==
368 jv[jss::result][jss::asks][0u][jss::TakerPays] ==
371 jv[jss::result][jss::bids][0u][jss::TakerGets] ==
374 jv[jss::result][jss::bids][0u][jss::TakerPays] ==
376 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
381 env(offer(
"alice",
XRP(700), USD(100)),
386 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
387 auto const& t = jv[jss::transaction];
388 return t[jss::TransactionType] == jss::OfferCreate &&
390 USD(100).value().getJson(JsonOptions::none) &&
392 XRP(700).value().getJson(JsonOptions::none);
402 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
403 auto const& t = jv[jss::transaction];
404 return t[jss::TransactionType] == jss::OfferCreate &&
406 XRP(75).value().getJson(JsonOptions::none) &&
408 USD(100).value().getJson(JsonOptions::none);
413 auto jv = wsc->invoke(
"unsubscribe", books);
414 BEAST_EXPECT(jv[jss::status] ==
"success");
415 if (wsc->version() == 2)
418 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
420 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
421 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
428 testcase(
"Multiple Books, One Side Empty");
429 using namespace std::chrono_literals;
433 auto USD =
Account(
"alice")[
"USD"];
434 auto CNY =
Account(
"alice")[
"CNY"];
435 auto JPY =
Account(
"alice")[
"JPY"];
444 j[jss::snapshot] =
true;
445 j[jss::taker_gets][jss::currency] =
"XRP";
446 j[jss::taker_pays][jss::currency] =
"USD";
447 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
451 j[jss::snapshot] =
true;
452 j[jss::taker_gets][jss::currency] =
"CNY";
453 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
454 j[jss::taker_pays][jss::currency] =
"JPY";
455 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
458 auto jv = wsc->invoke(
"subscribe", books);
459 if (wsc->version() == 2)
462 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
464 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
465 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
467 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
470 jv[jss::result].isMember(jss::offers) &&
471 jv[jss::result][jss::offers].size() == 0);
472 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
473 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
478 env(offer(
"alice",
XRP(700), USD(100)),
483 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
484 auto const& t = jv[jss::transaction];
485 return t[jss::TransactionType] == jss::OfferCreate &&
487 USD(100).value().getJson(JsonOptions::none) &&
489 XRP(700).value().getJson(JsonOptions::none);
497 BEAST_EXPECT(!wsc->getMsg(10ms));
502 env(offer(
"alice", CNY(700), JPY(100)),
507 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
508 auto const& t = jv[jss::transaction];
509 return t[jss::TransactionType] == jss::OfferCreate &&
511 JPY(100).value().getJson(JsonOptions::none) &&
513 CNY(700).value().getJson(JsonOptions::none);
519 env(offer(
"alice", JPY(100), CNY(75)),
require(
owners(
"alice", 4)));
521 BEAST_EXPECT(!wsc->getMsg(10ms));
525 auto jv = wsc->invoke(
"unsubscribe", books);
526 BEAST_EXPECT(jv[jss::status] ==
"success");
527 if (wsc->version() == 2)
530 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
532 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
533 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
540 testcase(
"Multiple Books, One Side Offers In Book");
541 using namespace std::chrono_literals;
545 auto USD =
Account(
"alice")[
"USD"];
546 auto CNY =
Account(
"alice")[
"CNY"];
547 auto JPY =
Account(
"alice")[
"JPY"];
555 env(offer(
"alice", CNY(500), JPY(100)),
require(
owners(
"alice", 2)));
561 env(offer(
"alice", JPY(100), CNY(200)),
require(
owners(
"alice", 4)));
569 j[jss::snapshot] =
true;
570 j[jss::taker_gets][jss::currency] =
"XRP";
571 j[jss::taker_pays][jss::currency] =
"USD";
572 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
576 j[jss::snapshot] =
true;
577 j[jss::taker_gets][jss::currency] =
"CNY";
578 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
579 j[jss::taker_pays][jss::currency] =
"JPY";
580 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
583 auto jv = wsc->invoke(
"subscribe", books);
584 if (wsc->version() == 2)
587 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
589 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
590 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
592 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
595 jv[jss::result].isMember(jss::offers) &&
596 jv[jss::result][jss::offers].size() == 2);
598 jv[jss::result][jss::offers][0u][jss::TakerGets] ==
601 jv[jss::result][jss::offers][0u][jss::TakerPays] ==
604 jv[jss::result][jss::offers][1u][jss::TakerGets] ==
607 jv[jss::result][jss::offers][1u][jss::TakerPays] ==
609 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
610 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
615 env(offer(
"alice",
XRP(700), USD(100)),
620 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
621 auto const& t = jv[jss::transaction];
622 return t[jss::TransactionType] == jss::OfferCreate &&
624 USD(100).value().getJson(JsonOptions::none) &&
626 XRP(700).value().getJson(JsonOptions::none);
634 BEAST_EXPECT(!wsc->getMsg(10ms));
639 env(offer(
"alice", CNY(700), JPY(100)),
644 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
645 auto const& t = jv[jss::transaction];
646 return t[jss::TransactionType] == jss::OfferCreate &&
648 JPY(100).value().getJson(JsonOptions::none) &&
650 CNY(700).value().getJson(JsonOptions::none);
656 env(offer(
"alice", JPY(100), CNY(75)),
require(
owners(
"alice", 8)));
658 BEAST_EXPECT(!wsc->getMsg(10ms));
662 auto jv = wsc->invoke(
"unsubscribe", books);
663 BEAST_EXPECT(jv[jss::status] ==
"success");
664 if (wsc->version() == 2)
667 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
669 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
670 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
677 testcase(
"Multiple Books, Both Sides Empty Book");
678 using namespace std::chrono_literals;
682 auto USD =
Account(
"alice")[
"USD"];
683 auto CNY =
Account(
"alice")[
"CNY"];
684 auto JPY =
Account(
"alice")[
"JPY"];
693 j[jss::snapshot] =
true;
695 j[jss::taker_gets][jss::currency] =
"XRP";
696 j[jss::taker_pays][jss::currency] =
"USD";
697 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
701 j[jss::snapshot] =
true;
703 j[jss::taker_gets][jss::currency] =
"CNY";
704 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
705 j[jss::taker_pays][jss::currency] =
"JPY";
706 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
709 auto jv = wsc->invoke(
"subscribe", books);
710 if (wsc->version() == 2)
713 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
715 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
716 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
718 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
721 jv[jss::result].isMember(jss::asks) &&
722 jv[jss::result][jss::asks].size() == 0);
724 jv[jss::result].isMember(jss::bids) &&
725 jv[jss::result][jss::bids].size() == 0);
726 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
731 env(offer(
"alice",
XRP(700), USD(100)),
736 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
737 auto const& t = jv[jss::transaction];
738 return t[jss::TransactionType] == jss::OfferCreate &&
740 USD(100).value().getJson(JsonOptions::none) &&
742 XRP(700).value().getJson(JsonOptions::none);
752 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
753 auto const& t = jv[jss::transaction];
754 return t[jss::TransactionType] == jss::OfferCreate &&
756 XRP(75).value().getJson(JsonOptions::none) &&
758 USD(100).value().getJson(JsonOptions::none);
764 env(offer(
"alice", CNY(700), JPY(100)),
769 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
770 auto const& t = jv[jss::transaction];
771 return t[jss::TransactionType] == jss::OfferCreate &&
773 JPY(100).value().getJson(JsonOptions::none) &&
775 CNY(700).value().getJson(JsonOptions::none);
781 env(offer(
"alice", JPY(100), CNY(75)),
require(
owners(
"alice", 4)));
785 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
786 auto const& t = jv[jss::transaction];
787 return t[jss::TransactionType] == jss::OfferCreate &&
789 CNY(75).value().getJson(JsonOptions::none) &&
791 JPY(100).value().getJson(JsonOptions::none);
796 auto jv = wsc->invoke(
"unsubscribe", books);
797 BEAST_EXPECT(jv[jss::status] ==
"success");
798 if (wsc->version() == 2)
801 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
803 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
804 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
811 testcase(
"Multiple Books, Both Sides Offers In Book");
812 using namespace std::chrono_literals;
816 auto USD =
Account(
"alice")[
"USD"];
817 auto CNY =
Account(
"alice")[
"CNY"];
818 auto JPY =
Account(
"alice")[
"JPY"];
826 env(offer(
"alice", CNY(500), JPY(100)),
require(
owners(
"alice", 2)));
832 env(offer(
"alice", JPY(100), CNY(200)),
require(
owners(
"alice", 4)));
840 j[jss::snapshot] =
true;
842 j[jss::taker_gets][jss::currency] =
"XRP";
843 j[jss::taker_pays][jss::currency] =
"USD";
844 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
849 j[jss::snapshot] =
true;
851 j[jss::taker_gets][jss::currency] =
"CNY";
852 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
853 j[jss::taker_pays][jss::currency] =
"JPY";
854 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
857 auto jv = wsc->invoke(
"subscribe", books);
858 if (wsc->version() == 2)
861 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
863 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
864 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
866 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
869 jv[jss::result].isMember(jss::asks) &&
870 jv[jss::result][jss::asks].size() == 2);
872 jv[jss::result].isMember(jss::bids) &&
873 jv[jss::result][jss::bids].size() == 2);
875 jv[jss::result][jss::asks][0u][jss::TakerGets] ==
878 jv[jss::result][jss::asks][0u][jss::TakerPays] ==
881 jv[jss::result][jss::asks][1u][jss::TakerGets] ==
884 jv[jss::result][jss::asks][1u][jss::TakerPays] ==
887 jv[jss::result][jss::bids][0u][jss::TakerGets] ==
890 jv[jss::result][jss::bids][0u][jss::TakerPays] ==
893 jv[jss::result][jss::bids][1u][jss::TakerGets] ==
896 jv[jss::result][jss::bids][1u][jss::TakerPays] ==
898 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
903 env(offer(
"alice",
XRP(700), USD(100)),
908 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
909 auto const& t = jv[jss::transaction];
910 return t[jss::TransactionType] == jss::OfferCreate &&
912 USD(100).value().getJson(JsonOptions::none) &&
914 XRP(700).value().getJson(JsonOptions::none);
924 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
925 auto const& t = jv[jss::transaction];
926 return t[jss::TransactionType] == jss::OfferCreate &&
928 XRP(75).value().getJson(JsonOptions::none) &&
930 USD(100).value().getJson(JsonOptions::none);
936 env(offer(
"alice", CNY(700), JPY(100)),
941 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
942 auto const& t = jv[jss::transaction];
943 return t[jss::TransactionType] == jss::OfferCreate &&
945 JPY(100).value().getJson(JsonOptions::none) &&
947 CNY(700).value().getJson(JsonOptions::none);
953 env(offer(
"alice", JPY(100), CNY(75)),
require(
owners(
"alice", 8)));
957 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
958 auto const& t = jv[jss::transaction];
959 return t[jss::TransactionType] == jss::OfferCreate &&
961 CNY(75).value().getJson(JsonOptions::none) &&
963 JPY(100).value().getJson(JsonOptions::none);
968 auto jv = wsc->invoke(
"unsubscribe", books);
969 BEAST_EXPECT(jv[jss::status] ==
"success");
970 if (wsc->version() == 2)
973 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
975 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
976 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
983 testcase(
"TrackOffers");
990 env.
fund(
XRP(20000), alice, bob, gw);
992 auto USD = gw[
"USD"];
999 j[jss::snapshot] =
true;
1000 j[jss::taker_gets][jss::currency] =
"XRP";
1001 j[jss::taker_pays][jss::currency] =
"USD";
1002 j[jss::taker_pays][jss::issuer] = gw.human();
1005 auto jv = wsc->invoke(
"subscribe", books);
1006 if (wsc->version() == 2)
1009 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1011 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1012 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1014 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1017 jv[jss::result].isMember(jss::offers) &&
1018 jv[jss::result][jss::offers].size() == 0);
1019 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
1020 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
1025 env.
trust(USD(1000), alice);
1026 env.
trust(USD(1000), bob);
1027 env(
pay(gw, alice, USD(100)));
1028 env(
pay(gw, bob, USD(50)));
1029 env(offer(alice,
XRP(4000), USD(10)));
1034 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1035 jvParams[jss::ledger_index] =
"validated";
1036 jvParams[jss::taker_gets][jss::currency] =
"USD";
1037 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1039 auto jv = wsc->invoke(
"book_offers", jvParams);
1040 if (wsc->version() == 2)
1043 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1045 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1046 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1048 auto jrr = jv[jss::result];
1050 BEAST_EXPECT(jrr[jss::offers].isArray());
1051 BEAST_EXPECT(jrr[jss::offers].size() == 1);
1052 auto const jrOffer = jrr[jss::offers][0u];
1058 BEAST_EXPECT(jrOffer[jss::Flags] == 0);
1063 jrOffer[jss::TakerGets] ==
1066 jrOffer[jss::TakerPays] ==
1068 BEAST_EXPECT(jrOffer[jss::owner_funds] ==
"100");
1069 BEAST_EXPECT(jrOffer[jss::quality] ==
"400000000");
1071 using namespace std::chrono_literals;
1072 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jval) {
1073 auto const& t = jval[jss::transaction];
1074 return t[jss::TransactionType] == jss::OfferCreate &&
1075 t[jss::TakerGets] ==
1076 USD(10).value().getJson(JsonOptions::none) &&
1077 t[jss::owner_funds] ==
"100" &&
1078 t[jss::TakerPays] ==
1079 XRP(4000).value().getJson(JsonOptions::none);
1082 env(offer(bob,
XRP(2000), USD(5)));
1085 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jval) {
1086 auto const& t = jval[jss::transaction];
1087 return t[jss::TransactionType] == jss::OfferCreate &&
1088 t[jss::TakerGets] ==
1089 USD(5).value().getJson(JsonOptions::none) &&
1090 t[jss::owner_funds] ==
"50" &&
1091 t[jss::TakerPays] ==
1092 XRP(2000).value().getJson(JsonOptions::none);
1095 jv = wsc->invoke(
"book_offers", jvParams);
1096 if (wsc->version() == 2)
1099 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1101 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1102 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1104 jrr = jv[jss::result];
1106 BEAST_EXPECT(jrr[jss::offers].isArray());
1107 BEAST_EXPECT(jrr[jss::offers].size() == 2);
1108 auto const jrNextOffer = jrr[jss::offers][1u];
1114 BEAST_EXPECT(jrNextOffer[jss::Flags] == 0);
1119 jrNextOffer[jss::TakerGets] ==
1122 jrNextOffer[jss::TakerPays] ==
1124 BEAST_EXPECT(jrNextOffer[jss::owner_funds] ==
"50");
1125 BEAST_EXPECT(jrNextOffer[jss::quality] ==
"400000000");
1127 jv = wsc->invoke(
"unsubscribe", books);
1128 if (wsc->version() == 2)
1131 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1133 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1134 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1136 BEAST_EXPECT(jv[jss::status] ==
"success");
1147 auto maybeJv = wsc->getMsg(timeout);
1152 if (!(*maybeJv).isMember(jss::transaction))
1154 auto const& t = (*maybeJv)[jss::transaction];
1155 if (t[jss::TransactionType] != jss::OfferCreate ||
1160 return wsc->getMsg(timeout) == std::nullopt;
1166 testcase(
"Crossing single book offer");
1172 using namespace jtx;
1179 auto const gw =
Account(
"gateway");
1180 auto const alice =
Account(
"alice");
1181 auto const bob =
Account(
"bob");
1182 auto const charlie =
Account(
"charlie");
1183 auto const USD = gw[
"USD"];
1185 env.
fund(
XRP(1000000), gw, alice, bob, charlie);
1188 env(
trust(alice, USD(500)));
1189 env(
trust(bob, USD(500)));
1192 env(
pay(gw, alice, USD(500)));
1193 env(
pay(gw, bob, USD(500)));
1197 env(offer(alice,
XRP(500), USD(500)));
1198 env(offer(bob,
XRP(500), USD(500)));
1208 j[jss::snapshot] =
false;
1209 j[jss::taker_gets][jss::currency] =
"XRP";
1210 j[jss::taker_pays][jss::currency] =
"USD";
1211 j[jss::taker_pays][jss::issuer] = gw.human();
1214 auto jv = wsc->invoke(
"subscribe", books);
1215 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1220 env(offer(charlie, USD(1000),
XRP(1000)));
1223 using namespace std::chrono_literals;
1227 auto jv = wsc->invoke(
"unsubscribe", books);
1228 BEAST_EXPECT(jv[jss::status] ==
"success");
1234 testcase(
"Crossing multi-book offer");
1240 using namespace jtx;
1249 auto const gw =
Account(
"gateway");
1250 auto const alice =
Account(
"alice");
1251 auto const bob =
Account(
"bob");
1252 auto const charlie =
Account(
"charlie");
1253 auto const USD = gw[
"USD"];
1254 auto const EUR = gw[
"EUR"];
1256 env.
fund(
XRP(1000000), gw, alice, bob, charlie);
1259 for (
auto const& account : {alice, bob, charlie})
1261 for (
auto const& iou : {USD, EUR})
1263 env(
trust(account, iou(1)));
1268 env(
pay(gw, alice, USD(1)));
1269 env(
pay(gw, charlie, EUR(1)));
1272 env(offer(alice,
XRP(100), USD(1)));
1273 env(offer(bob, EUR(1),
XRP(100)));
1284 j[jss::snapshot] =
false;
1285 j[jss::taker_gets][jss::currency] =
"XRP";
1286 j[jss::taker_pays][jss::currency] =
"USD";
1287 j[jss::taker_pays][jss::issuer] = gw.human();
1292 j[jss::snapshot] =
false;
1293 j[jss::taker_gets][jss::currency] =
"EUR";
1294 j[jss::taker_gets][jss::issuer] = gw.human();
1295 j[jss::taker_pays][jss::currency] =
"XRP";
1298 auto jv = wsc->invoke(
"subscribe", books);
1299 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1304 env(offer(charlie, USD(1), EUR(1)));
1306 using namespace std::chrono_literals;
1310 auto jv = wsc->invoke(
"unsubscribe", books);
1311 BEAST_EXPECT(jv[jss::status] ==
"success");
1317 testcase(
"BookOffersRPC Errors");
1318 using namespace jtx;
1322 env.
fund(
XRP(10000), alice, gw);
1324 auto USD = gw[
"USD"];
1328 jvParams[jss::ledger_index] = 10u;
1329 auto const jrr = env.
rpc(
1330 "json",
"book_offers",
to_string(jvParams))[jss::result];
1331 BEAST_EXPECT(jrr[jss::error] ==
"lgrNotFound");
1332 BEAST_EXPECT(jrr[jss::error_message] ==
"ledgerNotFound");
1337 jvParams[jss::ledger_index] =
"validated";
1338 auto const jrr = env.
rpc(
1339 "json",
"book_offers",
to_string(jvParams))[jss::result];
1340 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1342 jrr[jss::error_message] ==
"Missing field 'taker_pays'.");
1347 jvParams[jss::ledger_index] =
"validated";
1349 auto const jrr = env.
rpc(
1350 "json",
"book_offers",
to_string(jvParams))[jss::result];
1351 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1353 jrr[jss::error_message] ==
"Missing field 'taker_gets'.");
1358 jvParams[jss::ledger_index] =
"validated";
1359 jvParams[jss::taker_pays] =
"not an object";
1361 auto const jrr = env.
rpc(
1362 "json",
"book_offers",
to_string(jvParams))[jss::result];
1363 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1365 jrr[jss::error_message] ==
1366 "Invalid field 'taker_pays', not object.");
1371 jvParams[jss::ledger_index] =
"validated";
1373 jvParams[jss::taker_gets] =
"not an object";
1374 auto const jrr = env.
rpc(
1375 "json",
"book_offers",
to_string(jvParams))[jss::result];
1376 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1378 jrr[jss::error_message] ==
1379 "Invalid field 'taker_gets', not object.");
1384 jvParams[jss::ledger_index] =
"validated";
1387 auto const jrr = env.
rpc(
1388 "json",
"book_offers",
to_string(jvParams))[jss::result];
1389 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1391 jrr[jss::error_message] ==
1392 "Missing field 'taker_pays.currency'.");
1397 jvParams[jss::ledger_index] =
"validated";
1398 jvParams[jss::taker_pays][jss::currency] = 1;
1400 auto const jrr = env.
rpc(
1401 "json",
"book_offers",
to_string(jvParams))[jss::result];
1402 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1404 jrr[jss::error_message] ==
1405 "Invalid field 'taker_pays.currency', not string.");
1410 jvParams[jss::ledger_index] =
"validated";
1411 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1413 auto const jrr = env.
rpc(
1414 "json",
"book_offers",
to_string(jvParams))[jss::result];
1415 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1417 jrr[jss::error_message] ==
1418 "Missing field 'taker_gets.currency'.");
1423 jvParams[jss::ledger_index] =
"validated";
1424 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1425 jvParams[jss::taker_gets][jss::currency] = 1;
1426 auto const jrr = env.
rpc(
1427 "json",
"book_offers",
to_string(jvParams))[jss::result];
1428 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1430 jrr[jss::error_message] ==
1431 "Invalid field 'taker_gets.currency', not string.");
1436 jvParams[jss::ledger_index] =
"validated";
1437 jvParams[jss::taker_pays][jss::currency] =
"NOT_VALID";
1438 jvParams[jss::taker_gets][jss::currency] =
"XRP";
1439 auto const jrr = env.
rpc(
1440 "json",
"book_offers",
to_string(jvParams))[jss::result];
1441 BEAST_EXPECT(jrr[jss::error] ==
"srcCurMalformed");
1443 jrr[jss::error_message] ==
1444 "Invalid field 'taker_pays.currency', bad currency.");
1449 jvParams[jss::ledger_index] =
"validated";
1450 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1451 jvParams[jss::taker_gets][jss::currency] =
"NOT_VALID";
1452 auto const jrr = env.
rpc(
1453 "json",
"book_offers",
to_string(jvParams))[jss::result];
1454 BEAST_EXPECT(jrr[jss::error] ==
"dstAmtMalformed");
1456 jrr[jss::error_message] ==
1457 "Invalid field 'taker_gets.currency', bad currency.");
1462 jvParams[jss::ledger_index] =
"validated";
1463 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1464 jvParams[jss::taker_gets][jss::currency] =
"USD";
1465 jvParams[jss::taker_gets][jss::issuer] = 1;
1466 auto const jrr = env.
rpc(
1467 "json",
"book_offers",
to_string(jvParams))[jss::result];
1468 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1470 jrr[jss::error_message] ==
1471 "Invalid field 'taker_gets.issuer', not string.");
1476 jvParams[jss::ledger_index] =
"validated";
1477 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1478 jvParams[jss::taker_pays][jss::issuer] = 1;
1479 jvParams[jss::taker_gets][jss::currency] =
"USD";
1480 auto const jrr = env.
rpc(
1481 "json",
"book_offers",
to_string(jvParams))[jss::result];
1482 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1484 jrr[jss::error_message] ==
1485 "Invalid field 'taker_pays.issuer', not string.");
1490 jvParams[jss::ledger_index] =
"validated";
1491 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1492 jvParams[jss::taker_pays][jss::issuer] = gw.human() +
"DEAD";
1493 jvParams[jss::taker_gets][jss::currency] =
"USD";
1494 auto const jrr = env.
rpc(
1495 "json",
"book_offers",
to_string(jvParams))[jss::result];
1496 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1498 jrr[jss::error_message] ==
1499 "Invalid field 'taker_pays.issuer', bad issuer.");
1504 jvParams[jss::ledger_index] =
"validated";
1505 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1507 jvParams[jss::taker_gets][jss::currency] =
"USD";
1508 auto const jrr = env.
rpc(
1509 "json",
"book_offers",
to_string(jvParams))[jss::result];
1510 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1512 jrr[jss::error_message] ==
1513 "Invalid field 'taker_pays.issuer', bad issuer account one.");
1518 jvParams[jss::ledger_index] =
"validated";
1519 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1520 jvParams[jss::taker_gets][jss::currency] =
"USD";
1521 jvParams[jss::taker_gets][jss::issuer] = gw.human() +
"DEAD";
1522 auto const jrr = env.
rpc(
1523 "json",
"book_offers",
to_string(jvParams))[jss::result];
1524 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1526 jrr[jss::error_message] ==
1527 "Invalid field 'taker_gets.issuer', bad issuer.");
1532 jvParams[jss::ledger_index] =
"validated";
1533 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1534 jvParams[jss::taker_gets][jss::currency] =
"USD";
1536 auto const jrr = env.
rpc(
1537 "json",
"book_offers",
to_string(jvParams))[jss::result];
1538 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1540 jrr[jss::error_message] ==
1541 "Invalid field 'taker_gets.issuer', bad issuer account one.");
1546 jvParams[jss::ledger_index] =
"validated";
1547 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1548 jvParams[jss::taker_pays][jss::issuer] = alice.human();
1549 jvParams[jss::taker_gets][jss::currency] =
"USD";
1550 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1551 auto const jrr = env.
rpc(
1552 "json",
"book_offers",
to_string(jvParams))[jss::result];
1553 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1555 jrr[jss::error_message] ==
1556 "Unneeded field 'taker_pays.issuer' "
1557 "for XRP currency specification.");
1562 jvParams[jss::ledger_index] =
"validated";
1563 jvParams[jss::taker_pays][jss::currency] =
"USD";
1565 jvParams[jss::taker_gets][jss::currency] =
"USD";
1566 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1567 auto const jrr = env.
rpc(
1568 "json",
"book_offers",
to_string(jvParams))[jss::result];
1569 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1571 jrr[jss::error_message] ==
1572 "Invalid field 'taker_pays.issuer', expected non-XRP issuer.");
1577 jvParams[jss::ledger_index] =
"validated";
1578 jvParams[jss::taker] = 1;
1579 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1580 jvParams[jss::taker_gets][jss::currency] =
"USD";
1581 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1582 auto const jrr = env.
rpc(
1583 "json",
"book_offers",
to_string(jvParams))[jss::result];
1584 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1586 jrr[jss::error_message] ==
1587 "Invalid field 'taker', not string.");
1592 jvParams[jss::ledger_index] =
"validated";
1593 jvParams[jss::taker] = env.
master.
human() +
"DEAD";
1594 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1595 jvParams[jss::taker_gets][jss::currency] =
"USD";
1596 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1597 auto const jrr = env.
rpc(
1598 "json",
"book_offers",
to_string(jvParams))[jss::result];
1599 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1600 BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker'.");
1605 jvParams[jss::ledger_index] =
"validated";
1607 jvParams[jss::taker_pays][jss::currency] =
"USD";
1608 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1609 jvParams[jss::taker_gets][jss::currency] =
"USD";
1610 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1611 auto const jrr = env.
rpc(
1612 "json",
"book_offers",
to_string(jvParams))[jss::result];
1613 BEAST_EXPECT(jrr[jss::error] ==
"badMarket");
1614 BEAST_EXPECT(jrr[jss::error_message] ==
"No such market.");
1619 jvParams[jss::ledger_index] =
"validated";
1621 jvParams[jss::limit] =
"0";
1622 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1623 jvParams[jss::taker_gets][jss::currency] =
"USD";
1624 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1625 auto const jrr = env.
rpc(
1626 "json",
"book_offers",
to_string(jvParams))[jss::result];
1627 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1629 jrr[jss::error_message] ==
1630 "Invalid field 'limit', not unsigned integer.");
1635 jvParams[jss::ledger_index] =
"validated";
1636 jvParams[jss::taker_pays][jss::currency] =
"USD";
1637 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1638 jvParams[jss::taker_gets][jss::currency] =
"USD";
1639 auto const jrr = env.
rpc(
1640 "json",
"book_offers",
to_string(jvParams))[jss::result];
1641 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1643 jrr[jss::error_message] ==
1644 "Invalid field 'taker_gets.issuer', "
1645 "expected non-XRP issuer.");
1650 jvParams[jss::ledger_index] =
"validated";
1651 jvParams[jss::taker_pays][jss::currency] =
"USD";
1652 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1653 jvParams[jss::taker_gets][jss::currency] =
"XRP";
1654 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1655 auto const jrr = env.
rpc(
1656 "json",
"book_offers",
to_string(jvParams))[jss::result];
1657 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1659 jrr[jss::error_message] ==
1660 "Unneeded field 'taker_gets.issuer' "
1661 "for XRP currency specification.");
1668 testcase(
"BookOffer Limits");
1669 using namespace jtx;
1672 env.fund(
XRP(200000), gw);
1677 auto USD = gw[
"USD"];
1680 env(offer(gw,
XRP(50 + 1 * i), USD(1.0 + 0.1 * i)));
1686 jvParams[jss::limit] = 1;
1687 jvParams[jss::ledger_index] =
"validated";
1688 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1689 jvParams[jss::taker_gets][jss::currency] =
"USD";
1690 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1692 env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1693 BEAST_EXPECT(jrr[jss::offers].isArray());
1694 BEAST_EXPECT(jrr[jss::offers].size() == (asAdmin ? 1u : 0u));
1697 jvParams[jss::limit] = 0u;
1698 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1699 BEAST_EXPECT(jrr[jss::offers].isArray());
1700 BEAST_EXPECT(jrr[jss::offers].size() == 0u);
1703 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1704 BEAST_EXPECT(jrr[jss::offers].isArray());
1706 jrr[jss::offers].size() ==
1710 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1711 BEAST_EXPECT(jrr[jss::offers].isArray());
1713 jrr[jss::offers].size() ==