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(),
232 params[jss::command] =
"ripple_path_find";
233 params[jss::source_account] =
toBase58(src);
234 params[jss::destination_account] =
toBase58(dst);
235 params[jss::destination_amount] =
243 j[jss::currency] =
to_string(saSrcCurrency.value());
249 app.getJobQueue().postCoro(
250 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
251 context.params = std::move(params);
257 using namespace std::chrono_literals;
259 BEAST_EXPECT(!result.
isMember(jss::error));
269 boost::optional<STAmount>
const& saSendMax = boost::none,
270 boost::optional<Currency>
const& saSrcCurrency = boost::none)
273 env, src, dst, saDstAmount, saSendMax, saSrcCurrency);
274 BEAST_EXPECT(!result.
isMember(jss::error));
277 if (result.
isMember(jss::destination_amount))
282 if (result.
isMember(jss::alternatives))
284 auto const& alts = result[jss::alternatives];
287 auto const&
path = alts[0u];
289 if (
path.isMember(jss::source_amount))
292 if (
path.isMember(jss::destination_amount))
296 if (
path.isMember(jss::paths_computed))
299 p[
"Paths"] =
path[jss::paths_computed];
312 testcase(
"source currency limits");
313 using namespace std::chrono_literals;
316 auto const gw =
Account(
"gateway");
317 env.
fund(
XRP(10000),
"alice",
"bob", gw);
318 env.
trust(gw[
"USD"](100),
"alice",
"bob");
321 auto& app = env.
app();
330 app.getLedgerMaster(),
339 app.getJobQueue().postCoro(
340 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
341 context.params =
rpf(
348 BEAST_EXPECT(!result.
isMember(jss::error));
351 app.getJobQueue().postCoro(
352 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
362 BEAST_EXPECT(result.
isMember(jss::error));
367 app.getJobQueue().postCoro(
368 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
375 BEAST_EXPECT(!result.
isMember(jss::error));
379 app.getJobQueue().postCoro(
380 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
387 BEAST_EXPECT(result.
isMember(jss::error));
393 testcase(
"no direct path no intermediary no alternatives");
396 env.
fund(
XRP(10000),
"alice",
"bob");
400 BEAST_EXPECT(std::get<0>(result).empty());
406 testcase(
"direct path no intermediary");
409 env.
fund(
XRP(10000),
"alice",
"bob");
416 BEAST_EXPECT(st.
empty());
423 testcase(
"payment auto path find");
426 auto const gw =
Account(
"gateway");
427 auto const USD = gw[
"USD"];
428 env.
fund(
XRP(10000),
"alice",
"bob", gw);
429 env.
trust(USD(600),
"alice");
430 env.
trust(USD(700),
"bob");
431 env(
pay(gw,
"alice", USD(70)));
432 env(
pay(
"alice",
"bob", USD(24)));
442 testcase(
"path find");
445 auto const gw =
Account(
"gateway");
446 auto const USD = gw[
"USD"];
447 env.
fund(
XRP(10000),
"alice",
"bob", gw);
448 env.
trust(USD(600),
"alice");
449 env.
trust(USD(700),
"bob");
450 env(
pay(gw,
"alice", USD(70)));
451 env(
pay(gw,
"bob", USD(50)));
465 testcase(
"XRP to XRP");
467 env.
fund(
XRP(10000),
"alice",
"bob");
470 BEAST_EXPECT(std::get<0>(result).empty());
476 testcase(
"path find consume all");
481 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
492 env,
"alice",
"edward",
Account(
"edward")[
"USD"](-1));
500 auto const gw =
Account(
"gateway");
501 auto const USD = gw[
"USD"];
502 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
503 env.
trust(USD(100),
"bob",
"carol");
504 env(
pay(gw,
"carol", USD(100)));
505 env(
offer(
"carol",
XRP(100), USD(100)));
515 boost::optional<STAmount>(
XRP(100000000)));
516 BEAST_EXPECT(st.
empty());
522 boost::optional<STAmount>(
XRP(100000000)));
523 BEAST_EXPECT(sa ==
XRP(100));
531 testcase(
"alternative path consume both");
534 auto const gw =
Account(
"gateway");
535 auto const USD = gw[
"USD"];
536 auto const gw2 =
Account(
"gateway2");
537 auto const gw2_USD = gw2[
"USD"];
538 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
539 env.
trust(USD(600),
"alice");
540 env.
trust(gw2_USD(800),
"alice");
541 env.
trust(USD(700),
"bob");
542 env.
trust(gw2_USD(900),
"bob");
543 env(
pay(gw,
"alice", USD(70)));
544 env(
pay(gw2,
"alice", gw2_USD(70)));
545 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
560 testcase(
"alternative paths consume best transfer");
563 auto const gw =
Account(
"gateway");
564 auto const USD = gw[
"USD"];
565 auto const gw2 =
Account(
"gateway2");
566 auto const gw2_USD = gw2[
"USD"];
567 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
569 env.
trust(USD(600),
"alice");
570 env.
trust(gw2_USD(800),
"alice");
571 env.
trust(USD(700),
"bob");
572 env.
trust(gw2_USD(900),
"bob");
573 env(
pay(gw,
"alice", USD(70)));
574 env(
pay(gw2,
"alice", gw2_USD(70)));
575 env(
pay(
"alice",
"bob", USD(70)));
589 testcase(
"alternative paths - consume best transfer first");
592 auto const gw =
Account(
"gateway");
593 auto const USD = gw[
"USD"];
594 auto const gw2 =
Account(
"gateway2");
595 auto const gw2_USD = gw2[
"USD"];
596 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
598 env.
trust(USD(600),
"alice");
599 env.
trust(gw2_USD(800),
"alice");
600 env.
trust(USD(700),
"bob");
601 env.
trust(gw2_USD(900),
"bob");
602 env(
pay(gw,
"alice", USD(70)));
603 env(
pay(gw2,
"alice", gw2_USD(70)));
604 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
620 testcase(
"alternative paths - limit returned paths to best quality");
623 auto const gw =
Account(
"gateway");
624 auto const USD = gw[
"USD"];
625 auto const gw2 =
Account(
"gateway2");
626 auto const gw2_USD = gw2[
"USD"];
627 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
628 env(
rate(
"carol", 1.1));
629 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
631 env.
trust(USD(800),
"alice",
"bob");
632 env.
trust(gw2_USD(800),
"alice",
"bob");
635 env(
pay(gw2,
"alice", gw2_USD(100)));
636 env(
pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
637 env(
pay(gw,
"alice", USD(100)));
655 testcase(
"path negative: Issue #5");
658 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
659 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
662 env(
pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
668 BEAST_EXPECT(std::get<0>(result).empty());
673 BEAST_EXPECT(std::get<0>(result).empty());
693 testcase(
"path negative: ripple-client issue #23: smaller");
696 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
701 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](55)),
712 testcase(
"path negative: ripple-client issue #23: larger");
715 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
721 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](50)),
739 testcase(
"via gateway");
742 auto const gw =
Account(
"gateway");
743 auto const AUD = gw[
"AUD"];
744 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
746 env.
trust(AUD(100),
"bob",
"carol");
747 env(
pay(gw,
"carol", AUD(50)));
748 env(
offer(
"carol",
XRP(50), AUD(50)));
755 BEAST_EXPECT(std::get<0>(result).empty());
761 testcase(
"path find");
764 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
779 testcase(
"quality set and test");
782 env.
fund(
XRP(10000),
"alice",
"bob");
792 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
798 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
801 "HighNode" : "0000000000000000",
802 "HighQualityIn" : 2000,
803 "HighQualityOut" : 1400000000,
804 "LedgerEntryType" : "RippleState",
807 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
810 "LowNode" : "0000000000000000"
818 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
819 BEAST_EXPECT(*it == jv_l[it.memberName()]);
825 testcase(
"trust normal clear");
828 env.
fund(
XRP(10000),
"alice",
"bob");
837 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
843 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
846 "HighNode" : "0000000000000000",
847 "LedgerEntryType" : "RippleState",
850 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
853 "LowNode" : "0000000000000000"
862 BEAST_EXPECT(*it == jv_l[it.memberName()]);
875 testcase(
"trust auto clear");
878 env.
fund(
XRP(10000),
"alice",
"bob");
880 env(
pay(
"bob",
"alice",
Account(
"bob")[
"USD"](50)));
889 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
896 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
899 "HighNode" : "0000000000000000",
900 "LedgerEntryType" : "RippleState",
904 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
907 "LowNode" : "0000000000000000"
915 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
916 BEAST_EXPECT(*it == jv_l[it.memberName()]);
918 env(
pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
928 testcase(
"Path Find: XRP -> XRP and XRP -> IOU");
941 env.
fund(
XRP(1000), A3, G1, G2, G3, M1);
944 env.
trust(G1[
"XYZ"](5000), A1);
945 env.
trust(G3[
"ABC"](5000), A1);
946 env.
trust(G2[
"XYZ"](5000), A2);
947 env.
trust(G3[
"ABC"](5000), A2);
949 env.
trust(G1[
"XYZ"](100000), M1);
950 env.
trust(G2[
"XYZ"](100000), M1);
951 env.
trust(G3[
"ABC"](100000), M1);
954 env(
pay(G1, A1, G1[
"XYZ"](3500)));
955 env(
pay(G3, A1, G3[
"ABC"](1200)));
956 env(
pay(G2, M1, G2[
"XYZ"](25000)));
957 env(
pay(G3, M1, G3[
"ABC"](25000)));
960 env(
offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)));
961 env(
offer(M1,
XRP(10000), G3[
"ABC"](1000)));
967 auto const& send_amt =
XRP(10);
970 BEAST_EXPECT(
equal(da, send_amt));
971 BEAST_EXPECT(st.
empty());
977 auto const& send_amt =
XRP(200);
980 BEAST_EXPECT(
equal(da, send_amt));
981 BEAST_EXPECT(st.
empty());
985 auto const& send_amt = G3[
"ABC"](10);
988 BEAST_EXPECT(
equal(da, send_amt));
994 auto const& send_amt = A2[
"ABC"](1);
997 BEAST_EXPECT(
equal(da, send_amt));
1003 auto const& send_amt = A3[
"ABC"](1);
1006 BEAST_EXPECT(
equal(da, send_amt));
1007 BEAST_EXPECT(
equal(sa, XRP(10)));
1015 testcase(
"Path Find: non-XRP -> XRP");
1016 using namespace jtx;
1023 env.
fund(
XRP(1000), A1, A2, G3);
1027 env.
trust(G3[
"ABC"](1000), A1, A2);
1028 env.
trust(G3[
"ABC"](100000), M1);
1031 env(
pay(G3, A1, G3[
"ABC"](1000)));
1032 env(
pay(G3, A2, G3[
"ABC"](1000)));
1033 env(
pay(G3, M1, G3[
"ABC"](1200)));
1036 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)));
1041 auto const& send_amt =
XRP(10);
1043 find_paths(env, A1, A2, send_amt, boost::none, A2[
"ABC"].currency);
1044 BEAST_EXPECT(
equal(da, send_amt));
1045 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
1052 testcase(
"Path Find: Bitstamp and SnapSwap, liquidity with no offers");
1053 using namespace jtx;
1057 Account G1BS{
"G1BS"};
1058 Account G2SW{
"G2SW"};
1061 env.
fund(
XRP(1000), G1BS, G2SW, A1, A2);
1066 env.
trust(G2SW[
"HKD"](2000), A2);
1067 env.
trust(G1BS[
"HKD"](100000), M1);
1068 env.
trust(G2SW[
"HKD"](100000), M1);
1071 env(
pay(G1BS, A1, G1BS[
"HKD"](1000)));
1072 env(
pay(G2SW, A2, G2SW[
"HKD"](1000)));
1076 env(
pay(G1BS, M1, G1BS[
"HKD"](1200)));
1077 env(
pay(G2SW, M1, G2SW[
"HKD"](5000)));
1084 auto const& send_amt = A2[
"HKD"](10);
1086 env, A1, A2, send_amt, boost::none, A2[
"HKD"].currency);
1087 BEAST_EXPECT(
equal(da, send_amt));
1088 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1089 BEAST_EXPECT(
same(st,
stpath(G1BS, M1, G2SW)));
1093 auto const& send_amt = A1[
"HKD"](10);
1095 env, A2, A1, send_amt, boost::none, A1[
"HKD"].currency);
1096 BEAST_EXPECT(
equal(da, send_amt));
1097 BEAST_EXPECT(
equal(sa, A2[
"HKD"](10)));
1098 BEAST_EXPECT(
same(st,
stpath(G2SW, M1, G1BS)));
1102 auto const& send_amt = A2[
"HKD"](10);
1104 env, G1BS, A2, send_amt, boost::none, A1[
"HKD"].currency);
1105 BEAST_EXPECT(
equal(da, send_amt));
1106 BEAST_EXPECT(
equal(sa, G1BS[
"HKD"](10)));
1111 auto const& send_amt = M1[
"HKD"](10);
1113 env, M1, G1BS, send_amt, boost::none, A1[
"HKD"].currency);
1114 BEAST_EXPECT(
equal(da, send_amt));
1115 BEAST_EXPECT(
equal(sa, M1[
"HKD"](10)));
1116 BEAST_EXPECT(st.
empty());
1120 auto const& send_amt = A1[
"HKD"](10);
1122 env, G2SW, A1, send_amt, boost::none, A1[
"HKD"].currency);
1123 BEAST_EXPECT(
equal(da, send_amt));
1124 BEAST_EXPECT(
equal(sa, G2SW[
"HKD"](10)));
1132 testcase(
"Path Find: non-XRP -> non-XRP, same currency");
1133 using namespace jtx;
1146 env.
fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1151 env.
trust(G1[
"HKD"](2000), A1);
1152 env.
trust(G2[
"HKD"](2000), A2);
1153 env.
trust(G1[
"HKD"](2000), A3);
1154 env.
trust(G1[
"HKD"](100000), M1);
1155 env.
trust(G2[
"HKD"](100000), M1);
1156 env.
trust(G1[
"HKD"](100000), M2);
1157 env.
trust(G2[
"HKD"](100000), M2);
1160 env(
pay(G1, A1, G1[
"HKD"](1000)));
1161 env(
pay(G2, A2, G2[
"HKD"](1000)));
1162 env(
pay(G1, A3, G1[
"HKD"](1000)));
1163 env(
pay(G1, M1, G1[
"HKD"](1200)));
1164 env(
pay(G2, M1, G2[
"HKD"](5000)));
1165 env(
pay(G1, M2, G1[
"HKD"](1200)));
1166 env(
pay(G2, M2, G2[
"HKD"](5000)));
1169 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1170 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1171 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1179 auto const& send_amt = G1[
"HKD"](10);
1181 env, A1, G1, send_amt, boost::none, G1[
"HKD"].currency);
1182 BEAST_EXPECT(st.empty());
1183 BEAST_EXPECT(
equal(da, send_amt));
1184 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1190 auto const& send_amt = A1[
"HKD"](10);
1192 env, A1, G1, send_amt, boost::none, G1[
"HKD"].currency);
1193 BEAST_EXPECT(st.empty());
1195 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1201 auto const& send_amt = A3[
"HKD"](10);
1203 env, A1, A3, send_amt, boost::none, G1[
"HKD"].currency);
1204 BEAST_EXPECT(
equal(da, send_amt));
1205 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1212 auto const& send_amt = G2[
"HKD"](10);
1214 env, G1, G2, send_amt, boost::none, G1[
"HKD"].currency);
1215 BEAST_EXPECT(
equal(da, send_amt));
1216 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1228 auto const& send_amt = G2[
"HKD"](10);
1230 env, A1, G2, send_amt, boost::none, G1[
"HKD"].currency);
1231 BEAST_EXPECT(
equal(da, send_amt));
1232 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1244 auto const& send_amt = A2[
"HKD"](10);
1246 env, A1, A2, send_amt, boost::none, G1[
"HKD"].currency);
1247 BEAST_EXPECT(
equal(da, send_amt));
1248 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1261 testcase(
"Path Find: non-XRP -> non-XRP, same currency)");
1262 using namespace jtx;
1272 env.
fund(
XRP(1000), A1, A2, A3, G1, G2);
1275 env.
trust(G1[
"HKD"](2000), A1);
1276 env.
trust(G2[
"HKD"](2000), A2);
1277 env.
trust(A2[
"HKD"](2000), A3);
1278 env.
trust(G1[
"HKD"](100000), M1);
1279 env.
trust(G2[
"HKD"](100000), M1);
1282 env(
pay(G1, A1, G1[
"HKD"](1000)));
1283 env(
pay(G2, A2, G2[
"HKD"](1000)));
1284 env(
pay(G1, M1, G1[
"HKD"](5000)));
1285 env(
pay(G2, M1, G2[
"HKD"](5000)));
1288 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1292 auto const& send_amt = A2[
"HKD"](10);
1296 find_paths(env, G1, A2, send_amt, boost::none, G1[
"HKD"].currency);
1297 BEAST_EXPECT(
equal(da, send_amt));
1298 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));