2#include <test/jtx/AMM.h>
3#include <test/jtx/AMMTest.h>
4#include <test/jtx/envconfig.h>
5#include <test/jtx/permissioned_dex.h>
7#include <xrpld/rpc/RPCHandler.h>
8#include <xrpld/rpc/detail/Tuning.h>
10#include <xrpl/beast/unit_test.h>
11#include <xrpl/core/JobQueue.h>
12#include <xrpl/json/json_reader.h>
13#include <xrpl/protocol/ApiVersion.h>
14#include <xrpl/protocol/STParsedJSON.h>
15#include <xrpl/protocol/TxFlags.h>
16#include <xrpl/protocol/jss.h>
17#include <xrpl/resource/Fees.h>
35 jv[jss::command] =
"ripple_path_find";
36 jv[jss::source_account] =
toBase58(src);
50 jv[jss::destination_account] = d;
53 j[jss::currency] =
"USD";
54 j[jss::value] =
"0.01";
72 cfg->PATH_SEARCH_OLD = 7;
74 cfg->PATH_SEARCH_MAX = 10;
90 template <
class Rep,
class Period>
121 auto& app = env.
app();
130 app.getLedgerMaster(),
140 params[jss::command] =
"ripple_path_find";
141 params[jss::source_account] =
toBase58(src);
142 params[jss::destination_account] =
toBase58(dst);
150 j[jss::currency] =
to_string(saSrcCurrency.value());
158 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
159 context.params = std::move(params);
165 using namespace std::chrono_literals;
167 BEAST_EXPECT(!result.
isMember(jss::error));
182 BEAST_EXPECT(!result.
isMember(jss::error));
185 if (result.
isMember(jss::destination_amount))
190 if (result.
isMember(jss::alternatives))
192 auto const& alts = result[jss::alternatives];
195 auto const&
path = alts[0u];
197 if (
path.isMember(jss::source_amount))
200 if (
path.isMember(jss::destination_amount))
203 if (
path.isMember(jss::paths_computed))
206 p[
"Paths"] =
path[jss::paths_computed];
220 using namespace std::chrono_literals;
223 auto const gw =
Account(
"gateway");
224 env.
fund(
XRP(10000),
"alice",
"bob", gw);
226 env.
trust(gw[
"USD"](100),
"alice",
"bob");
229 auto& app = env.
app();
238 app.getLedgerMaster(),
249 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
256 BEAST_EXPECT(!result.
isMember(jss::error));
259 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
266 BEAST_EXPECT(result.
isMember(jss::error));
271 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
278 BEAST_EXPECT(!result.
isMember(jss::error));
282 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
289 BEAST_EXPECT(result.
isMember(jss::error));
295 testcase(
"no direct path no intermediary no alternatives");
298 env.
fund(
XRP(10000),
"alice",
"bob");
308 testcase(
"direct path no intermediary");
311 env.
fund(
XRP(10000),
"alice",
"bob");
318 BEAST_EXPECT(st.
empty());
328 auto const gw =
Account(
"gateway");
329 auto const USD = gw[
"USD"];
330 env.
fund(
XRP(10000),
"alice",
"bob", gw);
332 env.
trust(USD(600),
"alice");
333 env.
trust(USD(700),
"bob");
334 env(
pay(gw,
"alice", USD(70)));
335 env(
pay(
"alice",
"bob", USD(24)));
348 auto const gw =
Account(
"gateway");
349 auto const USD = gw[
"USD"];
350 env.
fund(
XRP(10000),
"alice",
"bob", gw);
352 env.
trust(USD(600),
"alice");
353 env.
trust(USD(700),
"bob");
354 env(
pay(gw,
"alice", USD(70)));
355 env(
pay(gw,
"bob", USD(50)));
375 env.
fund(
XRP(10000),
"alice",
"bob");
389 testcase(
std::string(
"path find consume all") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
394 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
404 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan",
"edward"});
418 auto const gw =
Account(
"gateway");
419 auto const USD = gw[
"USD"];
420 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
422 env.
trust(USD(100),
"bob",
"carol");
424 env(
pay(gw,
"carol", USD(100)));
430 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"gateway"});
435 env(
offer(
"carol",
XRP(100), USD(100)));
450 BEAST_EXPECT(st.
empty());
459 BEAST_EXPECT(sa ==
XRP(100));
474 BEAST_EXPECT(st.
empty());
482 testcase(
std::string(
"alternative path consume both") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
485 auto const gw =
Account(
"gateway");
486 auto const USD = gw[
"USD"];
487 auto const gw2 =
Account(
"gateway2");
488 auto const gw2_USD = gw2[
"USD"];
489 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
491 env.
trust(USD(600),
"alice");
492 env.
trust(gw2_USD(800),
"alice");
493 env.
trust(USD(700),
"bob");
494 env.
trust(gw2_USD(900),
"bob");
499 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
500 env(
pay(gw,
"alice", USD(70)),
domain(*domainID));
501 env(
pay(gw2,
"alice", gw2_USD(70)),
domain(*domainID));
506 env(
pay(gw,
"alice", USD(70)));
507 env(
pay(gw2,
"alice", gw2_USD(70)));
525 std::string(
"alternative paths consume best transfer") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
528 auto const gw =
Account(
"gateway");
529 auto const USD = gw[
"USD"];
530 auto const gw2 =
Account(
"gateway2");
531 auto const gw2_USD = gw2[
"USD"];
532 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
535 env.
trust(USD(600),
"alice");
536 env.
trust(gw2_USD(800),
"alice");
537 env.
trust(USD(700),
"bob");
538 env.
trust(gw2_USD(900),
"bob");
543 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
544 env(
pay(gw,
"alice", USD(70)),
domain(*domainID));
545 env(
pay(gw2,
"alice", gw2_USD(70)),
domain(*domainID));
546 env(
pay(
"alice",
"bob", USD(70)),
domain(*domainID));
550 env(
pay(gw,
"alice", USD(70)));
551 env(
pay(gw2,
"alice", gw2_USD(70)));
552 env(
pay(
"alice",
"bob", USD(70)));
567 testcase(
"alternative paths - consume best transfer first");
570 auto const gw =
Account(
"gateway");
571 auto const USD = gw[
"USD"];
572 auto const gw2 =
Account(
"gateway2");
573 auto const gw2_USD = gw2[
"USD"];
574 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
577 env.
trust(USD(600),
"alice");
578 env.
trust(gw2_USD(800),
"alice");
579 env.
trust(USD(700),
"bob");
580 env.
trust(gw2_USD(900),
"bob");
581 env(
pay(gw,
"alice", USD(70)));
582 env(
pay(gw2,
"alice", gw2_USD(70)));
583 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
600 std::string(
"alternative paths - limit returned paths to best quality") +
601 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
604 auto const gw =
Account(
"gateway");
605 auto const USD = gw[
"USD"];
606 auto const gw2 =
Account(
"gateway2");
607 auto const gw2_USD = gw2[
"USD"];
608 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
610 env(
rate(
"carol", 1.1));
611 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
613 env.
trust(USD(800),
"alice",
"bob");
614 env.
trust(gw2_USD(800),
"alice",
"bob");
618 env(
pay(gw2,
"alice", gw2_USD(100)));
620 env(
pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
622 env(
pay(gw,
"alice", USD(100)));
628 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan", gw, gw2});
642 testcase(
std::string(
"path negative: Issue #5") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
645 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
647 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
650 env(
pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
658 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan"});
688 testcase(
"path negative: ripple-client issue #23: smaller");
691 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
707 testcase(
"path negative: ripple-client issue #23: larger");
710 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
737 auto const gw =
Account(
"gateway");
738 auto const AUD = gw[
"AUD"];
739 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
743 env.
trust(AUD(100),
"bob",
"carol");
745 env(
pay(gw,
"carol", AUD(50)));
751 domainID =
setupDomain(env, {
"alice",
"bob",
"carol", gw});
759 env(
offer(
"carol",
XRP(50), AUD(50)));
779 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
797 env.
fund(
XRP(10000),
"alice",
"bob");
800 json(
"{\"" + sfQualityIn.fieldName +
"\": 2000}"),
801 json(
"{\"" + sfQualityOut.fieldName +
"\": 1400000000}"));
808 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
814 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
818 "HighQualityIn" : 2000,
819 "HighQualityOut" : 1400000000,
820 "LedgerEntryType" : "RippleState",
823 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
832 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
833 BEAST_EXPECT(*it == jv_l[it.memberName()]);
842 env.
fund(
XRP(10000),
"alice",
"bob");
852 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
858 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
862 "LedgerEntryType" : "RippleState",
865 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
874 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
875 BEAST_EXPECT(*it == jv_l[it.memberName()]);
888 env.
fund(
XRP(10000),
"alice",
"bob");
891 env(
pay(
"bob",
"alice",
Account(
"bob")[
"USD"](50)));
900 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
907 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
911 "LedgerEntryType" : "RippleState",
915 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
924 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
925 BEAST_EXPECT(*it == jv_l[it.memberName()]);
927 env(
pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
934 testcase(
std::string(
"Path Find: XRP -> XRP and XRP -> IOU") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
945 env.fund(
XRP(100000), A1);
946 env.fund(
XRP(10000), A2);
947 env.fund(
XRP(1000), A3, G1, G2, G3, M1);
950 env.trust(G1[
"XYZ"](5000), A1);
951 env.trust(G3[
"ABC"](5000), A1);
952 env.trust(G2[
"XYZ"](5000), A2);
953 env.trust(G3[
"ABC"](5000), A2);
954 env.trust(A2[
"ABC"](1000), A3);
955 env.trust(G1[
"XYZ"](100000), M1);
956 env.trust(G2[
"XYZ"](100000), M1);
957 env.trust(G3[
"ABC"](100000), M1);
960 env(
pay(G1, A1, G1[
"XYZ"](3500)));
961 env(
pay(G3, A1, G3[
"ABC"](1200)));
962 env(
pay(G2, M1, G2[
"XYZ"](25000)));
963 env(
pay(G3, M1, G3[
"ABC"](25000)));
969 domainID =
setupDomain(env, {A1, A2, A3, G1, G2, G3, M1});
970 env(
offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)),
domain(*domainID));
976 env(
offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)));
977 env(
offer(M1,
XRP(10000), G3[
"ABC"](1000)));
985 auto const& send_amt =
XRP(10);
987 BEAST_EXPECT(
equal(da, send_amt));
988 BEAST_EXPECT(st.empty());
994 auto const& send_amt =
XRP(200);
996 BEAST_EXPECT(
equal(da, send_amt));
997 BEAST_EXPECT(st.empty());
1001 auto const& send_amt = G3[
"ABC"](10);
1003 BEAST_EXPECT(
equal(da, send_amt));
1009 auto const& send_amt = A2[
"ABC"](1);
1011 BEAST_EXPECT(
equal(da, send_amt));
1017 auto const& send_amt = A3[
"ABC"](1);
1019 BEAST_EXPECT(
equal(da, send_amt));
1028 testcase(
std::string(
"Path Find: non-XRP -> XRP") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1029 using namespace jtx;
1036 env.fund(
XRP(1000), A1, A2, G3);
1037 env.fund(
XRP(11000), M1);
1040 env.trust(G3[
"ABC"](1000), A1, A2);
1041 env.trust(G3[
"ABC"](100000), M1);
1044 env(
pay(G3, A1, G3[
"ABC"](1000)));
1045 env(
pay(G3, A2, G3[
"ABC"](1000)));
1046 env(
pay(G3, M1, G3[
"ABC"](1200)));
1053 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)), domain(*domainID));
1057 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)));
1062 auto const& send_amt =
XRP(10);
1066 BEAST_EXPECT(
equal(da, send_amt));
1067 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
1076 BEAST_EXPECT(
equal(da, send_amt));
1077 BEAST_EXPECT(st.empty());
1085 std::string(
"Path Find: Bitstamp and SnapSwap, liquidity with no offers") +
1086 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1087 using namespace jtx;
1091 Account G1BS{
"G1BS"};
1092 Account G2SW{
"G2SW"};
1095 env.fund(
XRP(1000), G1BS, G2SW, A1, A2);
1096 env.fund(
XRP(11000), M1);
1099 env.trust(G1BS[
"HKD"](2000), A1);
1100 env.trust(G2SW[
"HKD"](2000), A2);
1101 env.trust(G1BS[
"HKD"](100000), M1);
1102 env.trust(G2SW[
"HKD"](100000), M1);
1105 env(
pay(G1BS, A1, G1BS[
"HKD"](1000)));
1106 env(
pay(G2SW, A2, G2SW[
"HKD"](1000)));
1110 env(
pay(G1BS, M1, G1BS[
"HKD"](1200)));
1111 env(
pay(G2SW, M1, G2SW[
"HKD"](5000)));
1116 domainID =
setupDomain(env, {A1, A2, G1BS, G2SW, M1});
1122 auto const& send_amt = A2[
"HKD"](10);
1124 BEAST_EXPECT(
equal(da, send_amt));
1125 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1126 BEAST_EXPECT(
same(st,
stpath(G1BS, M1, G2SW)));
1130 auto const& send_amt = A1[
"HKD"](10);
1132 BEAST_EXPECT(
equal(da, send_amt));
1133 BEAST_EXPECT(
equal(sa, A2[
"HKD"](10)));
1134 BEAST_EXPECT(
same(st,
stpath(G2SW, M1, G1BS)));
1138 auto const& send_amt = A2[
"HKD"](10);
1140 BEAST_EXPECT(
equal(da, send_amt));
1141 BEAST_EXPECT(
equal(sa, G1BS[
"HKD"](10)));
1146 auto const& send_amt = M1[
"HKD"](10);
1148 BEAST_EXPECT(
equal(da, send_amt));
1149 BEAST_EXPECT(
equal(sa, M1[
"HKD"](10)));
1150 BEAST_EXPECT(st.empty());
1154 auto const& send_amt = A1[
"HKD"](10);
1156 BEAST_EXPECT(
equal(da, send_amt));
1157 BEAST_EXPECT(
equal(sa, G2SW[
"HKD"](10)));
1166 std::string(
"Path Find: non-XRP -> non-XRP, same currency") + (domainEnabled ?
" w/ " :
" w/o ") +
1168 using namespace jtx;
1181 env.fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1182 env.fund(
XRP(10000), A4);
1183 env.fund(
XRP(11000), M1, M2);
1186 env.trust(G1[
"HKD"](2000), A1);
1187 env.trust(G2[
"HKD"](2000), A2);
1188 env.trust(G1[
"HKD"](2000), A3);
1189 env.trust(G1[
"HKD"](100000), M1);
1190 env.trust(G2[
"HKD"](100000), M1);
1191 env.trust(G1[
"HKD"](100000), M2);
1192 env.trust(G2[
"HKD"](100000), M2);
1195 env(
pay(G1, A1, G1[
"HKD"](1000)));
1196 env(
pay(G2, A2, G2[
"HKD"](1000)));
1197 env(
pay(G1, A3, G1[
"HKD"](1000)));
1198 env(
pay(G1, M1, G1[
"HKD"](1200)));
1199 env(
pay(G2, M1, G2[
"HKD"](5000)));
1200 env(
pay(G1, M2, G1[
"HKD"](1200)));
1201 env(
pay(G2, M2, G2[
"HKD"](5000)));
1207 domainID =
setupDomain(env, {A1, A2, A3, A4, G1, G2, G3, G4, M1, M2});
1208 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(*domainID));
1209 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(*domainID));
1210 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(*domainID));
1214 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1215 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1216 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1225 auto const& send_amt = G1[
"HKD"](10);
1227 BEAST_EXPECT(st.empty());
1228 BEAST_EXPECT(
equal(da, send_amt));
1229 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1235 auto const& send_amt = A1[
"HKD"](10);
1237 BEAST_EXPECT(st.empty());
1238 BEAST_EXPECT(
equal(da, send_amt));
1239 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1245 auto const& send_amt = A3[
"HKD"](10);
1247 BEAST_EXPECT(
equal(da, send_amt));
1248 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1255 auto const& send_amt = G2[
"HKD"](10);
1257 BEAST_EXPECT(
equal(da, send_amt));
1258 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1266 auto const& send_amt = G2[
"HKD"](10);
1268 BEAST_EXPECT(
equal(da, send_amt));
1269 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1282 auto const& send_amt = A2[
"HKD"](10);
1284 BEAST_EXPECT(
equal(da, send_amt));
1299 std::string(
"Path Find: non-XRP -> non-XRP, same currency)") + (domainEnabled ?
" w/ " :
" w/o ") +
1301 using namespace jtx;
1310 env.fund(
XRP(11000), M1);
1311 env.fund(
XRP(1000), A1, A2, A3, G1, G2);
1314 env.trust(G1[
"HKD"](2000), A1);
1315 env.trust(G2[
"HKD"](2000), A2);
1316 env.trust(A2[
"HKD"](2000), A3);
1317 env.trust(G1[
"HKD"](100000), M1);
1318 env.trust(G2[
"HKD"](100000), M1);
1321 env(
pay(G1, A1, G1[
"HKD"](1000)));
1322 env(
pay(G2, A2, G2[
"HKD"](1000)));
1323 env(
pay(G1, M1, G1[
"HKD"](5000)));
1324 env(
pay(G2, M1, G2[
"HKD"](5000)));
1330 domainID =
setupDomain(env, {A1, A2, A3, G1, G2, M1});
1331 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(*domainID));
1335 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1340 auto const& send_amt = A2[
"HKD"](10);
1344 BEAST_EXPECT(
equal(da, send_amt));
1345 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1354 using namespace jtx;
1355 auto const alice = Account(
"alice");
1356 auto const bob = Account(
"bob");
1357 auto const charlie = Account(
"charlie");
1358 auto const gw = Account(
"gw");
1359 auto const USD = gw[
"USD"];
1363 env.fund(
XRP(10000), alice, bob, charlie, gw);
1365 env.trust(USD(100), alice, bob, charlie);
1367 env(
pay(gw, charlie, USD(10)));
1373 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1379 env(
offer(charlie,
XRP(10), USD(10)));
1384 BEAST_EXPECT(sa ==
XRP(10));
1385 BEAST_EXPECT(
equal(da, USD(10)));
1386 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1388 auto const& pathElem = st[0][0];
1390 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() && pathElem.getCurrency() == USD.currency);
1396 env.fund(
XRP(10000), alice, bob, charlie, gw);
1398 env.trust(USD(100), alice, bob, charlie);
1400 env(
pay(gw, alice, USD(10)));
1406 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1407 env(
offer(charlie, USD(10),
XRP(10)), domain(*domainID));
1412 env(
offer(charlie, USD(10),
XRP(10)));
1417 BEAST_EXPECT(sa == USD(10));
1419 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1421 auto const& pathElem = st[0][0];
1423 pathElem.isOffer() && pathElem.getIssuerID() ==
xrpAccount() &&
1432 using namespace jtx;
1437 auto const alice = Account(
"alice");
1438 auto const bob = Account(
"bob");
1439 auto const george = Account(
"george");
1440 auto const USD = george[
"USD"];
1441 auto test = [&](
std::string casename,
bool aliceRipple,
bool bobRipple,
bool expectPath) {
1445 env.fund(
XRP(10000),
noripple(alice, bob, george));
1454 env(
pay(george, alice, USD(70)));
1457 auto [st, sa, da] =
find_paths(env,
"alice",
"bob", Account(
"bob")[
"USD"](5));
1458 BEAST_EXPECT(
equal(da, bob[
"USD"](5)));
1462 BEAST_EXPECT(st.size() == 1);
1464 BEAST_EXPECT(
equal(sa, alice[
"USD"](5)));
1468 BEAST_EXPECT(st.size() == 0);
1472 test(
"ripple -> ripple",
true,
true,
true);
1473 test(
"ripple -> no ripple",
true,
false,
true);
1474 test(
"no ripple -> ripple",
false,
true,
true);
1475 test(
"no ripple -> no ripple",
false,
false,
false);
1482 using namespace jtx;
1487 auto testPathfind = [&](
auto func,
bool const domainEnabled =
false) {
1500 env.fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1501 env.fund(
XRP(10000), A4);
1502 env.fund(
XRP(11000), M1, M2);
1505 env.trust(G1[
"HKD"](2000), A1);
1506 env.trust(G2[
"HKD"](2000), A2);
1507 env.trust(G1[
"HKD"](2000), A3);
1508 env.trust(G1[
"HKD"](100000), M1);
1509 env.trust(G2[
"HKD"](100000), M1);
1510 env.trust(G1[
"HKD"](100000), M2);
1511 env.trust(G2[
"HKD"](100000), M2);
1514 env(
pay(G1, A1, G1[
"HKD"](1000)));
1515 env(
pay(G2, A2, G2[
"HKD"](1000)));
1516 env(
pay(G1, A3, G1[
"HKD"](1000)));
1517 env(
pay(G1, M1, G1[
"HKD"](1200)));
1518 env(
pay(G2, M1, G2[
"HKD"](5000)));
1519 env(
pay(G1, M2, G1[
"HKD"](1200)));
1520 env(
pay(G2, M2, G2[
"HKD"](5000)));
1523 std::optional<uint256> domainID =
setupDomain(env, {A1, A2, A3, A4, G1, G2, G3, G4, M1, M2});
1524 BEAST_EXPECT(domainID);
1526 func(env, M1, M2, G1, G2, *domainID);
1534 auto const& send_amt = G1[
"HKD"](10);
1536 env, A1, G1, send_amt,
std::nullopt, G1[
"HKD"].currency, domainEnabled ? domainID :
std::nullopt);
1537 BEAST_EXPECT(st.empty());
1538 BEAST_EXPECT(
equal(da, send_amt));
1539 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1545 auto const& send_amt = A1[
"HKD"](10);
1547 env, A1, G1, send_amt,
std::nullopt, G1[
"HKD"].currency, domainEnabled ? domainID :
std::nullopt);
1548 BEAST_EXPECT(st.empty());
1549 BEAST_EXPECT(
equal(da, send_amt));
1550 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1556 auto const& send_amt = A3[
"HKD"](10);
1558 env, A1, A3, send_amt,
std::nullopt, G1[
"HKD"].currency, domainEnabled ? domainID :
std::nullopt);
1559 BEAST_EXPECT(
equal(da, send_amt));
1560 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1567 auto const& send_amt = G2[
"HKD"](10);
1569 env, G1, G2, send_amt,
std::nullopt, G1[
"HKD"].currency, domainEnabled ? domainID :
std::nullopt);
1570 BEAST_EXPECT(
equal(da, send_amt));
1571 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1579 auto const& send_amt = G2[
"HKD"](10);
1581 env, A1, G2, send_amt,
std::nullopt, G1[
"HKD"].currency, domainEnabled ? domainID :
std::nullopt);
1582 BEAST_EXPECT(
equal(da, send_amt));
1583 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1596 auto const& send_amt = A2[
"HKD"](10);
1598 env, A1, A2, send_amt,
std::nullopt, G1[
"HKD"].currency, domainEnabled ? domainID :
std::nullopt);
1599 BEAST_EXPECT(
equal(da, send_amt));
1600 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1614 testPathfind([](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1615 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(domainID), txflags(
tfHybrid));
1616 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1617 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1623 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1632 testPathfind([](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1633 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1634 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1635 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(domainID), txflags(
tfHybrid));
1638 testPathfind([](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1639 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1640 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(domainID), txflags(
tfHybrid));
1641 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(domainID), txflags(
tfHybrid));
1658 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1659 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(domainID), txflags(
tfHybrid));
1660 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(domainID), txflags(
tfHybrid));
1661 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(domainID));
1666 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1667 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(domainID));
1668 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(domainID));
1669 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(domainID), txflags(
tfHybrid));
1674 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1675 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(domainID));
1676 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(domainID), txflags(
tfHybrid));
1677 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(domainID), txflags(
tfHybrid));
1686 testcase(
"AMM not used in domain path");
1687 using namespace jtx;
1689 PermissionedDEX permDex(env);
1690 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = permDex;
1696 auto const& send_amt =
XRP(1);
1700 BEAST_EXPECT(st.empty());
1723 for (
bool const domainEnabled : {
false,
true})
Unserialize a JSON document into a Value.
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
const_iterator begin() const
const_iterator end() const
bool isMember(char const *key) const
Return true if the object has a member named key.
testcase_t testcase
Memberspace for declaring test cases.
An endpoint that consumes resources.
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Holds the serialized result of parsing an input JSON object.
std::optional< STObject > object
The STObject if the parse was successful.
bool wait_for(std::chrono::duration< Rep, Period > const &rel_time)
std::condition_variable cv_
void path_find_consume_all(bool const domainEnabled)
void xrp_to_xrp(bool const domainEnabled)
void run() override
Runs the suite.
void direct_path_no_intermediary()
void indirect_paths_path_find()
void trust_auto_clear_trust_normal_clear()
auto find_paths_request(jtx::Env &env, jtx::Account const &src, jtx::Account const &dst, STAmount const &saDstAmount, std::optional< STAmount > const &saSendMax=std::nullopt, std::optional< Currency > const &saSrcCurrency=std::nullopt, std::optional< uint256 > const &domain=std::nullopt)
void noripple_combinations()
void payment_auto_path_find()
void source_currencies_limit()
void alternative_paths_consume_best_transfer(bool const domainEnabled)
void via_offers_via_gateway(bool const domainEnabled)
void quality_paths_quality_set_and_test()
void trust_auto_clear_trust_auto_clear()
void alternative_paths_consume_best_transfer_first()
void path_find_05(bool const domainEnabled)
void issues_path_negative_ripple_client_issue_23_larger()
void path_find_06(bool const domainEnabled)
void path_find_04(bool const domainEnabled)
void issues_path_negative_ripple_client_issue_23_smaller()
void path_find_01(bool const domainEnabled)
void path_find_02(bool const domainEnabled)
std::tuple< STPathSet, STAmount, STAmount > find_paths(jtx::Env &env, jtx::Account const &src, jtx::Account const &dst, STAmount const &saDstAmount, std::optional< STAmount > const &saSendMax=std::nullopt, std::optional< Currency > const &saSrcCurrency=std::nullopt, std::optional< uint256 > const &domain=std::nullopt)
void path_find(bool const domainEnabled)
void alternative_paths_limit_returned_paths_to_best_quality(bool const domainEnabled)
void no_direct_path_no_intermediary_no_alternatives()
void receive_max(bool const domainEnabled)
void alternative_path_consume_both(bool const domainEnabled)
void issues_path_negative_issue(bool const domainEnabled)
Immutable cryptographic account descriptor.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
beast::Journal const journal
void require(Args const &... args)
Check a set of requirements.
Set Paths, SendMax on a JTx.
Sets the SendMax on a JTx.
Set the expected result code for a JTx The test will fail if the code doesn't match.
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
static int constexpr max_auto_src_cur
Maximum number of auto source currencies in a path find request.
static int constexpr max_src_cur
Maximum number of source currencies allowed in a path find request.
Status doCommand(RPC::JsonContext &context, Json::Value &result)
Execute an RPC command and store the results in a Json::Value.
static constexpr auto apiVersionIfUnspecified
Charge const feeReferenceRPC
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
bool same(STPathSet const &st1, Args const &... args)
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
XRP_t const XRP
Converts to XRP Issue or STAmount.
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
uint256 setupDomain(jtx::Env &env, std::vector< jtx::Account > const &accounts, jtx::Account const &domainOwner, std::string const &credType)
STPathElement IPE(Issue const &iss)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
STPath stpath(Args const &... args)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
bool equal(std::unique_ptr< Step > const &s1, DirectStepInfo const &dsi)
Json::Value rpf(jtx::Account const &src, jtx::Account const &dst, std::uint32_t num_src)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
STAmount amountFromJson(SField const &name, Json::Value const &v)
std::string to_string(base_uint< Bits, Tag > const &a)
constexpr std::uint32_t tfHybrid
constexpr std::uint32_t tfSetNoRipple
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Currency const & xrpCurrency()
XRP currency.
constexpr std::uint32_t tfClearNoRipple
AccountID const & xrpAccount()
Compute AccountID from public key.