21#include <test/jtx/AMM.h>
22#include <test/jtx/AMMTest.h>
23#include <test/jtx/envconfig.h>
24#include <test/jtx/permissioned_dex.h>
26#include <xrpld/core/JobQueue.h>
27#include <xrpld/rpc/RPCHandler.h>
28#include <xrpld/rpc/detail/RPCHelpers.h>
29#include <xrpld/rpc/detail/Tuning.h>
31#include <xrpl/beast/unit_test.h>
32#include <xrpl/json/json_reader.h>
33#include <xrpl/protocol/STParsedJSON.h>
34#include <xrpl/protocol/TxFlags.h>
35#include <xrpl/protocol/jss.h>
36#include <xrpl/resource/Fees.h>
54 jv[jss::command] =
"ripple_path_find";
55 jv[jss::source_account] =
toBase58(src);
69 jv[jss::destination_account] = d;
72 j[jss::currency] =
"USD";
73 j[jss::value] =
"0.01";
91 cfg->PATH_SEARCH_OLD = 7;
93 cfg->PATH_SEARCH_MAX = 10;
109 template <
class Rep,
class Period>
140 auto& app = env.
app();
149 app.getLedgerMaster(),
159 params[jss::command] =
"ripple_path_find";
160 params[jss::source_account] =
toBase58(src);
161 params[jss::destination_account] =
toBase58(dst);
162 params[jss::destination_amount] =
170 j[jss::currency] =
to_string(saSrcCurrency.value());
178 app.getJobQueue().postCoro(
179 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
180 context.params = std::move(params);
186 using namespace std::chrono_literals;
188 BEAST_EXPECT(!result.
isMember(jss::error));
203 env, src, dst, saDstAmount, saSendMax, saSrcCurrency,
domain);
204 BEAST_EXPECT(!result.
isMember(jss::error));
207 if (result.
isMember(jss::destination_amount))
212 if (result.
isMember(jss::alternatives))
214 auto const& alts = result[jss::alternatives];
217 auto const&
path = alts[0u];
219 if (
path.isMember(jss::source_amount))
222 if (
path.isMember(jss::destination_amount))
226 if (
path.isMember(jss::paths_computed))
229 p[
"Paths"] =
path[jss::paths_computed];
243 using namespace std::chrono_literals;
246 auto const gw =
Account(
"gateway");
247 env.
fund(
XRP(10000),
"alice",
"bob", gw);
249 env.
trust(gw[
"USD"](100),
"alice",
"bob");
252 auto& app = env.
app();
261 app.getLedgerMaster(),
272 app.getJobQueue().postCoro(
273 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
274 context.params =
rpf(
281 BEAST_EXPECT(!result.
isMember(jss::error));
284 app.getJobQueue().postCoro(
285 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
295 BEAST_EXPECT(result.
isMember(jss::error));
300 app.getJobQueue().postCoro(
301 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
308 BEAST_EXPECT(!result.
isMember(jss::error));
312 app.getJobQueue().postCoro(
313 jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
320 BEAST_EXPECT(result.
isMember(jss::error));
326 testcase(
"no direct path no intermediary no alternatives");
329 env.
fund(
XRP(10000),
"alice",
"bob");
340 testcase(
"direct path no intermediary");
343 env.
fund(
XRP(10000),
"alice",
"bob");
351 BEAST_EXPECT(st.
empty());
361 auto const gw =
Account(
"gateway");
362 auto const USD = gw[
"USD"];
363 env.
fund(
XRP(10000),
"alice",
"bob", gw);
365 env.
trust(USD(600),
"alice");
366 env.
trust(USD(700),
"bob");
367 env(
pay(gw,
"alice", USD(70)));
368 env(
pay(
"alice",
"bob", USD(24)));
379 std::string(
"path find") + (domainEnabled ?
" w/ " :
" w/o ") +
383 auto const gw =
Account(
"gateway");
384 auto const USD = gw[
"USD"];
385 env.
fund(
XRP(10000),
"alice",
"bob", gw);
387 env.
trust(USD(600),
"alice");
388 env.
trust(USD(700),
"bob");
389 env(
pay(gw,
"alice", USD(70)));
390 env(
pay(gw,
"bob", USD(50)));
415 std::string(
"XRP to XRP") + (domainEnabled ?
" w/ " :
" w/o ") +
418 env.
fund(
XRP(10000),
"alice",
"bob");
435 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
440 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
451 env, {
"alice",
"bob",
"carol",
"dan",
"edward"});
471 auto const gw =
Account(
"gateway");
472 auto const USD = gw[
"USD"];
473 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
475 env.
trust(USD(100),
"bob",
"carol");
477 env(
pay(gw,
"carol", USD(100)));
484 setupDomain(env, {
"alice",
"bob",
"carol",
"gateway"});
489 env(
offer(
"carol",
XRP(100), USD(100)));
504 BEAST_EXPECT(st.
empty());
513 BEAST_EXPECT(sa ==
XRP(100));
528 BEAST_EXPECT(st.
empty());
538 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
541 auto const gw =
Account(
"gateway");
542 auto const USD = gw[
"USD"];
543 auto const gw2 =
Account(
"gateway2");
544 auto const gw2_USD = gw2[
"USD"];
545 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
547 env.
trust(USD(600),
"alice");
548 env.
trust(gw2_USD(800),
"alice");
549 env.
trust(USD(700),
"bob");
550 env.
trust(gw2_USD(900),
"bob");
556 setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
557 env(
pay(gw,
"alice", USD(70)),
domain(*domainID));
558 env(
pay(gw2,
"alice", gw2_USD(70)),
domain(*domainID));
559 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
565 env(
pay(gw,
"alice", USD(70)));
566 env(
pay(gw2,
"alice", gw2_USD(70)));
567 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
585 std::string(
"alternative paths consume best transfer") +
586 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
589 auto const gw =
Account(
"gateway");
590 auto const USD = gw[
"USD"];
591 auto const gw2 =
Account(
"gateway2");
592 auto const gw2_USD = gw2[
"USD"];
593 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
596 env.
trust(USD(600),
"alice");
597 env.
trust(gw2_USD(800),
"alice");
598 env.
trust(USD(700),
"bob");
599 env.
trust(gw2_USD(900),
"bob");
605 setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
606 env(
pay(gw,
"alice", USD(70)),
domain(*domainID));
607 env(
pay(gw2,
"alice", gw2_USD(70)),
domain(*domainID));
608 env(
pay(
"alice",
"bob", USD(70)),
domain(*domainID));
612 env(
pay(gw,
"alice", USD(70)));
613 env(
pay(gw2,
"alice", gw2_USD(70)));
614 env(
pay(
"alice",
"bob", USD(70)));
629 testcase(
"alternative paths - consume best transfer first");
632 auto const gw =
Account(
"gateway");
633 auto const USD = gw[
"USD"];
634 auto const gw2 =
Account(
"gateway2");
635 auto const gw2_USD = gw2[
"USD"];
636 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
639 env.
trust(USD(600),
"alice");
640 env.
trust(gw2_USD(800),
"alice");
641 env.
trust(USD(700),
"bob");
642 env.
trust(gw2_USD(900),
"bob");
643 env(
pay(gw,
"alice", USD(70)));
644 env(
pay(gw2,
"alice", gw2_USD(70)));
645 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
660 bool const domainEnabled)
664 "alternative paths - limit returned paths to best quality") +
665 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
668 auto const gw =
Account(
"gateway");
669 auto const USD = gw[
"USD"];
670 auto const gw2 =
Account(
"gateway2");
671 auto const gw2_USD = gw2[
"USD"];
672 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
674 env(
rate(
"carol", 1.1));
675 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
677 env.
trust(USD(800),
"alice",
"bob");
678 env.
trust(gw2_USD(800),
"alice",
"bob");
682 env(
pay(gw2,
"alice", gw2_USD(100)));
684 env(
pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
686 env(
pay(gw,
"alice", USD(100)));
693 setupDomain(env, {
"alice",
"bob",
"carol",
"dan", gw, gw2});
720 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
723 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
725 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
728 env(
pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
736 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan"});
780 testcase(
"path negative: ripple-client issue #23: smaller");
783 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
789 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](55)),
800 testcase(
"path negative: ripple-client issue #23: larger");
803 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
810 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](50)),
829 std::string(
"via gateway") + (domainEnabled ?
" w/ " :
" w/o ") +
833 auto const gw =
Account(
"gateway");
834 auto const AUD = gw[
"AUD"];
835 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
839 env.
trust(AUD(100),
"bob",
"carol");
841 env(
pay(gw,
"carol", AUD(50)));
847 domainID =
setupDomain(env, {
"alice",
"bob",
"carol", gw});
850 env(
pay(
"alice",
"bob", AUD(10)),
858 env(
offer(
"carol",
XRP(50), AUD(50)));
884 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
903 env.
fund(
XRP(10000),
"alice",
"bob");
906 json(
"{\"" + sfQualityIn.fieldName +
"\": 2000}"),
907 json(
"{\"" + sfQualityOut.fieldName +
"\": 1400000000}"));
914 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
920 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
924 "HighQualityIn" : 2000,
925 "HighQualityOut" : 1400000000,
926 "LedgerEntryType" : "RippleState",
929 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
940 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
941 BEAST_EXPECT(*it == jv_l[it.memberName()]);
950 env.
fund(
XRP(10000),
"alice",
"bob");
960 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
966 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
970 "LedgerEntryType" : "RippleState",
973 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
984 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
985 BEAST_EXPECT(*it == jv_l[it.memberName()]);
1001 env.
fund(
XRP(10000),
"alice",
"bob");
1004 env(
pay(
"bob",
"alice",
Account(
"bob")[
"USD"](50)));
1013 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
1020 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
1024 "LedgerEntryType" : "RippleState",
1028 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
1039 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
1040 BEAST_EXPECT(*it == jv_l[it.memberName()]);
1042 env(
pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
1053 std::string(
"Path Find: XRP -> XRP and XRP -> IOU") +
1054 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1055 using namespace jtx;
1065 env.fund(
XRP(100000), A1);
1066 env.fund(
XRP(10000), A2);
1067 env.fund(
XRP(1000), A3, G1, G2, G3, M1);
1070 env.trust(G1[
"XYZ"](5000), A1);
1071 env.trust(G3[
"ABC"](5000), A1);
1072 env.trust(G2[
"XYZ"](5000), A2);
1073 env.trust(G3[
"ABC"](5000), A2);
1074 env.trust(A2[
"ABC"](1000), A3);
1075 env.trust(G1[
"XYZ"](100000), M1);
1076 env.trust(G2[
"XYZ"](100000), M1);
1077 env.trust(G3[
"ABC"](100000), M1);
1080 env(
pay(G1, A1, G1[
"XYZ"](3500)));
1081 env(
pay(G3, A1, G3[
"ABC"](1200)));
1082 env(
pay(G2, M1, G2[
"XYZ"](25000)));
1083 env(
pay(G3, M1, G3[
"ABC"](25000)));
1089 domainID =
setupDomain(env, {A1, A2, A3, G1, G2, G3, M1});
1090 env(
offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)), domain(*domainID));
1091 env(
offer(M1,
XRP(10000), G3[
"ABC"](1000)), domain(*domainID));
1096 env(
offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)));
1097 env(
offer(M1,
XRP(10000), G3[
"ABC"](1000)));
1105 auto const& send_amt =
XRP(10);
1108 BEAST_EXPECT(
equal(da, send_amt));
1109 BEAST_EXPECT(st.
empty());
1115 auto const& send_amt =
XRP(200);
1124 BEAST_EXPECT(
equal(da, send_amt));
1125 BEAST_EXPECT(st.
empty());
1129 auto const& send_amt = G3[
"ABC"](10);
1132 BEAST_EXPECT(
equal(da, send_amt));
1138 auto const& send_amt = A2[
"ABC"](1);
1141 BEAST_EXPECT(
equal(da, send_amt));
1147 auto const& send_amt = A3[
"ABC"](1);
1150 BEAST_EXPECT(
equal(da, send_amt));
1161 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1162 using namespace jtx;
1169 env.
fund(
XRP(1000), A1, A2, G3);
1173 env.
trust(G3[
"ABC"](1000), A1, A2);
1174 env.
trust(G3[
"ABC"](100000), M1);
1177 env(
pay(G3, A1, G3[
"ABC"](1000)));
1178 env(
pay(G3, A2, G3[
"ABC"](1000)));
1179 env(
pay(G3, M1, G3[
"ABC"](1200)));
1190 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)));
1195 auto const& send_amt =
XRP(10);
1206 BEAST_EXPECT(
equal(da, send_amt));
1207 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
1216 env, A1, A2, send_amt,
std::nullopt, A2[
"ABC"].currency);
1217 BEAST_EXPECT(
equal(da, send_amt));
1218 BEAST_EXPECT(st.
empty());
1227 "Path Find: Bitstamp and SnapSwap, liquidity with no offers") +
1228 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1229 using namespace jtx;
1233 Account G1BS{
"G1BS"};
1234 Account G2SW{
"G2SW"};
1237 env.
fund(
XRP(1000), G1BS, G2SW, A1, A2);
1241 env.
trust(G1BS[
"HKD"](2000), A1);
1242 env.
trust(G2SW[
"HKD"](2000), A2);
1243 env.
trust(G1BS[
"HKD"](100000), M1);
1244 env.
trust(G2SW[
"HKD"](100000), M1);
1247 env(
pay(G1BS, A1, G1BS[
"HKD"](1000)));
1248 env(
pay(G2SW, A2, G2SW[
"HKD"](1000)));
1252 env(
pay(G1BS, M1, G1BS[
"HKD"](1200)));
1253 env(
pay(G2SW, M1, G2SW[
"HKD"](5000)));
1258 domainID =
setupDomain(env, {A1, A2, G1BS, G2SW, M1});
1264 auto const& send_amt = A2[
"HKD"](10);
1273 BEAST_EXPECT(
equal(da, send_amt));
1274 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1279 auto const& send_amt = A1[
"HKD"](10);
1288 BEAST_EXPECT(
equal(da, send_amt));
1289 BEAST_EXPECT(
equal(sa, A2[
"HKD"](10)));
1290 BEAST_EXPECT(
same(st,
stpath(G2SW, M1, G1BS)));
1294 auto const& send_amt = A2[
"HKD"](10);
1303 BEAST_EXPECT(
equal(da, send_amt));
1304 BEAST_EXPECT(
equal(sa, G1BS[
"HKD"](10)));
1309 auto const& send_amt = M1[
"HKD"](10);
1318 BEAST_EXPECT(
equal(da, send_amt));
1319 BEAST_EXPECT(
equal(sa, M1[
"HKD"](10)));
1320 BEAST_EXPECT(st.empty());
1324 auto const& send_amt = A1[
"HKD"](10);
1333 BEAST_EXPECT(
equal(da, send_amt));
1334 BEAST_EXPECT(
equal(sa, G2SW[
"HKD"](10)));
1343 std::string(
"Path Find: non-XRP -> non-XRP, same currency") +
1344 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1345 using namespace jtx;
1358 env.fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1359 env.fund(
XRP(10000), A4);
1360 env.fund(
XRP(11000), M1, M2);
1363 env.trust(G1[
"HKD"](2000), A1);
1364 env.trust(G2[
"HKD"](2000), A2);
1365 env.trust(G1[
"HKD"](2000), A3);
1366 env.trust(G1[
"HKD"](100000), M1);
1367 env.trust(G2[
"HKD"](100000), M1);
1368 env.trust(G1[
"HKD"](100000), M2);
1369 env.trust(G2[
"HKD"](100000), M2);
1372 env(
pay(G1, A1, G1[
"HKD"](1000)));
1373 env(
pay(G2, A2, G2[
"HKD"](1000)));
1374 env(
pay(G1, A3, G1[
"HKD"](1000)));
1375 env(
pay(G1, M1, G1[
"HKD"](1200)));
1376 env(
pay(G2, M1, G2[
"HKD"](5000)));
1377 env(
pay(G1, M2, G1[
"HKD"](1200)));
1378 env(
pay(G2, M2, G2[
"HKD"](5000)));
1385 setupDomain(env, {A1, A2, A3, A4, G1, G2, G3, G4, M1, M2});
1386 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(*domainID));
1387 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(*domainID));
1388 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(*domainID));
1392 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1393 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1394 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1403 auto const& send_amt = G1[
"HKD"](10);
1412 BEAST_EXPECT(st.empty());
1413 BEAST_EXPECT(
equal(da, send_amt));
1414 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1420 auto const& send_amt = A1[
"HKD"](10);
1429 BEAST_EXPECT(st.empty());
1430 BEAST_EXPECT(
equal(da, send_amt));
1431 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1437 auto const& send_amt = A3[
"HKD"](10);
1446 BEAST_EXPECT(
equal(da, send_amt));
1447 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1454 auto const& send_amt = G2[
"HKD"](10);
1463 BEAST_EXPECT(
equal(da, send_amt));
1464 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1476 auto const& send_amt = G2[
"HKD"](10);
1485 BEAST_EXPECT(
equal(da, send_amt));
1486 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1499 auto const& send_amt = A2[
"HKD"](10);
1508 BEAST_EXPECT(
equal(da, send_amt));
1509 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1523 std::string(
"Path Find: non-XRP -> non-XRP, same currency)") +
1524 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1525 using namespace jtx;
1534 env.fund(
XRP(11000), M1);
1535 env.fund(
XRP(1000), A1, A2, A3, G1, G2);
1538 env.trust(G1[
"HKD"](2000), A1);
1539 env.trust(G2[
"HKD"](2000), A2);
1540 env.trust(A2[
"HKD"](2000), A3);
1541 env.trust(G1[
"HKD"](100000), M1);
1542 env.trust(G2[
"HKD"](100000), M1);
1545 env(
pay(G1, A1, G1[
"HKD"](1000)));
1546 env(
pay(G2, A2, G2[
"HKD"](1000)));
1547 env(
pay(G1, M1, G1[
"HKD"](5000)));
1548 env(
pay(G2, M1, G2[
"HKD"](5000)));
1554 domainID =
setupDomain(env, {A1, A2, A3, G1, G2, M1});
1555 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(*domainID));
1559 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1564 auto const& send_amt = A2[
"HKD"](10);
1568 env, G1, A2, send_amt,
std::nullopt, G1[
"HKD"].currency, domainID);
1569 BEAST_EXPECT(
equal(da, send_amt));
1570 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1578 std::string(
"Receive max") + (domainEnabled ?
" w/ " :
" w/o ") +
1581 using namespace jtx;
1582 auto const alice = Account(
"alice");
1583 auto const bob = Account(
"bob");
1584 auto const charlie = Account(
"charlie");
1585 auto const gw = Account(
"gw");
1586 auto const USD = gw[
"USD"];
1590 env.fund(
XRP(10000), alice, bob, charlie, gw);
1592 env.trust(USD(100), alice, bob, charlie);
1594 env(
pay(gw, charlie, USD(10)));
1600 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1601 env(
offer(charlie,
XRP(10), USD(10)), domain(*domainID));
1606 env(
offer(charlie,
XRP(10), USD(10)));
1618 BEAST_EXPECT(sa ==
XRP(10));
1619 BEAST_EXPECT(
equal(da, USD(10)));
1620 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1622 auto const& pathElem = st[0][0];
1624 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
1625 pathElem.getCurrency() == USD.currency);
1631 env.fund(
XRP(10000), alice, bob, charlie, gw);
1633 env.trust(USD(100), alice, bob, charlie);
1635 env(
pay(gw, alice, USD(10)));
1641 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1642 env(
offer(charlie, USD(10),
XRP(10)), domain(*domainID));
1647 env(
offer(charlie, USD(10),
XRP(10)));
1659 BEAST_EXPECT(sa == USD(10));
1661 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1663 auto const& pathElem = st[0][0];
1665 pathElem.isOffer() &&
1675 using namespace jtx;
1680 auto const alice =
Account(
"alice");
1681 auto const bob =
Account(
"bob");
1682 auto const george =
Account(
"george");
1683 auto const USD = george[
"USD"];
1710 env(
pay(george, alice, USD(70)));
1715 BEAST_EXPECT(
equal(da, bob[
"USD"](5)));
1719 BEAST_EXPECT(st.size() == 1);
1721 BEAST_EXPECT(
equal(sa, alice[
"USD"](5)));
1725 BEAST_EXPECT(st.size() == 0);
1729 test(
"ripple -> ripple",
true,
true,
true);
1730 test(
"ripple -> no ripple",
true,
false,
true);
1731 test(
"no ripple -> ripple",
false,
true,
true);
1732 test(
"no ripple -> no ripple",
false,
false,
false);
1739 using namespace jtx;
1744 auto testPathfind = [&](
auto func,
bool const domainEnabled =
false) {
1757 env.
fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1762 env.
trust(G1[
"HKD"](2000), A1);
1763 env.
trust(G2[
"HKD"](2000), A2);
1764 env.
trust(G1[
"HKD"](2000), A3);
1765 env.
trust(G1[
"HKD"](100000), M1);
1766 env.
trust(G2[
"HKD"](100000), M1);
1767 env.
trust(G1[
"HKD"](100000), M2);
1768 env.
trust(G2[
"HKD"](100000), M2);
1771 env(
pay(G1, A1, G1[
"HKD"](1000)));
1772 env(
pay(G2, A2, G2[
"HKD"](1000)));
1773 env(
pay(G1, A3, G1[
"HKD"](1000)));
1774 env(
pay(G1, M1, G1[
"HKD"](1200)));
1775 env(
pay(G2, M1, G2[
"HKD"](5000)));
1776 env(
pay(G1, M2, G1[
"HKD"](1200)));
1777 env(
pay(G2, M2, G2[
"HKD"](5000)));
1781 setupDomain(env, {A1, A2, A3, A4, G1, G2, G3, G4, M1, M2});
1782 BEAST_EXPECT(domainID);
1784 func(env, M1, M2, G1, G2, *domainID);
1792 auto const& send_amt = G1[
"HKD"](10);
1800 domainEnabled ? domainID :
std::nullopt);
1801 BEAST_EXPECT(st.empty());
1802 BEAST_EXPECT(
equal(da, send_amt));
1803 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1809 auto const& send_amt = A1[
"HKD"](10);
1817 domainEnabled ? domainID :
std::nullopt);
1818 BEAST_EXPECT(st.empty());
1819 BEAST_EXPECT(
equal(da, send_amt));
1820 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1826 auto const& send_amt = A3[
"HKD"](10);
1834 domainEnabled ? domainID :
std::nullopt);
1835 BEAST_EXPECT(
equal(da, send_amt));
1836 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1843 auto const& send_amt = G2[
"HKD"](10);
1851 domainEnabled ? domainID :
std::nullopt);
1852 BEAST_EXPECT(
equal(da, send_amt));
1853 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1865 auto const& send_amt = G2[
"HKD"](10);
1873 domainEnabled ? domainID :
std::nullopt);
1874 BEAST_EXPECT(
equal(da, send_amt));
1875 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1888 auto const& send_amt = A2[
"HKD"](10);
1896 domainEnabled ? domainID :
std::nullopt);
1897 BEAST_EXPECT(
equal(da, send_amt));
1898 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1912 testPathfind([](Env& env,
1918 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
1921 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1922 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1925 testPathfind([](Env& env,
1931 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
1934 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
1937 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1940 testPathfind([](Env& env,
1946 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
1949 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
1952 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
1957 testPathfind([](Env& env,
1963 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1964 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1965 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
1970 testPathfind([](Env& env,
1976 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1977 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
1980 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
1997 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
2000 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
2002 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
2014 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
2017 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
2020 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
2032 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
2034 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
2036 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
2049 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
2051 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
2054 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
2065 testcase(
"AMM not used in domain path");
2066 using namespace jtx;
2068 PermissionedDEX permDex(env);
2069 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
2076 auto const& send_amt =
XRP(1);
2080 env, bob, carol, send_amt,
std::nullopt, USD.currency, domainID);
2081 BEAST_EXPECT(st.empty());
2105 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.
std::condition_variable cv_
bool wait_for(std::chrono::duration< Rep, Period > const &rel_time)
void noripple_combinations()
void path_find_consume_all(bool const domainEnabled)
void trust_auto_clear_trust_normal_clear()
void alternative_path_consume_both(bool const domainEnabled)
void payment_auto_path_find()
void indirect_paths_path_find()
void via_offers_via_gateway(bool const domainEnabled)
void issues_path_negative_issue(bool const domainEnabled)
void trust_auto_clear_trust_auto_clear()
void direct_path_no_intermediary()
void receive_max(bool const domainEnabled)
void path_find_02(bool const domainEnabled)
void issues_path_negative_ripple_client_issue_23_larger()
void alternative_paths_consume_best_transfer(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 alternative_paths_limit_returned_paths_to_best_quality(bool const domainEnabled)
void source_currencies_limit()
void path_find_04(bool const domainEnabled)
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 issues_path_negative_ripple_client_issue_23_smaller()
void alternative_paths_consume_best_transfer_first()
void path_find_05(bool const domainEnabled)
void path_find_01(bool const domainEnabled)
void path_find_06(bool const domainEnabled)
void quality_paths_quality_set_and_test()
void xrp_to_xrp(bool const domainEnabled)
void run() override
Runs the suite.
void path_find(bool const domainEnabled)
void no_direct_path_no_intermediary_no_alternatives()
Immutable cryptographic account descriptor.
A transaction testing environment.
void require(Args const &... args)
Check a set of requirements.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
beast::Journal const journal
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
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_src_cur
Maximum number of source currencies allowed in a path find request.
static int constexpr max_auto_src_cur
Maximum number of auto source currencies 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.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
uint256 setupDomain(jtx::Env &env, std::vector< jtx::Account > const &accounts, jtx::Account const &domainOwner, std::string const &credType)
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
bool same(STPathSet const &st1, Args const &... args)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
STPathElement IPE(Issue const &iss)
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
XRP_t const XRP
Converts to XRP Issue or STAmount.
STPath stpath(Args const &... args)
Json::Value rpf(jtx::Account const &src, jtx::Account const &dst, std::uint32_t num_src)
bool equal(std::unique_ptr< Step > const &s1, DirectStepInfo const &dsi)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
AccountID const & xrpAccount()
Compute AccountID from public key.
STAmount amountFromJson(SField const &name, Json::Value const &v)
constexpr std::uint32_t tfHybrid
constexpr std::uint32_t tfClearNoRipple
Currency const & xrpCurrency()
XRP currency.
std::string to_string(base_uint< Bits, Tag > const &a)
constexpr std::uint32_t tfSetNoRipple