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>
38 #include <test/jtx/TestHelpers.h>
39 #include <test/jtx/envconfig.h>
51 jv[jss::command] =
"ripple_path_find";
52 jv[jss::source_account] =
toBase58(src);
66 jv[jss::destination_account] = d;
69 j[jss::currency] =
"USD";
70 j[jss::value] =
"0.01";
88 cfg->PATH_SEARCH_OLD = 7;
90 cfg->PATH_SEARCH_MAX = 10;
106 template <
class Rep,
class Period>
136 auto& app = env.
app();
145 app.getLedgerMaster(),
155 params[jss::command] =
"ripple_path_find";
156 params[jss::source_account] =
toBase58(src);
157 params[jss::destination_account] =
toBase58(dst);
158 params[jss::destination_amount] =
166 j[jss::currency] =
to_string(saSrcCurrency.value());
172 app.getJobQueue().postCoro(
173 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
174 context.params = std::move(params);
180 using namespace std::chrono_literals;
182 BEAST_EXPECT(!result.
isMember(jss::error));
196 env, src, dst, saDstAmount, saSendMax, saSrcCurrency);
197 BEAST_EXPECT(!result.
isMember(jss::error));
200 if (result.
isMember(jss::destination_amount))
205 if (result.
isMember(jss::alternatives))
207 auto const& alts = result[jss::alternatives];
210 auto const&
path = alts[0u];
212 if (
path.isMember(jss::source_amount))
215 if (
path.isMember(jss::destination_amount))
219 if (
path.isMember(jss::paths_computed))
222 p[
"Paths"] =
path[jss::paths_computed];
235 testcase(
"source currency limits");
236 using namespace std::chrono_literals;
239 auto const gw =
Account(
"gateway");
240 env.
fund(
XRP(10000),
"alice",
"bob", gw);
241 env.
trust(gw[
"USD"](100),
"alice",
"bob");
244 auto& app = env.
app();
253 app.getLedgerMaster(),
264 app.getJobQueue().postCoro(
265 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
266 context.params =
rpf(
273 BEAST_EXPECT(!result.
isMember(jss::error));
276 app.getJobQueue().postCoro(
277 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
287 BEAST_EXPECT(result.
isMember(jss::error));
292 app.getJobQueue().postCoro(
293 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
300 BEAST_EXPECT(!result.
isMember(jss::error));
304 app.getJobQueue().postCoro(
305 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
312 BEAST_EXPECT(result.
isMember(jss::error));
318 testcase(
"no direct path no intermediary no alternatives");
321 env.
fund(
XRP(10000),
"alice",
"bob");
325 BEAST_EXPECT(std::get<0>(result).empty());
331 testcase(
"direct path no intermediary");
334 env.
fund(
XRP(10000),
"alice",
"bob");
341 BEAST_EXPECT(st.
empty());
348 testcase(
"payment auto path find");
351 auto const gw =
Account(
"gateway");
352 auto const USD = gw[
"USD"];
353 env.
fund(
XRP(10000),
"alice",
"bob", gw);
354 env.
trust(USD(600),
"alice");
355 env.
trust(USD(700),
"bob");
356 env(pay(gw,
"alice", USD(70)));
357 env(pay(
"alice",
"bob", USD(24)));
367 testcase(
"path find");
370 auto const gw =
Account(
"gateway");
371 auto const USD = gw[
"USD"];
372 env.
fund(
XRP(10000),
"alice",
"bob", gw);
373 env.
trust(USD(600),
"alice");
374 env.
trust(USD(700),
"bob");
375 env(pay(gw,
"alice", USD(70)));
376 env(pay(gw,
"bob", USD(50)));
390 testcase(
"XRP to XRP");
392 env.
fund(
XRP(10000),
"alice",
"bob");
395 BEAST_EXPECT(std::get<0>(result).empty());
401 testcase(
"path find consume all");
406 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
417 env,
"alice",
"edward",
Account(
"edward")[
"USD"](-1));
425 auto const gw =
Account(
"gateway");
426 auto const USD = gw[
"USD"];
427 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
428 env.
trust(USD(100),
"bob",
"carol");
429 env(pay(gw,
"carol", USD(100)));
430 env(offer(
"carol",
XRP(100), USD(100)));
441 BEAST_EXPECT(st.
empty());
448 BEAST_EXPECT(sa ==
XRP(100));
456 testcase(
"alternative path consume both");
459 auto const gw =
Account(
"gateway");
460 auto const USD = gw[
"USD"];
461 auto const gw2 =
Account(
"gateway2");
462 auto const gw2_USD = gw2[
"USD"];
463 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
464 env.
trust(USD(600),
"alice");
465 env.
trust(gw2_USD(800),
"alice");
466 env.
trust(USD(700),
"bob");
467 env.
trust(gw2_USD(900),
"bob");
468 env(pay(gw,
"alice", USD(70)));
469 env(pay(gw2,
"alice", gw2_USD(70)));
470 env(pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
485 testcase(
"alternative paths consume best transfer");
488 auto const gw =
Account(
"gateway");
489 auto const USD = gw[
"USD"];
490 auto const gw2 =
Account(
"gateway2");
491 auto const gw2_USD = gw2[
"USD"];
492 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
494 env.
trust(USD(600),
"alice");
495 env.
trust(gw2_USD(800),
"alice");
496 env.
trust(USD(700),
"bob");
497 env.
trust(gw2_USD(900),
"bob");
498 env(pay(gw,
"alice", USD(70)));
499 env(pay(gw2,
"alice", gw2_USD(70)));
500 env(pay(
"alice",
"bob", USD(70)));
514 testcase(
"alternative paths - consume best transfer first");
517 auto const gw =
Account(
"gateway");
518 auto const USD = gw[
"USD"];
519 auto const gw2 =
Account(
"gateway2");
520 auto const gw2_USD = gw2[
"USD"];
521 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
523 env.
trust(USD(600),
"alice");
524 env.
trust(gw2_USD(800),
"alice");
525 env.
trust(USD(700),
"bob");
526 env.
trust(gw2_USD(900),
"bob");
527 env(pay(gw,
"alice", USD(70)));
528 env(pay(gw2,
"alice", gw2_USD(70)));
529 env(pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
545 testcase(
"alternative paths - limit returned paths to best quality");
548 auto const gw =
Account(
"gateway");
549 auto const USD = gw[
"USD"];
550 auto const gw2 =
Account(
"gateway2");
551 auto const gw2_USD = gw2[
"USD"];
552 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
553 env(
rate(
"carol", 1.1));
554 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
556 env.
trust(USD(800),
"alice",
"bob");
557 env.
trust(gw2_USD(800),
"alice",
"bob");
560 env(pay(gw2,
"alice", gw2_USD(100)));
561 env(pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
562 env(pay(gw,
"alice", USD(100)));
580 testcase(
"path negative: Issue #5");
583 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
584 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
587 env(pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
593 BEAST_EXPECT(std::get<0>(result).empty());
598 BEAST_EXPECT(std::get<0>(result).empty());
618 testcase(
"path negative: ripple-client issue #23: smaller");
621 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
626 env(pay(
"alice",
"bob",
Account(
"bob")[
"USD"](55)),
637 testcase(
"path negative: ripple-client issue #23: larger");
640 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
646 env(pay(
"alice",
"bob",
Account(
"bob")[
"USD"](50)),
664 testcase(
"via gateway");
667 auto const gw =
Account(
"gateway");
668 auto const AUD = gw[
"AUD"];
669 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
671 env.
trust(AUD(100),
"bob",
"carol");
672 env(pay(gw,
"carol", AUD(50)));
673 env(offer(
"carol",
XRP(50), AUD(50)));
680 BEAST_EXPECT(std::get<0>(result).empty());
686 testcase(
"path find");
689 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
704 testcase(
"quality set and test");
707 env.
fund(
XRP(10000),
"alice",
"bob");
708 env(trust(
"bob",
Account(
"alice")[
"USD"](1000)),
717 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
723 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
727 "HighQualityIn" : 2000,
728 "HighQualityOut" : 1400000000,
729 "LedgerEntryType" : "RippleState",
732 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
743 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
744 BEAST_EXPECT(*it == jv_l[it.memberName()]);
750 testcase(
"trust normal clear");
753 env.
fund(
XRP(10000),
"alice",
"bob");
762 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
768 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
772 "LedgerEntryType" : "RippleState",
775 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
787 BEAST_EXPECT(*it == jv_l[it.memberName()]);
800 testcase(
"trust auto clear");
803 env.
fund(
XRP(10000),
"alice",
"bob");
805 env(pay(
"bob",
"alice",
Account(
"bob")[
"USD"](50)));
814 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
821 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
825 "LedgerEntryType" : "RippleState",
829 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
840 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
841 BEAST_EXPECT(*it == jv_l[it.memberName()]);
843 env(pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
853 testcase(
"Path Find: XRP -> XRP and XRP -> IOU");
864 env.fund(
XRP(100000), A1);
865 env.fund(
XRP(10000), A2);
866 env.fund(
XRP(1000), A3, G1, G2, G3, M1);
869 env.trust(G1[
"XYZ"](5000), A1);
870 env.trust(G3[
"ABC"](5000), A1);
871 env.trust(G2[
"XYZ"](5000), A2);
872 env.trust(G3[
"ABC"](5000), A2);
873 env.trust(A2[
"ABC"](1000), A3);
874 env.trust(G1[
"XYZ"](100000), M1);
875 env.trust(G2[
"XYZ"](100000), M1);
876 env.trust(G3[
"ABC"](100000), M1);
879 env(pay(G1, A1, G1[
"XYZ"](3500)));
880 env(pay(G3, A1, G3[
"ABC"](1200)));
881 env(pay(G2, M1, G2[
"XYZ"](25000)));
882 env(pay(G3, M1, G3[
"ABC"](25000)));
885 env(offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)));
886 env(offer(M1,
XRP(10000), G3[
"ABC"](1000)));
892 auto const& send_amt =
XRP(10);
895 BEAST_EXPECT(
equal(da, send_amt));
896 BEAST_EXPECT(st.
empty());
902 auto const& send_amt =
XRP(200);
905 BEAST_EXPECT(
equal(da, send_amt));
906 BEAST_EXPECT(st.
empty());
910 auto const& send_amt = G3[
"ABC"](10);
913 BEAST_EXPECT(
equal(da, send_amt));
919 auto const& send_amt = A2[
"ABC"](1);
922 BEAST_EXPECT(
equal(da, send_amt));
928 auto const& send_amt = A3[
"ABC"](1);
931 BEAST_EXPECT(
equal(da, send_amt));
932 BEAST_EXPECT(
equal(sa, XRP(10)));
933 BEAST_EXPECT(same(st, stpath(IPE(G3[
"ABC"]), G3, A2)));
940 testcase(
"Path Find: non-XRP -> XRP");
948 env.fund(
XRP(1000), A1, A2, G3);
949 env.fund(
XRP(11000), M1);
952 env.trust(G3[
"ABC"](1000), A1, A2);
953 env.trust(G3[
"ABC"](100000), M1);
956 env(
pay(G3, A1, G3[
"ABC"](1000)));
957 env(
pay(G3, A2, G3[
"ABC"](1000)));
958 env(
pay(G3, M1, G3[
"ABC"](1200)));
961 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)));
966 auto const& send_amt =
XRP(10);
968 find_paths(env, A1, A2, send_amt, std::nullopt, A2[
"ABC"].currency);
969 BEAST_EXPECT(
equal(da, send_amt));
970 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
977 testcase(
"Path Find: Bitstamp and SnapSwap, liquidity with no offers");
982 Account G1BS{
"G1BS"};
983 Account G2SW{
"G2SW"};
986 env.fund(
XRP(1000), G1BS, G2SW, A1, A2);
987 env.fund(
XRP(11000), M1);
990 env.trust(G1BS[
"HKD"](2000), A1);
991 env.trust(G2SW[
"HKD"](2000), A2);
992 env.trust(G1BS[
"HKD"](100000), M1);
993 env.trust(G2SW[
"HKD"](100000), M1);
996 env(pay(G1BS, A1, G1BS[
"HKD"](1000)));
997 env(pay(G2SW, A2, G2SW[
"HKD"](1000)));
1001 env(pay(G1BS, M1, G1BS[
"HKD"](1200)));
1002 env(pay(G2SW, M1, G2SW[
"HKD"](5000)));
1009 auto const& send_amt = A2[
"HKD"](10);
1011 env, A1, A2, send_amt, std::nullopt, A2[
"HKD"].currency);
1012 BEAST_EXPECT(
equal(da, send_amt));
1013 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1014 BEAST_EXPECT(
same(st,
stpath(G1BS, M1, G2SW)));
1018 auto const& send_amt = A1[
"HKD"](10);
1020 env, A2, A1, send_amt, std::nullopt, A1[
"HKD"].currency);
1021 BEAST_EXPECT(
equal(da, send_amt));
1022 BEAST_EXPECT(
equal(sa, A2[
"HKD"](10)));
1023 BEAST_EXPECT(
same(st,
stpath(G2SW, M1, G1BS)));
1027 auto const& send_amt = A2[
"HKD"](10);
1029 env, G1BS, A2, send_amt, std::nullopt, A1[
"HKD"].currency);
1030 BEAST_EXPECT(
equal(da, send_amt));
1031 BEAST_EXPECT(
equal(sa, G1BS[
"HKD"](10)));
1032 BEAST_EXPECT(same(st, stpath(M1, G2SW)));
1036 auto const& send_amt = M1[
"HKD"](10);
1038 env, M1, G1BS, send_amt, std::nullopt, A1[
"HKD"].currency);
1039 BEAST_EXPECT(
equal(da, send_amt));
1040 BEAST_EXPECT(
equal(sa, M1[
"HKD"](10)));
1041 BEAST_EXPECT(st.
empty());
1045 auto const& send_amt = A1[
"HKD"](10);
1047 env, G2SW, A1, send_amt, std::nullopt, A1[
"HKD"].currency);
1048 BEAST_EXPECT(
equal(da, send_amt));
1049 BEAST_EXPECT(
equal(sa, G2SW[
"HKD"](10)));
1057 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1058 using namespace jtx;
1071 env.fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1072 env.fund(
XRP(10000), A4);
1073 env.fund(
XRP(11000), M1, M2);
1076 env.trust(G1[
"HKD"](2000), A1);
1077 env.trust(G2[
"HKD"](2000), A2);
1078 env.trust(G1[
"HKD"](2000), A3);
1079 env.trust(G1[
"HKD"](100000), M1);
1080 env.trust(G2[
"HKD"](100000), M1);
1081 env.trust(G1[
"HKD"](100000), M2);
1082 env.trust(G2[
"HKD"](100000), M2);
1085 env(
pay(G1, A1, G1[
"HKD"](1000)));
1086 env(
pay(G2, A2, G2[
"HKD"](1000)));
1087 env(
pay(G1, A3, G1[
"HKD"](1000)));
1088 env(
pay(G1, M1, G1[
"HKD"](1200)));
1089 env(
pay(G2, M1, G2[
"HKD"](5000)));
1090 env(
pay(G1, M2, G1[
"HKD"](1200)));
1091 env(
pay(G2, M2, G2[
"HKD"](5000)));
1094 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1095 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1096 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1104 auto const& send_amt = G1[
"HKD"](10);
1106 env, A1, G1, send_amt, std::nullopt, G1[
"HKD"].currency);
1107 BEAST_EXPECT(st.empty());
1108 BEAST_EXPECT(
equal(da, send_amt));
1109 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1115 auto const& send_amt = A1[
"HKD"](10);
1117 env, A1, G1, send_amt, std::nullopt, G1[
"HKD"].currency);
1118 BEAST_EXPECT(st.empty());
1120 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1126 auto const& send_amt = A3[
"HKD"](10);
1128 env, A1, A3, send_amt, std::nullopt, G1[
"HKD"].currency);
1129 BEAST_EXPECT(
equal(da, send_amt));
1130 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1137 auto const& send_amt = G2[
"HKD"](10);
1139 env, G1, G2, send_amt, std::nullopt, G1[
"HKD"].currency);
1140 BEAST_EXPECT(
equal(da, send_amt));
1141 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1144 stpath(IPE(G2[
"HKD"])),
1147 stpath(IPE(
xrpIssue()), IPE(G2[
"HKD"]))));
1153 auto const& send_amt = G2[
"HKD"](10);
1155 env, A1, G2, send_amt, std::nullopt, G1[
"HKD"].currency);
1156 BEAST_EXPECT(
equal(da, send_amt));
1157 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1162 stpath(G1, IPE(G2[
"HKD"])),
1169 auto const& send_amt = A2[
"HKD"](10);
1171 env, A1, A2, send_amt, std::nullopt, G1[
"HKD"].currency);
1172 BEAST_EXPECT(
equal(da, send_amt));
1173 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1186 testcase(
"Path Find: non-XRP -> non-XRP, same currency)");
1187 using namespace jtx;
1196 env.fund(
XRP(11000), M1);
1197 env.fund(
XRP(1000), A1, A2, A3, G1, G2);
1200 env.trust(G1[
"HKD"](2000), A1);
1201 env.trust(G2[
"HKD"](2000), A2);
1202 env.trust(A2[
"HKD"](2000), A3);
1203 env.trust(G1[
"HKD"](100000), M1);
1204 env.trust(G2[
"HKD"](100000), M1);
1207 env(
pay(G1, A1, G1[
"HKD"](1000)));
1208 env(
pay(G2, A2, G2[
"HKD"](1000)));
1209 env(
pay(G1, M1, G1[
"HKD"](5000)));
1210 env(
pay(G2, M1, G2[
"HKD"](5000)));
1213 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1217 auto const& send_amt = A2[
"HKD"](10);
1221 find_paths(env, G1, A2, send_amt, std::nullopt, G1[
"HKD"].currency);
1223 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1230 testcase(
"Receive max");
1231 using namespace jtx;
1232 auto const alice =
Account(
"alice");
1233 auto const bob =
Account(
"bob");
1234 auto const charlie =
Account(
"charlie");
1235 auto const gw =
Account(
"gw");
1236 auto const USD = gw[
"USD"];
1240 env.
fund(
XRP(10000), alice, bob, charlie, gw);
1242 env.
trust(USD(100), alice, bob, charlie);
1244 env(pay(gw, charlie, USD(10)));
1246 env(offer(charlie,
XRP(10), USD(10)));
1250 BEAST_EXPECT(sa ==
XRP(10));
1251 BEAST_EXPECT(
equal(da, USD(10)));
1252 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1254 auto const& pathElem = st[0][0];
1256 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
1257 pathElem.getCurrency() == USD.currency);
1263 env.fund(XRP(10000), alice, bob, charlie, gw);
1265 env.trust(USD(100), alice, bob, charlie);
1267 env(pay(gw, alice, USD(10)));
1269 env(offer(charlie, USD(10), XRP(10)));
1272 find_paths(env, alice, bob, drops(-1), USD(100).value());
1273 BEAST_EXPECT(sa == USD(10));
1274 BEAST_EXPECT(
equal(da, XRP(10)));
1275 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1277 auto const& pathElem = st[0][0];
1279 pathElem.isOffer() &&
1289 using namespace jtx;
1294 auto const alice =
Account(
"alice");
1295 auto const bob =
Account(
"bob");
1296 auto const george =
Account(
"george");
1297 auto const USD = george[
"USD"];
1324 env(pay(george, alice, USD(70)));
1329 BEAST_EXPECT(
equal(da, bob[
"USD"](5)));
1333 BEAST_EXPECT(st.size() == 1);
1335 BEAST_EXPECT(
equal(sa, alice[
"USD"](5)));
1339 BEAST_EXPECT(st.size() == 0);
1343 test(
"ripple -> ripple",
true,
true,
true);
1344 test(
"ripple -> no ripple",
true,
false,
true);
1345 test(
"no ripple -> ripple",
false,
true,
true);
1346 test(
"no ripple -> no ripple",
false,
false,
false);