20 #include <ripple/app/paths/AccountCurrencies.h>
21 #include <ripple/basics/contract.h>
22 #include <ripple/beast/unit_test.h>
23 #include <ripple/core/JobQueue.h>
24 #include <ripple/json/json_reader.h>
25 #include <ripple/json/to_string.h>
26 #include <ripple/protocol/STParsedJSON.h>
27 #include <ripple/protocol/TxFlags.h>
28 #include <ripple/protocol/jss.h>
29 #include <ripple/resource/Fees.h>
30 #include <ripple/rpc/Context.h>
31 #include <ripple/rpc/RPCHandler.h>
32 #include <ripple/rpc/impl/RPCHelpers.h>
33 #include <ripple/rpc/impl/Tuning.h>
78 template <
class T,
class... Args>
83 if constexpr (
sizeof...(args) > 0)
87 template <
class... Args>
92 if constexpr (
sizeof...(args) > 0)
98 template <
class... Args>
107 template <
class... Args>
116 for (
auto const& p : st2)
134 jv[jss::command] =
"ripple_path_find";
135 jv[jss::source_account] =
toBase58(src);
149 jv[jss::destination_account] = d;
152 j[jss::currency] =
"USD";
153 j[jss::value] =
"0.01";
185 template <
class Rep,
class Period>
210 boost::optional<STAmount>
const& saSendMax = boost::none,
211 boost::optional<Currency>
const& saSrcCurrency = boost::none)
215 auto& app = env.
app();
224 app.getLedgerMaster(),
234 params[jss::command] =
"ripple_path_find";
235 params[jss::source_account] =
toBase58(src);
236 params[jss::destination_account] =
toBase58(dst);
237 params[jss::destination_amount] =
245 j[jss::currency] =
to_string(saSrcCurrency.value());
251 app.getJobQueue().postCoro(
252 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
253 context.params = std::move(params);
259 using namespace std::chrono_literals;
261 BEAST_EXPECT(!result.
isMember(jss::error));
271 boost::optional<STAmount>
const& saSendMax = boost::none,
272 boost::optional<Currency>
const& saSrcCurrency = boost::none)
275 env, src, dst, saDstAmount, saSendMax, saSrcCurrency);
276 BEAST_EXPECT(!result.
isMember(jss::error));
279 if (result.
isMember(jss::destination_amount))
284 if (result.
isMember(jss::alternatives))
286 auto const& alts = result[jss::alternatives];
289 auto const&
path = alts[0u];
291 if (
path.isMember(jss::source_amount))
294 if (
path.isMember(jss::destination_amount))
298 if (
path.isMember(jss::paths_computed))
301 p[
"Paths"] =
path[jss::paths_computed];
314 testcase(
"source currency limits");
315 using namespace std::chrono_literals;
318 auto const gw =
Account(
"gateway");
319 env.
fund(
XRP(10000),
"alice",
"bob", gw);
320 env.
trust(gw[
"USD"](100),
"alice",
"bob");
323 auto& app = env.
app();
332 app.getLedgerMaster(),
343 app.getJobQueue().postCoro(
344 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
345 context.params =
rpf(
352 BEAST_EXPECT(!result.
isMember(jss::error));
355 app.getJobQueue().postCoro(
356 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
366 BEAST_EXPECT(result.
isMember(jss::error));
371 app.getJobQueue().postCoro(
372 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
379 BEAST_EXPECT(!result.
isMember(jss::error));
383 app.getJobQueue().postCoro(
384 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
391 BEAST_EXPECT(result.
isMember(jss::error));
397 testcase(
"no direct path no intermediary no alternatives");
400 env.
fund(
XRP(10000),
"alice",
"bob");
404 BEAST_EXPECT(std::get<0>(result).empty());
410 testcase(
"direct path no intermediary");
413 env.
fund(
XRP(10000),
"alice",
"bob");
420 BEAST_EXPECT(st.
empty());
427 testcase(
"payment auto path find");
430 auto const gw =
Account(
"gateway");
431 auto const USD = gw[
"USD"];
432 env.
fund(
XRP(10000),
"alice",
"bob", gw);
433 env.
trust(USD(600),
"alice");
434 env.
trust(USD(700),
"bob");
435 env(
pay(gw,
"alice", USD(70)));
436 env(
pay(
"alice",
"bob", USD(24)));
446 testcase(
"path find");
449 auto const gw =
Account(
"gateway");
450 auto const USD = gw[
"USD"];
451 env.
fund(
XRP(10000),
"alice",
"bob", gw);
452 env.
trust(USD(600),
"alice");
453 env.
trust(USD(700),
"bob");
454 env(
pay(gw,
"alice", USD(70)));
455 env(
pay(gw,
"bob", USD(50)));
469 testcase(
"XRP to XRP");
471 env.
fund(
XRP(10000),
"alice",
"bob");
474 BEAST_EXPECT(std::get<0>(result).empty());
480 testcase(
"path find consume all");
485 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
496 env,
"alice",
"edward",
Account(
"edward")[
"USD"](-1));
504 auto const gw =
Account(
"gateway");
505 auto const USD = gw[
"USD"];
506 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
507 env.
trust(USD(100),
"bob",
"carol");
508 env(
pay(gw,
"carol", USD(100)));
509 env(offer(
"carol",
XRP(100), USD(100)));
519 boost::optional<STAmount>(
XRP(100000000)));
520 BEAST_EXPECT(st.
empty());
526 boost::optional<STAmount>(
XRP(100000000)));
527 BEAST_EXPECT(sa ==
XRP(100));
535 testcase(
"alternative path consume both");
538 auto const gw =
Account(
"gateway");
539 auto const USD = gw[
"USD"];
540 auto const gw2 =
Account(
"gateway2");
541 auto const gw2_USD = gw2[
"USD"];
542 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
543 env.
trust(USD(600),
"alice");
544 env.
trust(gw2_USD(800),
"alice");
545 env.
trust(USD(700),
"bob");
546 env.
trust(gw2_USD(900),
"bob");
547 env(
pay(gw,
"alice", USD(70)));
548 env(
pay(gw2,
"alice", gw2_USD(70)));
549 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
564 testcase(
"alternative paths consume best transfer");
567 auto const gw =
Account(
"gateway");
568 auto const USD = gw[
"USD"];
569 auto const gw2 =
Account(
"gateway2");
570 auto const gw2_USD = gw2[
"USD"];
571 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
573 env.
trust(USD(600),
"alice");
574 env.
trust(gw2_USD(800),
"alice");
575 env.
trust(USD(700),
"bob");
576 env.
trust(gw2_USD(900),
"bob");
577 env(
pay(gw,
"alice", USD(70)));
578 env(
pay(gw2,
"alice", gw2_USD(70)));
579 env(
pay(
"alice",
"bob", USD(70)));
593 testcase(
"alternative paths - consume best transfer first");
596 auto const gw =
Account(
"gateway");
597 auto const USD = gw[
"USD"];
598 auto const gw2 =
Account(
"gateway2");
599 auto const gw2_USD = gw2[
"USD"];
600 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
602 env.
trust(USD(600),
"alice");
603 env.
trust(gw2_USD(800),
"alice");
604 env.
trust(USD(700),
"bob");
605 env.
trust(gw2_USD(900),
"bob");
606 env(
pay(gw,
"alice", USD(70)));
607 env(
pay(gw2,
"alice", gw2_USD(70)));
608 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
624 testcase(
"alternative paths - limit returned paths to best quality");
627 auto const gw =
Account(
"gateway");
628 auto const USD = gw[
"USD"];
629 auto const gw2 =
Account(
"gateway2");
630 auto const gw2_USD = gw2[
"USD"];
631 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
632 env(
rate(
"carol", 1.1));
633 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
635 env.
trust(USD(800),
"alice",
"bob");
636 env.
trust(gw2_USD(800),
"alice",
"bob");
639 env(
pay(gw2,
"alice", gw2_USD(100)));
640 env(
pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
641 env(
pay(gw,
"alice", USD(100)));
659 testcase(
"path negative: Issue #5");
662 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
663 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
666 env(
pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
672 BEAST_EXPECT(std::get<0>(result).empty());
677 BEAST_EXPECT(std::get<0>(result).empty());
697 testcase(
"path negative: ripple-client issue #23: smaller");
700 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
705 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](55)),
716 testcase(
"path negative: ripple-client issue #23: larger");
719 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
725 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](50)),
743 testcase(
"via gateway");
746 auto const gw =
Account(
"gateway");
747 auto const AUD = gw[
"AUD"];
748 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
750 env.
trust(AUD(100),
"bob",
"carol");
751 env(
pay(gw,
"carol", AUD(50)));
752 env(offer(
"carol",
XRP(50), AUD(50)));
759 BEAST_EXPECT(std::get<0>(result).empty());
765 testcase(
"path find");
768 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
783 testcase(
"quality set and test");
786 env.
fund(
XRP(10000),
"alice",
"bob");
796 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
802 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
806 "HighQualityIn" : 2000,
807 "HighQualityOut" : 1400000000,
808 "LedgerEntryType" : "RippleState",
811 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
822 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
823 BEAST_EXPECT(*it == jv_l[it.memberName()]);
829 testcase(
"trust normal clear");
832 env.
fund(
XRP(10000),
"alice",
"bob");
841 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
847 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
851 "LedgerEntryType" : "RippleState",
854 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
866 BEAST_EXPECT(*it == jv_l[it.memberName()]);
879 testcase(
"trust auto clear");
882 env.
fund(
XRP(10000),
"alice",
"bob");
884 env(
pay(
"bob",
"alice",
Account(
"bob")[
"USD"](50)));
893 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
900 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
904 "LedgerEntryType" : "RippleState",
908 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
919 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
920 BEAST_EXPECT(*it == jv_l[it.memberName()]);
922 env(
pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
932 testcase(
"Path Find: XRP -> XRP and XRP -> IOU");
945 env.
fund(
XRP(1000), A3, G1, G2, G3, M1);
948 env.
trust(G1[
"XYZ"](5000), A1);
949 env.
trust(G3[
"ABC"](5000), A1);
950 env.
trust(G2[
"XYZ"](5000), A2);
951 env.
trust(G3[
"ABC"](5000), A2);
953 env.
trust(G1[
"XYZ"](100000), M1);
954 env.
trust(G2[
"XYZ"](100000), M1);
955 env.
trust(G3[
"ABC"](100000), M1);
958 env(
pay(G1, A1, G1[
"XYZ"](3500)));
959 env(
pay(G3, A1, G3[
"ABC"](1200)));
960 env(
pay(G2, M1, G2[
"XYZ"](25000)));
961 env(
pay(G3, M1, G3[
"ABC"](25000)));
964 env(offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)));
965 env(offer(M1,
XRP(10000), G3[
"ABC"](1000)));
971 auto const& send_amt =
XRP(10);
974 BEAST_EXPECT(
equal(da, send_amt));
975 BEAST_EXPECT(st.
empty());
981 auto const& send_amt =
XRP(200);
984 BEAST_EXPECT(
equal(da, send_amt));
985 BEAST_EXPECT(st.
empty());
989 auto const& send_amt = G3[
"ABC"](10);
992 BEAST_EXPECT(
equal(da, send_amt));
998 auto const& send_amt = A2[
"ABC"](1);
1001 BEAST_EXPECT(
equal(da, send_amt));
1007 auto const& send_amt = A3[
"ABC"](1);
1010 BEAST_EXPECT(
equal(da, send_amt));
1011 BEAST_EXPECT(
equal(sa, XRP(10)));
1019 testcase(
"Path Find: non-XRP -> XRP");
1020 using namespace jtx;
1027 env.
fund(
XRP(1000), A1, A2, G3);
1031 env.
trust(G3[
"ABC"](1000), A1, A2);
1032 env.
trust(G3[
"ABC"](100000), M1);
1035 env(
pay(G3, A1, G3[
"ABC"](1000)));
1036 env(
pay(G3, A2, G3[
"ABC"](1000)));
1037 env(
pay(G3, M1, G3[
"ABC"](1200)));
1040 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)));
1045 auto const& send_amt =
XRP(10);
1047 find_paths(env, A1, A2, send_amt, boost::none, A2[
"ABC"].currency);
1048 BEAST_EXPECT(
equal(da, send_amt));
1049 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
1056 testcase(
"Path Find: Bitstamp and SnapSwap, liquidity with no offers");
1057 using namespace jtx;
1061 Account G1BS{
"G1BS"};
1062 Account G2SW{
"G2SW"};
1065 env.
fund(
XRP(1000), G1BS, G2SW, A1, A2);
1070 env.
trust(G2SW[
"HKD"](2000), A2);
1071 env.
trust(G1BS[
"HKD"](100000), M1);
1072 env.
trust(G2SW[
"HKD"](100000), M1);
1075 env(
pay(G1BS, A1, G1BS[
"HKD"](1000)));
1076 env(
pay(G2SW, A2, G2SW[
"HKD"](1000)));
1080 env(
pay(G1BS, M1, G1BS[
"HKD"](1200)));
1081 env(
pay(G2SW, M1, G2SW[
"HKD"](5000)));
1088 auto const& send_amt = A2[
"HKD"](10);
1090 env, A1, A2, send_amt, boost::none, A2[
"HKD"].currency);
1091 BEAST_EXPECT(
equal(da, send_amt));
1092 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1093 BEAST_EXPECT(
same(st,
stpath(G1BS, M1, G2SW)));
1097 auto const& send_amt = A1[
"HKD"](10);
1099 env, A2, A1, send_amt, boost::none, A1[
"HKD"].currency);
1100 BEAST_EXPECT(
equal(da, send_amt));
1101 BEAST_EXPECT(
equal(sa, A2[
"HKD"](10)));
1102 BEAST_EXPECT(
same(st,
stpath(G2SW, M1, G1BS)));
1106 auto const& send_amt = A2[
"HKD"](10);
1108 env, G1BS, A2, send_amt, boost::none, A1[
"HKD"].currency);
1109 BEAST_EXPECT(
equal(da, send_amt));
1110 BEAST_EXPECT(
equal(sa, G1BS[
"HKD"](10)));
1115 auto const& send_amt = M1[
"HKD"](10);
1117 env, M1, G1BS, send_amt, boost::none, A1[
"HKD"].currency);
1118 BEAST_EXPECT(
equal(da, send_amt));
1119 BEAST_EXPECT(
equal(sa, M1[
"HKD"](10)));
1120 BEAST_EXPECT(st.
empty());
1124 auto const& send_amt = A1[
"HKD"](10);
1126 env, G2SW, A1, send_amt, boost::none, A1[
"HKD"].currency);
1127 BEAST_EXPECT(
equal(da, send_amt));
1128 BEAST_EXPECT(
equal(sa, G2SW[
"HKD"](10)));
1136 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1137 using namespace jtx;
1150 env.
fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1155 env.
trust(G1[
"HKD"](2000), A1);
1156 env.
trust(G2[
"HKD"](2000), A2);
1157 env.
trust(G1[
"HKD"](2000), A3);
1158 env.
trust(G1[
"HKD"](100000), M1);
1159 env.
trust(G2[
"HKD"](100000), M1);
1160 env.
trust(G1[
"HKD"](100000), M2);
1161 env.
trust(G2[
"HKD"](100000), M2);
1164 env(
pay(G1, A1, G1[
"HKD"](1000)));
1165 env(
pay(G2, A2, G2[
"HKD"](1000)));
1166 env(
pay(G1, A3, G1[
"HKD"](1000)));
1167 env(
pay(G1, M1, G1[
"HKD"](1200)));
1168 env(
pay(G2, M1, G2[
"HKD"](5000)));
1169 env(
pay(G1, M2, G1[
"HKD"](1200)));
1170 env(
pay(G2, M2, G2[
"HKD"](5000)));
1173 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1174 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1175 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1183 auto const& send_amt = G1[
"HKD"](10);
1185 env, A1, G1, send_amt, boost::none, G1[
"HKD"].currency);
1186 BEAST_EXPECT(st.empty());
1187 BEAST_EXPECT(
equal(da, send_amt));
1188 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1194 auto const& send_amt = A1[
"HKD"](10);
1196 env, A1, G1, send_amt, boost::none, G1[
"HKD"].currency);
1197 BEAST_EXPECT(st.empty());
1199 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1205 auto const& send_amt = A3[
"HKD"](10);
1207 env, A1, A3, send_amt, boost::none, G1[
"HKD"].currency);
1208 BEAST_EXPECT(
equal(da, send_amt));
1209 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1216 auto const& send_amt = G2[
"HKD"](10);
1218 env, G1, G2, send_amt, boost::none, G1[
"HKD"].currency);
1219 BEAST_EXPECT(
equal(da, send_amt));
1220 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1232 auto const& send_amt = G2[
"HKD"](10);
1234 env, A1, G2, send_amt, boost::none, G1[
"HKD"].currency);
1235 BEAST_EXPECT(
equal(da, send_amt));
1236 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1248 auto const& send_amt = A2[
"HKD"](10);
1250 env, A1, A2, send_amt, boost::none, G1[
"HKD"].currency);
1251 BEAST_EXPECT(
equal(da, send_amt));
1252 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1265 testcase(
"Path Find: non-XRP -> non-XRP, same currency)");
1266 using namespace jtx;
1276 env.
fund(
XRP(1000), A1, A2, A3, G1, G2);
1279 env.
trust(G1[
"HKD"](2000), A1);
1280 env.
trust(G2[
"HKD"](2000), A2);
1281 env.
trust(A2[
"HKD"](2000), A3);
1282 env.
trust(G1[
"HKD"](100000), M1);
1283 env.
trust(G2[
"HKD"](100000), M1);
1286 env(
pay(G1, A1, G1[
"HKD"](1000)));
1287 env(
pay(G2, A2, G2[
"HKD"](1000)));
1288 env(
pay(G1, M1, G1[
"HKD"](5000)));
1289 env(
pay(G2, M1, G2[
"HKD"](5000)));
1292 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1296 auto const& send_amt = A2[
"HKD"](10);
1300 find_paths(env, G1, A2, send_amt, boost::none, G1[
"HKD"].currency);
1301 BEAST_EXPECT(
equal(da, send_amt));
1302 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));