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/envconfig.h>
79 template <
class T,
class... Args>
84 if constexpr (
sizeof...(args) > 0)
88 template <
class... Args>
93 if constexpr (
sizeof...(args) > 0)
99 template <
class... Args>
108 template <
class... Args>
117 for (
auto const& p : st2)
135 jv[jss::command] =
"ripple_path_find";
136 jv[jss::source_account] =
toBase58(src);
150 jv[jss::destination_account] = d;
153 j[jss::currency] =
"USD";
154 j[jss::value] =
"0.01";
183 cfg->PATH_SEARCH_OLD = 7;
184 cfg->PATH_SEARCH = 7;
185 cfg->PATH_SEARCH_MAX = 10;
201 template <
class Rep,
class Period>
231 auto& app = env.
app();
240 app.getLedgerMaster(),
250 params[jss::command] =
"ripple_path_find";
251 params[jss::source_account] =
toBase58(src);
252 params[jss::destination_account] =
toBase58(dst);
253 params[jss::destination_amount] =
261 j[jss::currency] =
to_string(saSrcCurrency.value());
267 app.getJobQueue().postCoro(
268 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
269 context.params = std::move(params);
275 using namespace std::chrono_literals;
277 BEAST_EXPECT(!result.
isMember(jss::error));
291 env, src, dst, saDstAmount, saSendMax, saSrcCurrency);
292 BEAST_EXPECT(!result.
isMember(jss::error));
295 if (result.
isMember(jss::destination_amount))
300 if (result.
isMember(jss::alternatives))
302 auto const& alts = result[jss::alternatives];
305 auto const&
path = alts[0u];
307 if (
path.isMember(jss::source_amount))
310 if (
path.isMember(jss::destination_amount))
314 if (
path.isMember(jss::paths_computed))
317 p[
"Paths"] =
path[jss::paths_computed];
330 testcase(
"source currency limits");
331 using namespace std::chrono_literals;
334 auto const gw =
Account(
"gateway");
335 env.
fund(
XRP(10000),
"alice",
"bob", gw);
336 env.
trust(gw[
"USD"](100),
"alice",
"bob");
339 auto& app = env.
app();
348 app.getLedgerMaster(),
359 app.getJobQueue().postCoro(
360 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
361 context.params =
rpf(
368 BEAST_EXPECT(!result.
isMember(jss::error));
371 app.getJobQueue().postCoro(
372 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
382 BEAST_EXPECT(result.
isMember(jss::error));
387 app.getJobQueue().postCoro(
388 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
395 BEAST_EXPECT(!result.
isMember(jss::error));
399 app.getJobQueue().postCoro(
400 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
407 BEAST_EXPECT(result.
isMember(jss::error));
413 testcase(
"no direct path no intermediary no alternatives");
416 env.
fund(
XRP(10000),
"alice",
"bob");
420 BEAST_EXPECT(std::get<0>(result).empty());
426 testcase(
"direct path no intermediary");
429 env.
fund(
XRP(10000),
"alice",
"bob");
436 BEAST_EXPECT(st.
empty());
443 testcase(
"payment auto path find");
446 auto const gw =
Account(
"gateway");
447 auto const USD = gw[
"USD"];
448 env.
fund(
XRP(10000),
"alice",
"bob", gw);
449 env.
trust(USD(600),
"alice");
450 env.
trust(USD(700),
"bob");
451 env(
pay(gw,
"alice", USD(70)));
452 env(
pay(
"alice",
"bob", USD(24)));
462 testcase(
"path find");
465 auto const gw =
Account(
"gateway");
466 auto const USD = gw[
"USD"];
467 env.
fund(
XRP(10000),
"alice",
"bob", gw);
468 env.
trust(USD(600),
"alice");
469 env.
trust(USD(700),
"bob");
470 env(
pay(gw,
"alice", USD(70)));
471 env(
pay(gw,
"bob", USD(50)));
485 testcase(
"XRP to XRP");
487 env.
fund(
XRP(10000),
"alice",
"bob");
490 BEAST_EXPECT(std::get<0>(result).empty());
496 testcase(
"path find consume all");
501 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
512 env,
"alice",
"edward",
Account(
"edward")[
"USD"](-1));
520 auto const gw =
Account(
"gateway");
521 auto const USD = gw[
"USD"];
522 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
523 env.
trust(USD(100),
"bob",
"carol");
524 env(
pay(gw,
"carol", USD(100)));
525 env(offer(
"carol",
XRP(100), USD(100)));
536 BEAST_EXPECT(st.
empty());
543 BEAST_EXPECT(sa ==
XRP(100));
551 testcase(
"alternative path consume both");
554 auto const gw =
Account(
"gateway");
555 auto const USD = gw[
"USD"];
556 auto const gw2 =
Account(
"gateway2");
557 auto const gw2_USD = gw2[
"USD"];
558 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
559 env.
trust(USD(600),
"alice");
560 env.
trust(gw2_USD(800),
"alice");
561 env.
trust(USD(700),
"bob");
562 env.
trust(gw2_USD(900),
"bob");
563 env(
pay(gw,
"alice", USD(70)));
564 env(
pay(gw2,
"alice", gw2_USD(70)));
565 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
580 testcase(
"alternative paths consume best transfer");
583 auto const gw =
Account(
"gateway");
584 auto const USD = gw[
"USD"];
585 auto const gw2 =
Account(
"gateway2");
586 auto const gw2_USD = gw2[
"USD"];
587 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
589 env.
trust(USD(600),
"alice");
590 env.
trust(gw2_USD(800),
"alice");
591 env.
trust(USD(700),
"bob");
592 env.
trust(gw2_USD(900),
"bob");
593 env(
pay(gw,
"alice", USD(70)));
594 env(
pay(gw2,
"alice", gw2_USD(70)));
595 env(
pay(
"alice",
"bob", USD(70)));
609 testcase(
"alternative paths - consume best transfer first");
612 auto const gw =
Account(
"gateway");
613 auto const USD = gw[
"USD"];
614 auto const gw2 =
Account(
"gateway2");
615 auto const gw2_USD = gw2[
"USD"];
616 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
618 env.
trust(USD(600),
"alice");
619 env.
trust(gw2_USD(800),
"alice");
620 env.
trust(USD(700),
"bob");
621 env.
trust(gw2_USD(900),
"bob");
622 env(
pay(gw,
"alice", USD(70)));
623 env(
pay(gw2,
"alice", gw2_USD(70)));
624 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
640 testcase(
"alternative paths - limit returned paths to best quality");
643 auto const gw =
Account(
"gateway");
644 auto const USD = gw[
"USD"];
645 auto const gw2 =
Account(
"gateway2");
646 auto const gw2_USD = gw2[
"USD"];
647 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
648 env(
rate(
"carol", 1.1));
649 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
651 env.
trust(USD(800),
"alice",
"bob");
652 env.
trust(gw2_USD(800),
"alice",
"bob");
655 env(
pay(gw2,
"alice", gw2_USD(100)));
656 env(
pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
657 env(
pay(gw,
"alice", USD(100)));
675 testcase(
"path negative: Issue #5");
678 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
679 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
682 env(
pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
688 BEAST_EXPECT(std::get<0>(result).empty());
693 BEAST_EXPECT(std::get<0>(result).empty());
713 testcase(
"path negative: ripple-client issue #23: smaller");
716 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
721 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](55)),
732 testcase(
"path negative: ripple-client issue #23: larger");
735 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
741 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](50)),
759 testcase(
"via gateway");
762 auto const gw =
Account(
"gateway");
763 auto const AUD = gw[
"AUD"];
764 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
766 env.
trust(AUD(100),
"bob",
"carol");
767 env(
pay(gw,
"carol", AUD(50)));
768 env(offer(
"carol",
XRP(50), AUD(50)));
775 BEAST_EXPECT(std::get<0>(result).empty());
781 testcase(
"path find");
784 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
799 testcase(
"quality set and test");
802 env.
fund(
XRP(10000),
"alice",
"bob");
812 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
818 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
822 "HighQualityIn" : 2000,
823 "HighQualityOut" : 1400000000,
824 "LedgerEntryType" : "RippleState",
827 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
838 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
839 BEAST_EXPECT(*it == jv_l[it.memberName()]);
845 testcase(
"trust normal clear");
848 env.
fund(
XRP(10000),
"alice",
"bob");
857 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
863 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
867 "LedgerEntryType" : "RippleState",
870 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
882 BEAST_EXPECT(*it == jv_l[it.memberName()]);
895 testcase(
"trust auto clear");
898 env.
fund(
XRP(10000),
"alice",
"bob");
900 env(
pay(
"bob",
"alice",
Account(
"bob")[
"USD"](50)));
909 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
916 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
920 "LedgerEntryType" : "RippleState",
924 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
935 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
936 BEAST_EXPECT(*it == jv_l[it.memberName()]);
938 env(
pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
948 testcase(
"Path Find: XRP -> XRP and XRP -> IOU");
959 env.fund(
XRP(100000), A1);
960 env.fund(
XRP(10000), A2);
961 env.fund(
XRP(1000), A3, G1, G2, G3, M1);
964 env.trust(G1[
"XYZ"](5000), A1);
965 env.trust(G3[
"ABC"](5000), A1);
966 env.trust(G2[
"XYZ"](5000), A2);
967 env.trust(G3[
"ABC"](5000), A2);
968 env.trust(A2[
"ABC"](1000), A3);
969 env.trust(G1[
"XYZ"](100000), M1);
970 env.trust(G2[
"XYZ"](100000), M1);
971 env.trust(G3[
"ABC"](100000), M1);
974 env(
pay(G1, A1, G1[
"XYZ"](3500)));
975 env(
pay(G3, A1, G3[
"ABC"](1200)));
976 env(
pay(G2, M1, G2[
"XYZ"](25000)));
977 env(
pay(G3, M1, G3[
"ABC"](25000)));
980 env(offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)));
981 env(offer(M1,
XRP(10000), G3[
"ABC"](1000)));
987 auto const& send_amt =
XRP(10);
990 BEAST_EXPECT(
equal(da, send_amt));
991 BEAST_EXPECT(st.
empty());
997 auto const& send_amt =
XRP(200);
1000 BEAST_EXPECT(
equal(da, send_amt));
1001 BEAST_EXPECT(st.
empty());
1005 auto const& send_amt = G3[
"ABC"](10);
1008 BEAST_EXPECT(
equal(da, send_amt));
1014 auto const& send_amt = A2[
"ABC"](1);
1017 BEAST_EXPECT(
equal(da, send_amt));
1023 auto const& send_amt = A3[
"ABC"](1);
1026 BEAST_EXPECT(
equal(da, send_amt));
1027 BEAST_EXPECT(
equal(sa, XRP(10)));
1035 testcase(
"Path Find: non-XRP -> XRP");
1036 using namespace jtx;
1043 env.fund(
XRP(1000), A1, A2, G3);
1044 env.fund(
XRP(11000), M1);
1047 env.trust(G3[
"ABC"](1000), A1, A2);
1048 env.trust(G3[
"ABC"](100000), M1);
1051 env(
pay(G3, A1, G3[
"ABC"](1000)));
1052 env(
pay(G3, A2, G3[
"ABC"](1000)));
1053 env(
pay(G3, M1, G3[
"ABC"](1200)));
1056 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)));
1061 auto const& send_amt =
XRP(10);
1063 find_paths(env, A1, A2, send_amt, std::nullopt, A2[
"ABC"].currency);
1064 BEAST_EXPECT(
equal(da, send_amt));
1065 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
1072 testcase(
"Path Find: Bitstamp and SnapSwap, liquidity with no offers");
1073 using namespace jtx;
1077 Account G1BS{
"G1BS"};
1078 Account G2SW{
"G2SW"};
1081 env.fund(
XRP(1000), G1BS, G2SW, A1, A2);
1082 env.fund(
XRP(11000), M1);
1085 env.trust(G1BS[
"HKD"](2000), A1);
1086 env.trust(G2SW[
"HKD"](2000), A2);
1087 env.trust(G1BS[
"HKD"](100000), M1);
1088 env.trust(G2SW[
"HKD"](100000), M1);
1091 env(
pay(G1BS, A1, G1BS[
"HKD"](1000)));
1092 env(
pay(G2SW, A2, G2SW[
"HKD"](1000)));
1096 env(
pay(G1BS, M1, G1BS[
"HKD"](1200)));
1097 env(
pay(G2SW, M1, G2SW[
"HKD"](5000)));
1104 auto const& send_amt = A2[
"HKD"](10);
1106 env, A1, A2, send_amt, std::nullopt, A2[
"HKD"].currency);
1107 BEAST_EXPECT(
equal(da, send_amt));
1108 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1109 BEAST_EXPECT(
same(st,
stpath(G1BS, M1, G2SW)));
1113 auto const& send_amt = A1[
"HKD"](10);
1115 env, A2, A1, send_amt, std::nullopt, A1[
"HKD"].currency);
1116 BEAST_EXPECT(
equal(da, send_amt));
1117 BEAST_EXPECT(
equal(sa, A2[
"HKD"](10)));
1118 BEAST_EXPECT(
same(st,
stpath(G2SW, M1, G1BS)));
1122 auto const& send_amt = A2[
"HKD"](10);
1124 env, G1BS, A2, send_amt, std::nullopt, A1[
"HKD"].currency);
1125 BEAST_EXPECT(
equal(da, send_amt));
1126 BEAST_EXPECT(
equal(sa, G1BS[
"HKD"](10)));
1131 auto const& send_amt = M1[
"HKD"](10);
1133 env, M1, G1BS, send_amt, std::nullopt, A1[
"HKD"].currency);
1134 BEAST_EXPECT(
equal(da, send_amt));
1135 BEAST_EXPECT(
equal(sa, M1[
"HKD"](10)));
1136 BEAST_EXPECT(st.
empty());
1140 auto const& send_amt = A1[
"HKD"](10);
1142 env, G2SW, A1, send_amt, std::nullopt, A1[
"HKD"].currency);
1143 BEAST_EXPECT(
equal(da, send_amt));
1144 BEAST_EXPECT(
equal(sa, G2SW[
"HKD"](10)));
1152 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1153 using namespace jtx;
1166 env.fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1167 env.fund(
XRP(10000), A4);
1168 env.fund(
XRP(11000), M1, M2);
1171 env.trust(G1[
"HKD"](2000), A1);
1172 env.trust(G2[
"HKD"](2000), A2);
1173 env.trust(G1[
"HKD"](2000), A3);
1174 env.trust(G1[
"HKD"](100000), M1);
1175 env.trust(G2[
"HKD"](100000), M1);
1176 env.trust(G1[
"HKD"](100000), M2);
1177 env.trust(G2[
"HKD"](100000), M2);
1180 env(
pay(G1, A1, G1[
"HKD"](1000)));
1181 env(
pay(G2, A2, G2[
"HKD"](1000)));
1182 env(
pay(G1, A3, G1[
"HKD"](1000)));
1183 env(
pay(G1, M1, G1[
"HKD"](1200)));
1184 env(
pay(G2, M1, G2[
"HKD"](5000)));
1185 env(
pay(G1, M2, G1[
"HKD"](1200)));
1186 env(
pay(G2, M2, G2[
"HKD"](5000)));
1189 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1190 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1191 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1199 auto const& send_amt = G1[
"HKD"](10);
1201 env, A1, G1, send_amt, std::nullopt, G1[
"HKD"].currency);
1202 BEAST_EXPECT(st.empty());
1203 BEAST_EXPECT(
equal(da, send_amt));
1204 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1210 auto const& send_amt = A1[
"HKD"](10);
1212 env, A1, G1, send_amt, std::nullopt, G1[
"HKD"].currency);
1213 BEAST_EXPECT(st.empty());
1215 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1221 auto const& send_amt = A3[
"HKD"](10);
1223 env, A1, A3, send_amt, std::nullopt, G1[
"HKD"].currency);
1224 BEAST_EXPECT(
equal(da, send_amt));
1225 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1232 auto const& send_amt = G2[
"HKD"](10);
1234 env, G1, G2, send_amt, std::nullopt, G1[
"HKD"].currency);
1235 BEAST_EXPECT(
equal(da, send_amt));
1236 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1248 auto const& send_amt = G2[
"HKD"](10);
1250 env, A1, G2, send_amt, std::nullopt, G1[
"HKD"].currency);
1251 BEAST_EXPECT(
equal(da, send_amt));
1252 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1264 auto const& send_amt = A2[
"HKD"](10);
1266 env, A1, A2, send_amt, std::nullopt, G1[
"HKD"].currency);
1267 BEAST_EXPECT(
equal(da, send_amt));
1268 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1281 testcase(
"Path Find: non-XRP -> non-XRP, same currency)");
1282 using namespace jtx;
1291 env.fund(
XRP(11000), M1);
1292 env.fund(
XRP(1000), A1, A2, A3, G1, G2);
1295 env.trust(G1[
"HKD"](2000), A1);
1296 env.trust(G2[
"HKD"](2000), A2);
1297 env.trust(A2[
"HKD"](2000), A3);
1298 env.trust(G1[
"HKD"](100000), M1);
1299 env.trust(G2[
"HKD"](100000), M1);
1302 env(
pay(G1, A1, G1[
"HKD"](1000)));
1303 env(
pay(G2, A2, G2[
"HKD"](1000)));
1304 env(
pay(G1, M1, G1[
"HKD"](5000)));
1305 env(
pay(G2, M1, G2[
"HKD"](5000)));
1308 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1312 auto const& send_amt = A2[
"HKD"](10);
1316 find_paths(env, G1, A2, send_amt, std::nullopt, G1[
"HKD"].currency);
1318 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1325 testcase(
"Receive max");
1326 using namespace jtx;
1327 auto const alice =
Account(
"alice");
1328 auto const bob =
Account(
"bob");
1329 auto const charlie =
Account(
"charlie");
1330 auto const gw =
Account(
"gw");
1331 auto const USD = gw[
"USD"];
1335 env.
fund(
XRP(10000), alice, bob, charlie, gw);
1337 env.
trust(USD(100), alice, bob, charlie);
1339 env(
pay(gw, charlie, USD(10)));
1341 env(offer(charlie,
XRP(10), USD(10)));
1345 BEAST_EXPECT(sa ==
XRP(10));
1346 BEAST_EXPECT(
equal(da, USD(10)));
1347 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1349 auto const& pathElem = st[0][0];
1351 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
1352 pathElem.getCurrency() == USD.currency);
1358 env.fund(XRP(10000), alice, bob, charlie, gw);
1360 env.trust(USD(100), alice, bob, charlie);
1362 env(pay(gw, alice, USD(10)));
1364 env(offer(charlie, USD(10), XRP(10)));
1367 find_paths(env, alice, bob, drops(-1), USD(100).value());
1368 BEAST_EXPECT(sa == USD(10));
1369 BEAST_EXPECT(
equal(da, XRP(10)));
1370 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1372 auto const& pathElem = st[0][0];
1374 pathElem.isOffer() &&
1384 using namespace jtx;
1389 auto const alice =
Account(
"alice");
1390 auto const bob =
Account(
"bob");
1391 auto const george =
Account(
"george");
1392 auto const USD = george[
"USD"];
1419 env(
pay(george, alice, USD(70)));
1424 BEAST_EXPECT(
equal(da, bob[
"USD"](5)));
1428 BEAST_EXPECT(st.size() == 1);
1430 BEAST_EXPECT(
equal(sa, alice[
"USD"](5)));
1434 BEAST_EXPECT(st.size() == 0);
1438 test(
"ripple -> ripple",
true,
true,
true);
1439 test(
"ripple -> no ripple",
true,
false,
true);
1440 test(
"no ripple -> ripple",
false,
true,
true);
1441 test(
"no ripple -> no ripple",
false,
false,
false);