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;
44 *view, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex);
55 testcase(
"One Side Empty Book");
56 using namespace std::chrono_literals;
60 auto USD =
Account(
"alice")[
"USD"];
69 j[jss::snapshot] =
true;
70 j[jss::taker_gets][jss::currency] =
"XRP";
71 j[jss::taker_pays][jss::currency] =
"USD";
72 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
75 auto jv = wsc->invoke(
"subscribe", books);
76 if (wsc->version() == 2)
79 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
81 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
82 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
84 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
87 jv[jss::result].isMember(jss::offers) &&
88 jv[jss::result][jss::offers].size() == 0);
89 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
90 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
95 env(offer(
"alice",
XRP(700), USD(100)),
100 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
101 auto const& t = jv[jss::transaction];
102 return t[jss::TransactionType] == jss::OfferCreate &&
104 USD(100).value().getJson(JsonOptions::none) &&
106 XRP(700).value().getJson(JsonOptions::none);
114 BEAST_EXPECT(!wsc->getMsg(10ms));
118 auto jv = wsc->invoke(
"unsubscribe", books);
119 BEAST_EXPECT(jv[jss::status] ==
"success");
120 if (wsc->version() == 2)
123 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
125 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
126 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
133 testcase(
"One Side Offers In Book");
134 using namespace std::chrono_literals;
138 auto USD =
Account(
"alice")[
"USD"];
154 j[jss::snapshot] =
true;
155 j[jss::taker_gets][jss::currency] =
"XRP";
156 j[jss::taker_pays][jss::currency] =
"USD";
157 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
160 auto jv = wsc->invoke(
"subscribe", books);
161 if (wsc->version() == 2)
164 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
166 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
167 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
169 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
172 jv[jss::result].isMember(jss::offers) &&
173 jv[jss::result][jss::offers].size() == 1);
175 jv[jss::result][jss::offers][0u][jss::TakerGets] ==
178 jv[jss::result][jss::offers][0u][jss::TakerPays] ==
180 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
181 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
186 env(offer(
"alice",
XRP(700), USD(100)),
191 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
192 auto const& t = jv[jss::transaction];
193 return t[jss::TransactionType] == jss::OfferCreate &&
195 USD(100).value().getJson(JsonOptions::none) &&
197 XRP(700).value().getJson(JsonOptions::none);
205 BEAST_EXPECT(!wsc->getMsg(10ms));
209 auto jv = wsc->invoke(
"unsubscribe", books);
210 BEAST_EXPECT(jv[jss::status] ==
"success");
211 if (wsc->version() == 2)
214 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
216 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
217 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
224 testcase(
"Both Sides Empty Book");
225 using namespace std::chrono_literals;
229 auto USD =
Account(
"alice")[
"USD"];
238 j[jss::snapshot] =
true;
240 j[jss::taker_gets][jss::currency] =
"XRP";
241 j[jss::taker_pays][jss::currency] =
"USD";
242 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
245 auto jv = wsc->invoke(
"subscribe", books);
246 if (wsc->version() == 2)
249 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
251 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
252 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
254 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
257 jv[jss::result].isMember(jss::asks) &&
258 jv[jss::result][jss::asks].size() == 0);
260 jv[jss::result].isMember(jss::bids) &&
261 jv[jss::result][jss::bids].size() == 0);
262 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
267 env(offer(
"alice",
XRP(700), USD(100)),
272 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
273 auto const& t = jv[jss::transaction];
274 return t[jss::TransactionType] == jss::OfferCreate &&
276 USD(100).value().getJson(JsonOptions::none) &&
278 XRP(700).value().getJson(JsonOptions::none);
288 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
289 auto const& t = jv[jss::transaction];
290 return t[jss::TransactionType] == jss::OfferCreate &&
292 XRP(75).value().getJson(JsonOptions::none) &&
294 USD(100).value().getJson(JsonOptions::none);
299 auto jv = wsc->invoke(
"unsubscribe", books);
300 BEAST_EXPECT(jv[jss::status] ==
"success");
301 if (wsc->version() == 2)
304 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
306 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
307 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
314 testcase(
"Both Sides Offers In Book");
315 using namespace std::chrono_literals;
319 auto USD =
Account(
"alice")[
"USD"];
335 j[jss::snapshot] =
true;
337 j[jss::taker_gets][jss::currency] =
"XRP";
338 j[jss::taker_pays][jss::currency] =
"USD";
339 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
342 auto jv = wsc->invoke(
"subscribe", books);
343 if (wsc->version() == 2)
346 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
348 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
349 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
351 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
354 jv[jss::result].isMember(jss::asks) &&
355 jv[jss::result][jss::asks].size() == 1);
357 jv[jss::result].isMember(jss::bids) &&
358 jv[jss::result][jss::bids].size() == 1);
360 jv[jss::result][jss::asks][0u][jss::TakerGets] ==
363 jv[jss::result][jss::asks][0u][jss::TakerPays] ==
366 jv[jss::result][jss::bids][0u][jss::TakerGets] ==
369 jv[jss::result][jss::bids][0u][jss::TakerPays] ==
371 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
376 env(offer(
"alice",
XRP(700), USD(100)),
381 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
382 auto const& t = jv[jss::transaction];
383 return t[jss::TransactionType] == jss::OfferCreate &&
385 USD(100).value().getJson(JsonOptions::none) &&
387 XRP(700).value().getJson(JsonOptions::none);
397 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
398 auto const& t = jv[jss::transaction];
399 return t[jss::TransactionType] == jss::OfferCreate &&
401 XRP(75).value().getJson(JsonOptions::none) &&
403 USD(100).value().getJson(JsonOptions::none);
408 auto jv = wsc->invoke(
"unsubscribe", books);
409 BEAST_EXPECT(jv[jss::status] ==
"success");
410 if (wsc->version() == 2)
413 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
415 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
416 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
423 testcase(
"Multiple Books, One Side Empty");
424 using namespace std::chrono_literals;
428 auto USD =
Account(
"alice")[
"USD"];
429 auto CNY =
Account(
"alice")[
"CNY"];
430 auto JPY =
Account(
"alice")[
"JPY"];
439 j[jss::snapshot] =
true;
440 j[jss::taker_gets][jss::currency] =
"XRP";
441 j[jss::taker_pays][jss::currency] =
"USD";
442 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
446 j[jss::snapshot] =
true;
447 j[jss::taker_gets][jss::currency] =
"CNY";
448 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
449 j[jss::taker_pays][jss::currency] =
"JPY";
450 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
453 auto jv = wsc->invoke(
"subscribe", books);
454 if (wsc->version() == 2)
457 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
459 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
460 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
462 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
465 jv[jss::result].isMember(jss::offers) &&
466 jv[jss::result][jss::offers].size() == 0);
467 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
468 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
473 env(offer(
"alice",
XRP(700), USD(100)),
478 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
479 auto const& t = jv[jss::transaction];
480 return t[jss::TransactionType] == jss::OfferCreate &&
482 USD(100).value().getJson(JsonOptions::none) &&
484 XRP(700).value().getJson(JsonOptions::none);
492 BEAST_EXPECT(!wsc->getMsg(10ms));
497 env(offer(
"alice", CNY(700), JPY(100)),
502 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
503 auto const& t = jv[jss::transaction];
504 return t[jss::TransactionType] == jss::OfferCreate &&
506 JPY(100).value().getJson(JsonOptions::none) &&
508 CNY(700).value().getJson(JsonOptions::none);
514 env(offer(
"alice", JPY(100), CNY(75)),
require(
owners(
"alice", 4)));
516 BEAST_EXPECT(!wsc->getMsg(10ms));
520 auto jv = wsc->invoke(
"unsubscribe", books);
521 BEAST_EXPECT(jv[jss::status] ==
"success");
522 if (wsc->version() == 2)
525 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
527 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
528 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
535 testcase(
"Multiple Books, One Side Offers In Book");
536 using namespace std::chrono_literals;
540 auto USD =
Account(
"alice")[
"USD"];
541 auto CNY =
Account(
"alice")[
"CNY"];
542 auto JPY =
Account(
"alice")[
"JPY"];
550 env(offer(
"alice", CNY(500), JPY(100)),
require(
owners(
"alice", 2)));
556 env(offer(
"alice", JPY(100), CNY(200)),
require(
owners(
"alice", 4)));
564 j[jss::snapshot] =
true;
565 j[jss::taker_gets][jss::currency] =
"XRP";
566 j[jss::taker_pays][jss::currency] =
"USD";
567 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
571 j[jss::snapshot] =
true;
572 j[jss::taker_gets][jss::currency] =
"CNY";
573 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
574 j[jss::taker_pays][jss::currency] =
"JPY";
575 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
578 auto jv = wsc->invoke(
"subscribe", books);
579 if (wsc->version() == 2)
582 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
584 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
585 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
587 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
590 jv[jss::result].isMember(jss::offers) &&
591 jv[jss::result][jss::offers].size() == 2);
593 jv[jss::result][jss::offers][0u][jss::TakerGets] ==
596 jv[jss::result][jss::offers][0u][jss::TakerPays] ==
599 jv[jss::result][jss::offers][1u][jss::TakerGets] ==
602 jv[jss::result][jss::offers][1u][jss::TakerPays] ==
604 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
605 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
610 env(offer(
"alice",
XRP(700), USD(100)),
615 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
616 auto const& t = jv[jss::transaction];
617 return t[jss::TransactionType] == jss::OfferCreate &&
619 USD(100).value().getJson(JsonOptions::none) &&
621 XRP(700).value().getJson(JsonOptions::none);
629 BEAST_EXPECT(!wsc->getMsg(10ms));
634 env(offer(
"alice", CNY(700), JPY(100)),
639 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
640 auto const& t = jv[jss::transaction];
641 return t[jss::TransactionType] == jss::OfferCreate &&
643 JPY(100).value().getJson(JsonOptions::none) &&
645 CNY(700).value().getJson(JsonOptions::none);
651 env(offer(
"alice", JPY(100), CNY(75)),
require(
owners(
"alice", 8)));
653 BEAST_EXPECT(!wsc->getMsg(10ms));
657 auto jv = wsc->invoke(
"unsubscribe", books);
658 BEAST_EXPECT(jv[jss::status] ==
"success");
659 if (wsc->version() == 2)
662 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
664 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
665 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
672 testcase(
"Multiple Books, Both Sides Empty Book");
673 using namespace std::chrono_literals;
677 auto USD =
Account(
"alice")[
"USD"];
678 auto CNY =
Account(
"alice")[
"CNY"];
679 auto JPY =
Account(
"alice")[
"JPY"];
688 j[jss::snapshot] =
true;
690 j[jss::taker_gets][jss::currency] =
"XRP";
691 j[jss::taker_pays][jss::currency] =
"USD";
692 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
696 j[jss::snapshot] =
true;
698 j[jss::taker_gets][jss::currency] =
"CNY";
699 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
700 j[jss::taker_pays][jss::currency] =
"JPY";
701 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
704 auto jv = wsc->invoke(
"subscribe", books);
705 if (wsc->version() == 2)
708 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
710 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
711 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
713 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
716 jv[jss::result].isMember(jss::asks) &&
717 jv[jss::result][jss::asks].size() == 0);
719 jv[jss::result].isMember(jss::bids) &&
720 jv[jss::result][jss::bids].size() == 0);
721 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
726 env(offer(
"alice",
XRP(700), USD(100)),
731 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
732 auto const& t = jv[jss::transaction];
733 return t[jss::TransactionType] == jss::OfferCreate &&
735 USD(100).value().getJson(JsonOptions::none) &&
737 XRP(700).value().getJson(JsonOptions::none);
747 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
748 auto const& t = jv[jss::transaction];
749 return t[jss::TransactionType] == jss::OfferCreate &&
751 XRP(75).value().getJson(JsonOptions::none) &&
753 USD(100).value().getJson(JsonOptions::none);
759 env(offer(
"alice", CNY(700), JPY(100)),
764 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
765 auto const& t = jv[jss::transaction];
766 return t[jss::TransactionType] == jss::OfferCreate &&
768 JPY(100).value().getJson(JsonOptions::none) &&
770 CNY(700).value().getJson(JsonOptions::none);
776 env(offer(
"alice", JPY(100), CNY(75)),
require(
owners(
"alice", 4)));
780 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
781 auto const& t = jv[jss::transaction];
782 return t[jss::TransactionType] == jss::OfferCreate &&
784 CNY(75).value().getJson(JsonOptions::none) &&
786 JPY(100).value().getJson(JsonOptions::none);
791 auto jv = wsc->invoke(
"unsubscribe", books);
792 BEAST_EXPECT(jv[jss::status] ==
"success");
793 if (wsc->version() == 2)
796 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
798 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
799 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
806 testcase(
"Multiple Books, Both Sides Offers In Book");
807 using namespace std::chrono_literals;
811 auto USD =
Account(
"alice")[
"USD"];
812 auto CNY =
Account(
"alice")[
"CNY"];
813 auto JPY =
Account(
"alice")[
"JPY"];
821 env(offer(
"alice", CNY(500), JPY(100)),
require(
owners(
"alice", 2)));
827 env(offer(
"alice", JPY(100), CNY(200)),
require(
owners(
"alice", 4)));
835 j[jss::snapshot] =
true;
837 j[jss::taker_gets][jss::currency] =
"XRP";
838 j[jss::taker_pays][jss::currency] =
"USD";
839 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
844 j[jss::snapshot] =
true;
846 j[jss::taker_gets][jss::currency] =
"CNY";
847 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
848 j[jss::taker_pays][jss::currency] =
"JPY";
849 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
852 auto jv = wsc->invoke(
"subscribe", books);
853 if (wsc->version() == 2)
856 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
858 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
859 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
861 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
864 jv[jss::result].isMember(jss::asks) &&
865 jv[jss::result][jss::asks].size() == 2);
867 jv[jss::result].isMember(jss::bids) &&
868 jv[jss::result][jss::bids].size() == 2);
870 jv[jss::result][jss::asks][0u][jss::TakerGets] ==
873 jv[jss::result][jss::asks][0u][jss::TakerPays] ==
876 jv[jss::result][jss::asks][1u][jss::TakerGets] ==
879 jv[jss::result][jss::asks][1u][jss::TakerPays] ==
882 jv[jss::result][jss::bids][0u][jss::TakerGets] ==
885 jv[jss::result][jss::bids][0u][jss::TakerPays] ==
888 jv[jss::result][jss::bids][1u][jss::TakerGets] ==
891 jv[jss::result][jss::bids][1u][jss::TakerPays] ==
893 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
898 env(offer(
"alice",
XRP(700), USD(100)),
903 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
904 auto const& t = jv[jss::transaction];
905 return t[jss::TransactionType] == jss::OfferCreate &&
907 USD(100).value().getJson(JsonOptions::none) &&
909 XRP(700).value().getJson(JsonOptions::none);
919 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
920 auto const& t = jv[jss::transaction];
921 return t[jss::TransactionType] == jss::OfferCreate &&
923 XRP(75).value().getJson(JsonOptions::none) &&
925 USD(100).value().getJson(JsonOptions::none);
931 env(offer(
"alice", CNY(700), JPY(100)),
936 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
937 auto const& t = jv[jss::transaction];
938 return t[jss::TransactionType] == jss::OfferCreate &&
940 JPY(100).value().getJson(JsonOptions::none) &&
942 CNY(700).value().getJson(JsonOptions::none);
948 env(offer(
"alice", JPY(100), CNY(75)),
require(
owners(
"alice", 8)));
952 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
953 auto const& t = jv[jss::transaction];
954 return t[jss::TransactionType] == jss::OfferCreate &&
956 CNY(75).value().getJson(JsonOptions::none) &&
958 JPY(100).value().getJson(JsonOptions::none);
963 auto jv = wsc->invoke(
"unsubscribe", books);
964 BEAST_EXPECT(jv[jss::status] ==
"success");
965 if (wsc->version() == 2)
968 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
970 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
971 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
978 testcase(
"TrackOffers");
985 env.
fund(
XRP(20000), alice, bob, gw);
987 auto USD = gw[
"USD"];
994 j[jss::snapshot] =
true;
995 j[jss::taker_gets][jss::currency] =
"XRP";
996 j[jss::taker_pays][jss::currency] =
"USD";
997 j[jss::taker_pays][jss::issuer] = gw.human();
1000 auto jv = wsc->invoke(
"subscribe", books);
1001 if (wsc->version() == 2)
1004 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1006 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1007 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1009 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1012 jv[jss::result].isMember(jss::offers) &&
1013 jv[jss::result][jss::offers].size() == 0);
1014 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
1015 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
1020 env.
trust(USD(1000), alice);
1021 env.
trust(USD(1000), bob);
1022 env(
pay(gw, alice, USD(100)));
1023 env(
pay(gw, bob, USD(50)));
1024 env(offer(alice,
XRP(4000), USD(10)));
1029 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1030 jvParams[jss::ledger_index] =
"validated";
1031 jvParams[jss::taker_gets][jss::currency] =
"USD";
1032 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1034 auto jv = wsc->invoke(
"book_offers", jvParams);
1035 if (wsc->version() == 2)
1038 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1040 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1041 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1043 auto jrr = jv[jss::result];
1045 BEAST_EXPECT(jrr[jss::offers].isArray());
1046 BEAST_EXPECT(jrr[jss::offers].size() == 1);
1047 auto const jrOffer = jrr[jss::offers][0u];
1053 BEAST_EXPECT(jrOffer[jss::Flags] == 0);
1058 jrOffer[jss::TakerGets] ==
1061 jrOffer[jss::TakerPays] ==
1063 BEAST_EXPECT(jrOffer[jss::owner_funds] ==
"100");
1064 BEAST_EXPECT(jrOffer[jss::quality] ==
"400000000");
1066 using namespace std::chrono_literals;
1067 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jval) {
1068 auto const& t = jval[jss::transaction];
1069 return t[jss::TransactionType] == jss::OfferCreate &&
1070 t[jss::TakerGets] ==
1071 USD(10).value().getJson(JsonOptions::none) &&
1072 t[jss::owner_funds] ==
"100" &&
1073 t[jss::TakerPays] ==
1074 XRP(4000).value().getJson(JsonOptions::none);
1077 env(offer(bob,
XRP(2000), USD(5)));
1080 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jval) {
1081 auto const& t = jval[jss::transaction];
1082 return t[jss::TransactionType] == jss::OfferCreate &&
1083 t[jss::TakerGets] ==
1084 USD(5).value().getJson(JsonOptions::none) &&
1085 t[jss::owner_funds] ==
"50" &&
1086 t[jss::TakerPays] ==
1087 XRP(2000).value().getJson(JsonOptions::none);
1090 jv = wsc->invoke(
"book_offers", jvParams);
1091 if (wsc->version() == 2)
1094 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1096 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1097 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1099 jrr = jv[jss::result];
1101 BEAST_EXPECT(jrr[jss::offers].isArray());
1102 BEAST_EXPECT(jrr[jss::offers].size() == 2);
1103 auto const jrNextOffer = jrr[jss::offers][1u];
1109 BEAST_EXPECT(jrNextOffer[jss::Flags] == 0);
1114 jrNextOffer[jss::TakerGets] ==
1117 jrNextOffer[jss::TakerPays] ==
1119 BEAST_EXPECT(jrNextOffer[jss::owner_funds] ==
"50");
1120 BEAST_EXPECT(jrNextOffer[jss::quality] ==
"400000000");
1122 jv = wsc->invoke(
"unsubscribe", books);
1123 if (wsc->version() == 2)
1126 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1128 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1129 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1131 BEAST_EXPECT(jv[jss::status] ==
"success");
1142 auto maybeJv = wsc->getMsg(timeout);
1147 if (!(*maybeJv).isMember(jss::transaction))
1149 auto const& t = (*maybeJv)[jss::transaction];
1150 if (t[jss::TransactionType] != jss::OfferCreate ||
1155 return wsc->getMsg(timeout) == std::nullopt;
1161 testcase(
"Crossing single book offer");
1167 using namespace jtx;
1174 auto const gw =
Account(
"gateway");
1175 auto const alice =
Account(
"alice");
1176 auto const bob =
Account(
"bob");
1177 auto const charlie =
Account(
"charlie");
1178 auto const USD = gw[
"USD"];
1180 env.
fund(
XRP(1000000), gw, alice, bob, charlie);
1183 env(
trust(alice, USD(500)));
1184 env(
trust(bob, USD(500)));
1187 env(
pay(gw, alice, USD(500)));
1188 env(
pay(gw, bob, USD(500)));
1192 env(offer(alice,
XRP(500), USD(500)));
1193 env(offer(bob,
XRP(500), USD(500)));
1203 j[jss::snapshot] =
false;
1204 j[jss::taker_gets][jss::currency] =
"XRP";
1205 j[jss::taker_pays][jss::currency] =
"USD";
1206 j[jss::taker_pays][jss::issuer] = gw.human();
1209 auto jv = wsc->invoke(
"subscribe", books);
1210 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1215 env(offer(charlie, USD(1000),
XRP(1000)));
1218 using namespace std::chrono_literals;
1222 auto jv = wsc->invoke(
"unsubscribe", books);
1223 BEAST_EXPECT(jv[jss::status] ==
"success");
1229 testcase(
"Crossing multi-book offer");
1235 using namespace jtx;
1244 auto const gw =
Account(
"gateway");
1245 auto const alice =
Account(
"alice");
1246 auto const bob =
Account(
"bob");
1247 auto const charlie =
Account(
"charlie");
1248 auto const USD = gw[
"USD"];
1249 auto const EUR = gw[
"EUR"];
1251 env.
fund(
XRP(1000000), gw, alice, bob, charlie);
1254 for (
auto const& account : {alice, bob, charlie})
1256 for (
auto const& iou : {USD, EUR})
1258 env(
trust(account, iou(1)));
1263 env(
pay(gw, alice, USD(1)));
1264 env(
pay(gw, charlie, EUR(1)));
1267 env(offer(alice,
XRP(100), USD(1)));
1268 env(offer(bob, EUR(1),
XRP(100)));
1279 j[jss::snapshot] =
false;
1280 j[jss::taker_gets][jss::currency] =
"XRP";
1281 j[jss::taker_pays][jss::currency] =
"USD";
1282 j[jss::taker_pays][jss::issuer] = gw.human();
1287 j[jss::snapshot] =
false;
1288 j[jss::taker_gets][jss::currency] =
"EUR";
1289 j[jss::taker_gets][jss::issuer] = gw.human();
1290 j[jss::taker_pays][jss::currency] =
"XRP";
1293 auto jv = wsc->invoke(
"subscribe", books);
1294 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1299 env(offer(charlie, USD(1), EUR(1)));
1301 using namespace std::chrono_literals;
1305 auto jv = wsc->invoke(
"unsubscribe", books);
1306 BEAST_EXPECT(jv[jss::status] ==
"success");
1312 testcase(
"BookOffersRPC Errors");
1313 using namespace jtx;
1317 env.
fund(
XRP(10000), alice, gw);
1319 auto USD = gw[
"USD"];
1323 jvParams[jss::ledger_index] = 10u;
1324 auto const jrr = env.
rpc(
1325 "json",
"book_offers",
to_string(jvParams))[jss::result];
1326 BEAST_EXPECT(jrr[jss::error] ==
"lgrNotFound");
1327 BEAST_EXPECT(jrr[jss::error_message] ==
"ledgerNotFound");
1332 jvParams[jss::ledger_index] =
"validated";
1333 auto const jrr = env.
rpc(
1334 "json",
"book_offers",
to_string(jvParams))[jss::result];
1335 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1337 jrr[jss::error_message] ==
"Missing field 'taker_pays'.");
1342 jvParams[jss::ledger_index] =
"validated";
1344 auto const jrr = env.
rpc(
1345 "json",
"book_offers",
to_string(jvParams))[jss::result];
1346 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1348 jrr[jss::error_message] ==
"Missing field 'taker_gets'.");
1353 jvParams[jss::ledger_index] =
"validated";
1354 jvParams[jss::taker_pays] =
"not an object";
1356 auto const jrr = env.
rpc(
1357 "json",
"book_offers",
to_string(jvParams))[jss::result];
1358 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1360 jrr[jss::error_message] ==
1361 "Invalid field 'taker_pays', not object.");
1366 jvParams[jss::ledger_index] =
"validated";
1368 jvParams[jss::taker_gets] =
"not an object";
1369 auto const jrr = env.
rpc(
1370 "json",
"book_offers",
to_string(jvParams))[jss::result];
1371 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1373 jrr[jss::error_message] ==
1374 "Invalid field 'taker_gets', not object.");
1379 jvParams[jss::ledger_index] =
"validated";
1382 auto const jrr = env.
rpc(
1383 "json",
"book_offers",
to_string(jvParams))[jss::result];
1384 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1386 jrr[jss::error_message] ==
1387 "Missing field 'taker_pays.currency'.");
1392 jvParams[jss::ledger_index] =
"validated";
1393 jvParams[jss::taker_pays][jss::currency] = 1;
1395 auto const jrr = env.
rpc(
1396 "json",
"book_offers",
to_string(jvParams))[jss::result];
1397 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1399 jrr[jss::error_message] ==
1400 "Invalid field 'taker_pays.currency', not string.");
1405 jvParams[jss::ledger_index] =
"validated";
1406 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1408 auto const jrr = env.
rpc(
1409 "json",
"book_offers",
to_string(jvParams))[jss::result];
1410 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1412 jrr[jss::error_message] ==
1413 "Missing field 'taker_gets.currency'.");
1418 jvParams[jss::ledger_index] =
"validated";
1419 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1420 jvParams[jss::taker_gets][jss::currency] = 1;
1421 auto const jrr = env.
rpc(
1422 "json",
"book_offers",
to_string(jvParams))[jss::result];
1423 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1425 jrr[jss::error_message] ==
1426 "Invalid field 'taker_gets.currency', not string.");
1431 jvParams[jss::ledger_index] =
"validated";
1432 jvParams[jss::taker_pays][jss::currency] =
"NOT_VALID";
1433 jvParams[jss::taker_gets][jss::currency] =
"XRP";
1434 auto const jrr = env.
rpc(
1435 "json",
"book_offers",
to_string(jvParams))[jss::result];
1436 BEAST_EXPECT(jrr[jss::error] ==
"srcCurMalformed");
1438 jrr[jss::error_message] ==
1439 "Invalid field 'taker_pays.currency', bad currency.");
1444 jvParams[jss::ledger_index] =
"validated";
1445 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1446 jvParams[jss::taker_gets][jss::currency] =
"NOT_VALID";
1447 auto const jrr = env.
rpc(
1448 "json",
"book_offers",
to_string(jvParams))[jss::result];
1449 BEAST_EXPECT(jrr[jss::error] ==
"dstAmtMalformed");
1451 jrr[jss::error_message] ==
1452 "Invalid field 'taker_gets.currency', bad currency.");
1457 jvParams[jss::ledger_index] =
"validated";
1458 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1459 jvParams[jss::taker_gets][jss::currency] =
"USD";
1460 jvParams[jss::taker_gets][jss::issuer] = 1;
1461 auto const jrr = env.
rpc(
1462 "json",
"book_offers",
to_string(jvParams))[jss::result];
1463 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1465 jrr[jss::error_message] ==
1466 "Invalid field 'taker_gets.issuer', not string.");
1471 jvParams[jss::ledger_index] =
"validated";
1472 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1473 jvParams[jss::taker_pays][jss::issuer] = 1;
1474 jvParams[jss::taker_gets][jss::currency] =
"USD";
1475 auto const jrr = env.
rpc(
1476 "json",
"book_offers",
to_string(jvParams))[jss::result];
1477 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1479 jrr[jss::error_message] ==
1480 "Invalid field 'taker_pays.issuer', not string.");
1485 jvParams[jss::ledger_index] =
"validated";
1486 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1487 jvParams[jss::taker_pays][jss::issuer] = gw.human() +
"DEAD";
1488 jvParams[jss::taker_gets][jss::currency] =
"USD";
1489 auto const jrr = env.
rpc(
1490 "json",
"book_offers",
to_string(jvParams))[jss::result];
1491 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1493 jrr[jss::error_message] ==
1494 "Invalid field 'taker_pays.issuer', bad issuer.");
1499 jvParams[jss::ledger_index] =
"validated";
1500 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1502 jvParams[jss::taker_gets][jss::currency] =
"USD";
1503 auto const jrr = env.
rpc(
1504 "json",
"book_offers",
to_string(jvParams))[jss::result];
1505 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1507 jrr[jss::error_message] ==
1508 "Invalid field 'taker_pays.issuer', bad issuer account one.");
1513 jvParams[jss::ledger_index] =
"validated";
1514 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1515 jvParams[jss::taker_gets][jss::currency] =
"USD";
1516 jvParams[jss::taker_gets][jss::issuer] = gw.human() +
"DEAD";
1517 auto const jrr = env.
rpc(
1518 "json",
"book_offers",
to_string(jvParams))[jss::result];
1519 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1521 jrr[jss::error_message] ==
1522 "Invalid field 'taker_gets.issuer', bad issuer.");
1527 jvParams[jss::ledger_index] =
"validated";
1528 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1529 jvParams[jss::taker_gets][jss::currency] =
"USD";
1531 auto const jrr = env.
rpc(
1532 "json",
"book_offers",
to_string(jvParams))[jss::result];
1533 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1535 jrr[jss::error_message] ==
1536 "Invalid field 'taker_gets.issuer', bad issuer account one.");
1541 jvParams[jss::ledger_index] =
"validated";
1542 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1543 jvParams[jss::taker_pays][jss::issuer] = alice.human();
1544 jvParams[jss::taker_gets][jss::currency] =
"USD";
1545 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1546 auto const jrr = env.
rpc(
1547 "json",
"book_offers",
to_string(jvParams))[jss::result];
1548 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1550 jrr[jss::error_message] ==
1551 "Unneeded field 'taker_pays.issuer' "
1552 "for XRP currency specification.");
1557 jvParams[jss::ledger_index] =
"validated";
1558 jvParams[jss::taker_pays][jss::currency] =
"USD";
1560 jvParams[jss::taker_gets][jss::currency] =
"USD";
1561 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1562 auto const jrr = env.
rpc(
1563 "json",
"book_offers",
to_string(jvParams))[jss::result];
1564 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1566 jrr[jss::error_message] ==
1567 "Invalid field 'taker_pays.issuer', expected non-XRP issuer.");
1572 jvParams[jss::ledger_index] =
"validated";
1573 jvParams[jss::taker] = 1;
1574 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1575 jvParams[jss::taker_gets][jss::currency] =
"USD";
1576 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1577 auto const jrr = env.
rpc(
1578 "json",
"book_offers",
to_string(jvParams))[jss::result];
1579 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1581 jrr[jss::error_message] ==
1582 "Invalid field 'taker', not string.");
1587 jvParams[jss::ledger_index] =
"validated";
1588 jvParams[jss::taker] = env.
master.
human() +
"DEAD";
1589 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1590 jvParams[jss::taker_gets][jss::currency] =
"USD";
1591 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1592 auto const jrr = env.
rpc(
1593 "json",
"book_offers",
to_string(jvParams))[jss::result];
1594 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1595 BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker'.");
1600 jvParams[jss::ledger_index] =
"validated";
1602 jvParams[jss::taker_pays][jss::currency] =
"USD";
1603 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1604 jvParams[jss::taker_gets][jss::currency] =
"USD";
1605 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1606 auto const jrr = env.
rpc(
1607 "json",
"book_offers",
to_string(jvParams))[jss::result];
1608 BEAST_EXPECT(jrr[jss::error] ==
"badMarket");
1609 BEAST_EXPECT(jrr[jss::error_message] ==
"No such market.");
1614 jvParams[jss::ledger_index] =
"validated";
1616 jvParams[jss::limit] =
"0";
1617 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1618 jvParams[jss::taker_gets][jss::currency] =
"USD";
1619 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1620 auto const jrr = env.
rpc(
1621 "json",
"book_offers",
to_string(jvParams))[jss::result];
1622 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1624 jrr[jss::error_message] ==
1625 "Invalid field 'limit', not unsigned integer.");
1630 jvParams[jss::ledger_index] =
"validated";
1631 jvParams[jss::taker_pays][jss::currency] =
"USD";
1632 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1633 jvParams[jss::taker_gets][jss::currency] =
"USD";
1634 auto const jrr = env.
rpc(
1635 "json",
"book_offers",
to_string(jvParams))[jss::result];
1636 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1638 jrr[jss::error_message] ==
1639 "Invalid field 'taker_gets.issuer', "
1640 "expected non-XRP issuer.");
1645 jvParams[jss::ledger_index] =
"validated";
1646 jvParams[jss::taker_pays][jss::currency] =
"USD";
1647 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1648 jvParams[jss::taker_gets][jss::currency] =
"XRP";
1649 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1650 auto const jrr = env.
rpc(
1651 "json",
"book_offers",
to_string(jvParams))[jss::result];
1652 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1654 jrr[jss::error_message] ==
1655 "Unneeded field 'taker_gets.issuer' "
1656 "for XRP currency specification.");
1663 testcase(
"BookOffer Limits");
1664 using namespace jtx;
1667 env.fund(
XRP(200000), gw);
1672 auto USD = gw[
"USD"];
1675 env(offer(gw,
XRP(50 + 1 * i), USD(1.0 + 0.1 * i)));
1681 jvParams[jss::limit] = 1;
1682 jvParams[jss::ledger_index] =
"validated";
1683 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1684 jvParams[jss::taker_gets][jss::currency] =
"USD";
1685 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1687 env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1688 BEAST_EXPECT(jrr[jss::offers].isArray());
1689 BEAST_EXPECT(jrr[jss::offers].size() == (asAdmin ? 1u : 0u));
1692 jvParams[jss::limit] = 0u;
1693 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1694 BEAST_EXPECT(jrr[jss::offers].isArray());
1695 BEAST_EXPECT(jrr[jss::offers].size() == 0u);
1698 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1699 BEAST_EXPECT(jrr[jss::offers].isArray());
1701 jrr[jss::offers].size() ==
1705 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1706 BEAST_EXPECT(jrr[jss::offers].isArray());
1708 jrr[jss::offers].size() ==