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);
42 while ((num_src--) != 0u)
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));
183 BEAST_EXPECT(!result.
isMember(jss::error));
186 if (result.
isMember(jss::destination_amount))
191 if (result.
isMember(jss::alternatives))
193 auto const& alts = result[jss::alternatives];
196 auto const&
path = alts[0u];
198 if (
path.isMember(jss::source_amount))
201 if (
path.isMember(jss::destination_amount))
204 if (
path.isMember(jss::paths_computed))
207 p[
"Paths"] =
path[jss::paths_computed];
223 using namespace std::chrono_literals;
226 auto const gw =
Account(
"gateway");
227 env.
fund(
XRP(10000),
"alice",
"bob", gw);
229 env.
trust(gw[
"USD"](100),
"alice",
"bob");
232 auto& app = env.
app();
241 app.getLedgerMaster(),
252 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
259 BEAST_EXPECT(!result.
isMember(jss::error));
262 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
269 BEAST_EXPECT(result.
isMember(jss::error));
274 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
281 BEAST_EXPECT(!result.
isMember(jss::error));
285 app.getJobQueue().postCoro(
jtCLIENT,
"RPC-Client", [&](
auto const& coro) {
292 BEAST_EXPECT(result.
isMember(jss::error));
298 testcase(
"no direct path no intermediary no alternatives");
301 env.
fund(
XRP(10000),
"alice",
"bob");
311 testcase(
"direct path no intermediary");
314 env.
fund(
XRP(10000),
"alice",
"bob");
321 BEAST_EXPECT(st.
empty());
331 auto const gw =
Account(
"gateway");
332 auto const USD = gw[
"USD"];
333 env.
fund(
XRP(10000),
"alice",
"bob", gw);
335 env.
trust(USD(600),
"alice");
336 env.
trust(USD(700),
"bob");
337 env(
pay(gw,
"alice", USD(70)));
338 env(
pay(
"alice",
"bob", USD(24)));
351 auto const gw =
Account(
"gateway");
352 auto const USD = gw[
"USD"];
353 env.
fund(
XRP(10000),
"alice",
"bob", gw);
355 env.
trust(USD(600),
"alice");
356 env.
trust(USD(700),
"bob");
357 env(
pay(gw,
"alice", USD(70)));
358 env(
pay(gw,
"bob", USD(50)));
378 env.
fund(
XRP(10000),
"alice",
"bob");
394 std::string(
"path find consume all") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
399 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
409 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan",
"edward"});
429 auto const gw =
Account(
"gateway");
430 auto const USD = gw[
"USD"];
431 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
433 env.
trust(USD(100),
"bob",
"carol");
435 env(
pay(gw,
"carol", USD(100)));
441 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"gateway"});
446 env(
offer(
"carol",
XRP(100), USD(100)));
461 BEAST_EXPECT(st.
empty());
470 BEAST_EXPECT(sa ==
XRP(100));
485 BEAST_EXPECT(st.
empty());
494 std::string(
"alternative path consume both") + (domainEnabled ?
" w/ " :
" w/o ") +
498 auto const gw =
Account(
"gateway");
499 auto const USD = gw[
"USD"];
500 auto const gw2 =
Account(
"gateway2");
501 auto const gw2_USD = gw2[
"USD"];
502 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
504 env.
trust(USD(600),
"alice");
505 env.
trust(gw2_USD(800),
"alice");
506 env.
trust(USD(700),
"bob");
507 env.
trust(gw2_USD(900),
"bob");
512 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
513 env(
pay(gw,
"alice", USD(70)),
domain(*domainID));
514 env(
pay(gw2,
"alice", gw2_USD(70)),
domain(*domainID));
515 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
521 env(
pay(gw,
"alice", USD(70)));
522 env(
pay(gw2,
"alice", gw2_USD(70)));
540 std::string(
"alternative paths consume best transfer") +
541 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
544 auto const gw =
Account(
"gateway");
545 auto const USD = gw[
"USD"];
546 auto const gw2 =
Account(
"gateway2");
547 auto const gw2_USD = gw2[
"USD"];
548 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
551 env.
trust(USD(600),
"alice");
552 env.
trust(gw2_USD(800),
"alice");
553 env.
trust(USD(700),
"bob");
554 env.
trust(gw2_USD(900),
"bob");
559 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
560 env(
pay(gw,
"alice", USD(70)),
domain(*domainID));
561 env(
pay(gw2,
"alice", gw2_USD(70)),
domain(*domainID));
562 env(
pay(
"alice",
"bob", USD(70)),
domain(*domainID));
566 env(
pay(gw,
"alice", USD(70)));
567 env(
pay(gw2,
"alice", gw2_USD(70)));
568 env(
pay(
"alice",
"bob", USD(70)));
583 testcase(
"alternative paths - consume best transfer first");
586 auto const gw =
Account(
"gateway");
587 auto const USD = gw[
"USD"];
588 auto const gw2 =
Account(
"gateway2");
589 auto const gw2_USD = gw2[
"USD"];
590 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
593 env.
trust(USD(600),
"alice");
594 env.
trust(gw2_USD(800),
"alice");
595 env.
trust(USD(700),
"bob");
596 env.
trust(gw2_USD(900),
"bob");
597 env(
pay(gw,
"alice", USD(70)));
598 env(
pay(gw2,
"alice", gw2_USD(70)));
599 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
616 std::string(
"alternative paths - limit returned paths to best quality") +
617 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
620 auto const gw =
Account(
"gateway");
621 auto const USD = gw[
"USD"];
622 auto const gw2 =
Account(
"gateway2");
623 auto const gw2_USD = gw2[
"USD"];
624 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
626 env(
rate(
"carol", 1.1));
627 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
629 env.
trust(USD(800),
"alice",
"bob");
630 env.
trust(gw2_USD(800),
"alice",
"bob");
634 env(
pay(gw2,
"alice", gw2_USD(100)));
636 env(
pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
638 env(
pay(gw,
"alice", USD(100)));
644 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan", gw, gw2});
660 std::string(
"path negative: Issue #5") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
663 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
665 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
668 env(
pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
676 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan"});
708 testcase(
"path negative: ripple-client issue #23: smaller");
711 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
727 testcase(
"path negative: ripple-client issue #23: larger");
730 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
757 auto const gw =
Account(
"gateway");
758 auto const AUD = gw[
"AUD"];
759 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
763 env.
trust(AUD(100),
"bob",
"carol");
765 env(
pay(gw,
"carol", AUD(50)));
771 domainID =
setupDomain(env, {
"alice",
"bob",
"carol", gw});
779 env(
offer(
"carol",
XRP(50), AUD(50)));
799 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
818 env.
fund(
XRP(10000),
"alice",
"bob");
821 json(
"{\"" + sfQualityIn.fieldName +
"\": 2000}"),
822 json(
"{\"" + sfQualityOut.fieldName +
"\": 1400000000}"));
829 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
835 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
839 "HighQualityIn" : 2000,
840 "HighQualityOut" : 1400000000,
841 "LedgerEntryType" : "RippleState",
844 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
853 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
854 BEAST_EXPECT(*it == jv_l[it.memberName()]);
873 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
879 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
883 "LedgerEntryType" : "RippleState",
886 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
895 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
896 BEAST_EXPECT(*it == jv_l[it.memberName()]);
910 env.
fund(
XRP(10000),
"alice",
"bob");
913 env(
pay(
"bob",
"alice",
Account(
"bob")[
"USD"](50)));
922 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
929 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
933 "LedgerEntryType" : "RippleState",
937 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
946 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
947 BEAST_EXPECT(*it == jv_l[it.memberName()]);
949 env(
pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
958 std::string(
"Path Find: XRP -> XRP and XRP -> IOU") +
959 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
962 Account
const A1{
"A1"};
963 Account
const A2{
"A2"};
964 Account
const A3{
"A3"};
965 Account
const G1{
"G1"};
966 Account
const G2{
"G2"};
967 Account
const G3{
"G3"};
968 Account
const M1{
"M1"};
970 env.fund(
XRP(100000), A1);
971 env.fund(
XRP(10000), A2);
972 env.fund(
XRP(1000), A3, G1, G2, G3, M1);
975 env.trust(G1[
"XYZ"](5000), A1);
976 env.trust(G3[
"ABC"](5000), A1);
977 env.trust(G2[
"XYZ"](5000), A2);
978 env.trust(G3[
"ABC"](5000), A2);
979 env.trust(A2[
"ABC"](1000), A3);
980 env.trust(G1[
"XYZ"](100000), M1);
981 env.trust(G2[
"XYZ"](100000), M1);
982 env.trust(G3[
"ABC"](100000), M1);
985 env(
pay(G1, A1, G1[
"XYZ"](3500)));
986 env(
pay(G3, A1, G3[
"ABC"](1200)));
987 env(
pay(G2, M1, G2[
"XYZ"](25000)));
988 env(
pay(G3, M1, G3[
"ABC"](25000)));
994 domainID =
setupDomain(env, {A1, A2, A3, G1, G2, G3, M1});
995 env(
offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)),
domain(*domainID));
1001 env(
offer(M1, G1[
"XYZ"](1000), G2[
"XYZ"](1000)));
1002 env(
offer(M1,
XRP(10000), G3[
"ABC"](1000)));
1010 auto const& send_amt =
XRP(10);
1013 BEAST_EXPECT(
equal(da, send_amt));
1014 BEAST_EXPECT(st.
empty());
1020 auto const& send_amt =
XRP(200);
1023 BEAST_EXPECT(
equal(da, send_amt));
1024 BEAST_EXPECT(st.
empty());
1028 auto const& send_amt = G3[
"ABC"](10);
1031 BEAST_EXPECT(
equal(da, send_amt));
1037 auto const& send_amt = A2[
"ABC"](1);
1040 BEAST_EXPECT(
equal(da, send_amt));
1046 auto const& send_amt = A3[
"ABC"](1);
1049 BEAST_EXPECT(
equal(da, send_amt));
1059 std::string(
"Path Find: non-XRP -> XRP") + (domainEnabled ?
" w/ " :
" w/o ") +
1061 using namespace jtx;
1063 Account
const A1{
"A1"};
1064 Account
const A2{
"A2"};
1065 Account
const G3{
"G3"};
1066 Account
const M1{
"M1"};
1068 env.fund(
XRP(1000), A1, A2, G3);
1069 env.fund(
XRP(11000), M1);
1072 env.trust(G3[
"ABC"](1000), A1, A2);
1073 env.trust(G3[
"ABC"](100000), M1);
1076 env(
pay(G3, A1, G3[
"ABC"](1000)));
1077 env(
pay(G3, A2, G3[
"ABC"](1000)));
1078 env(
pay(G3, M1, G3[
"ABC"](1200)));
1085 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)), domain(*domainID));
1089 env(
offer(M1, G3[
"ABC"](1000),
XRP(10000)));
1094 auto const& send_amt =
XRP(10);
1099 BEAST_EXPECT(
equal(da, send_amt));
1100 BEAST_EXPECT(
equal(sa, A1[
"ABC"](1)));
1110 BEAST_EXPECT(
equal(da, send_amt));
1111 BEAST_EXPECT(st.empty());
1119 std::string(
"Path Find: Bitstamp and SnapSwap, liquidity with no offers") +
1120 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1121 using namespace jtx;
1123 Account
const A1{
"A1"};
1124 Account
const A2{
"A2"};
1125 Account
const G1BS{
"G1BS"};
1126 Account
const G2SW{
"G2SW"};
1127 Account
const M1{
"M1"};
1129 env.fund(
XRP(1000), G1BS, G2SW, A1, A2);
1130 env.fund(
XRP(11000), M1);
1133 env.trust(G1BS[
"HKD"](2000), A1);
1134 env.trust(G2SW[
"HKD"](2000), A2);
1135 env.trust(G1BS[
"HKD"](100000), M1);
1136 env.trust(G2SW[
"HKD"](100000), M1);
1139 env(
pay(G1BS, A1, G1BS[
"HKD"](1000)));
1140 env(
pay(G2SW, A2, G2SW[
"HKD"](1000)));
1144 env(
pay(G1BS, M1, G1BS[
"HKD"](1200)));
1145 env(
pay(G2SW, M1, G2SW[
"HKD"](5000)));
1150 domainID =
setupDomain(env, {A1, A2, G1BS, G2SW, M1});
1156 auto const& send_amt = A2[
"HKD"](10);
1159 BEAST_EXPECT(
equal(da, send_amt));
1160 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1161 BEAST_EXPECT(
same(st,
stpath(G1BS, M1, G2SW)));
1165 auto const& send_amt = A1[
"HKD"](10);
1168 BEAST_EXPECT(
equal(da, send_amt));
1169 BEAST_EXPECT(
equal(sa, A2[
"HKD"](10)));
1170 BEAST_EXPECT(
same(st,
stpath(G2SW, M1, G1BS)));
1174 auto const& send_amt = A2[
"HKD"](10);
1177 BEAST_EXPECT(
equal(da, send_amt));
1178 BEAST_EXPECT(
equal(sa, G1BS[
"HKD"](10)));
1183 auto const& send_amt = M1[
"HKD"](10);
1186 BEAST_EXPECT(
equal(da, send_amt));
1187 BEAST_EXPECT(
equal(sa, M1[
"HKD"](10)));
1188 BEAST_EXPECT(st.empty());
1192 auto const& send_amt = A1[
"HKD"](10);
1195 BEAST_EXPECT(
equal(da, send_amt));
1196 BEAST_EXPECT(
equal(sa, G2SW[
"HKD"](10)));
1205 std::string(
"Path Find: non-XRP -> non-XRP, same currency") +
1206 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1207 using namespace jtx;
1209 Account
const A1{
"A1"};
1210 Account
const A2{
"A2"};
1211 Account
const A3{
"A3"};
1212 Account
const A4{
"A4"};
1213 Account
const G1{
"G1"};
1214 Account
const G2{
"G2"};
1215 Account
const G3{
"G3"};
1216 Account
const G4{
"G4"};
1217 Account
const M1{
"M1"};
1218 Account
const M2{
"M2"};
1220 env.fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1221 env.fund(
XRP(10000), A4);
1222 env.fund(
XRP(11000), M1, M2);
1225 env.trust(G1[
"HKD"](2000), A1);
1226 env.trust(G2[
"HKD"](2000), A2);
1227 env.trust(G1[
"HKD"](2000), A3);
1228 env.trust(G1[
"HKD"](100000), M1);
1229 env.trust(G2[
"HKD"](100000), M1);
1230 env.trust(G1[
"HKD"](100000), M2);
1231 env.trust(G2[
"HKD"](100000), M2);
1234 env(
pay(G1, A1, G1[
"HKD"](1000)));
1235 env(
pay(G2, A2, G2[
"HKD"](1000)));
1236 env(
pay(G1, A3, G1[
"HKD"](1000)));
1237 env(
pay(G1, M1, G1[
"HKD"](1200)));
1238 env(
pay(G2, M1, G2[
"HKD"](5000)));
1239 env(
pay(G1, M2, G1[
"HKD"](1200)));
1240 env(
pay(G2, M2, G2[
"HKD"](5000)));
1246 domainID =
setupDomain(env, {A1, A2, A3, A4, G1, G2, G3, G4, M1, M2});
1247 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(*domainID));
1248 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(*domainID));
1249 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(*domainID));
1253 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1254 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1255 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1264 auto const& send_amt = G1[
"HKD"](10);
1267 BEAST_EXPECT(st.empty());
1268 BEAST_EXPECT(
equal(da, send_amt));
1269 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1275 auto const& send_amt = A1[
"HKD"](10);
1278 BEAST_EXPECT(st.empty());
1279 BEAST_EXPECT(
equal(da, send_amt));
1286 auto const& send_amt = A3[
"HKD"](10);
1289 BEAST_EXPECT(
equal(da, send_amt));
1290 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1297 auto const& send_amt = G2[
"HKD"](10);
1300 BEAST_EXPECT(
equal(da, send_amt));
1301 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1313 auto const& send_amt = G2[
"HKD"](10);
1316 BEAST_EXPECT(
equal(da, send_amt));
1317 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1330 auto const& send_amt = A2[
"HKD"](10);
1333 BEAST_EXPECT(
equal(da, send_amt));
1334 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1348 std::string(
"Path Find: non-XRP -> non-XRP, same currency)") +
1349 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1350 using namespace jtx;
1352 Account
const A1{
"A1"};
1353 Account
const A2{
"A2"};
1354 Account
const A3{
"A3"};
1355 Account
const G1{
"G1"};
1356 Account
const G2{
"G2"};
1357 Account
const M1{
"M1"};
1359 env.fund(
XRP(11000), M1);
1360 env.fund(
XRP(1000), A1, A2, A3, G1, G2);
1363 env.trust(G1[
"HKD"](2000), A1);
1364 env.trust(G2[
"HKD"](2000), A2);
1365 env.trust(A2[
"HKD"](2000), A3);
1366 env.trust(G1[
"HKD"](100000), M1);
1367 env.trust(G2[
"HKD"](100000), M1);
1370 env(
pay(G1, A1, G1[
"HKD"](1000)));
1371 env(
pay(G2, A2, G2[
"HKD"](1000)));
1372 env(
pay(G1, M1, G1[
"HKD"](5000)));
1373 env(
pay(G2, M1, G2[
"HKD"](5000)));
1379 domainID =
setupDomain(env, {A1, A2, A3, G1, G2, M1});
1380 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(*domainID));
1384 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1389 auto const& send_amt = A2[
"HKD"](10);
1394 BEAST_EXPECT(
equal(da, send_amt));
1395 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1404 using namespace jtx;
1405 auto const alice = Account(
"alice");
1406 auto const bob = Account(
"bob");
1407 auto const charlie = Account(
"charlie");
1408 auto const gw = Account(
"gw");
1409 auto const USD = gw[
"USD"];
1413 env.fund(
XRP(10000), alice, bob, charlie, gw);
1415 env.trust(USD(100), alice, bob, charlie);
1417 env(
pay(gw, charlie, USD(10)));
1423 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1429 env(
offer(charlie,
XRP(10), USD(10)));
1435 BEAST_EXPECT(sa ==
XRP(10));
1436 BEAST_EXPECT(
equal(da, USD(10)));
1437 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1439 auto const& pathElem = st[0][0];
1441 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
1442 pathElem.getCurrency() == USD.currency);
1448 env.fund(
XRP(10000), alice, bob, charlie, gw);
1450 env.trust(USD(100), alice, bob, charlie);
1452 env(
pay(gw, alice, USD(10)));
1458 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1459 env(
offer(charlie, USD(10),
XRP(10)), domain(*domainID));
1464 env(
offer(charlie, USD(10),
XRP(10)));
1470 BEAST_EXPECT(sa == USD(10));
1472 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1474 auto const& pathElem = st[0][0];
1476 pathElem.isOffer() && pathElem.getIssuerID() ==
xrpAccount() &&
1485 using namespace jtx;
1490 auto const alice = Account(
"alice");
1491 auto const bob = Account(
"bob");
1492 auto const george = Account(
"george");
1493 auto const USD = george[
"USD"];
1494 auto test = [&](
std::string casename,
bool aliceRipple,
bool bobRipple,
bool expectPath) {
1498 env.fund(
XRP(10000),
noripple(alice, bob, george));
1502 env(
trust(alice, USD(100), aliceRipple ? tfClearNoRipple : tfSetNoRipple));
1503 env(
trust(george, alice[
"USD"](100), aliceRipple ? tfClearNoRipple : tfSetNoRipple));
1504 env(
trust(bob, USD(100), bobRipple ? tfClearNoRipple : tfSetNoRipple));
1505 env(
trust(george, bob[
"USD"](100), bobRipple ? tfClearNoRipple : tfSetNoRipple));
1507 env(
pay(george, alice, USD(70)));
1510 auto [st, sa, da] =
find_paths(env,
"alice",
"bob", Account(
"bob")[
"USD"](5));
1511 BEAST_EXPECT(
equal(da, bob[
"USD"](5)));
1515 BEAST_EXPECT(st.size() == 1);
1517 BEAST_EXPECT(
equal(sa, alice[
"USD"](5)));
1521 BEAST_EXPECT(st.empty());
1525 test(
"ripple -> ripple",
true,
true,
true);
1526 test(
"ripple -> no ripple",
true,
false,
true);
1527 test(
"no ripple -> ripple",
false,
true,
true);
1528 test(
"no ripple -> no ripple",
false,
false,
false);
1535 using namespace jtx;
1540 auto testPathfind = [&](
auto func,
bool const domainEnabled =
false) {
1542 Account
const A1{
"A1"};
1543 Account
const A2{
"A2"};
1544 Account
const A3{
"A3"};
1545 Account
const A4{
"A4"};
1546 Account
const G1{
"G1"};
1547 Account
const G2{
"G2"};
1548 Account
const G3{
"G3"};
1549 Account
const G4{
"G4"};
1550 Account
const M1{
"M1"};
1551 Account
const M2{
"M2"};
1553 env.fund(
XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1554 env.fund(
XRP(10000), A4);
1555 env.fund(
XRP(11000), M1, M2);
1558 env.trust(G1[
"HKD"](2000), A1);
1559 env.trust(G2[
"HKD"](2000), A2);
1560 env.trust(G1[
"HKD"](2000), A3);
1561 env.trust(G1[
"HKD"](100000), M1);
1562 env.trust(G2[
"HKD"](100000), M1);
1563 env.trust(G1[
"HKD"](100000), M2);
1564 env.trust(G2[
"HKD"](100000), M2);
1567 env(
pay(G1, A1, G1[
"HKD"](1000)));
1568 env(
pay(G2, A2, G2[
"HKD"](1000)));
1569 env(
pay(G1, A3, G1[
"HKD"](1000)));
1570 env(
pay(G1, M1, G1[
"HKD"](1200)));
1571 env(
pay(G2, M1, G2[
"HKD"](5000)));
1572 env(
pay(G1, M2, G1[
"HKD"](1200)));
1573 env(
pay(G2, M2, G2[
"HKD"](5000)));
1577 setupDomain(env, {A1, A2, A3, A4, G1, G2, G3, G4, M1, M2});
1578 BEAST_EXPECT(domainID);
1580 func(env, M1, M2, G1, G2, *domainID);
1588 auto const& send_amt = G1[
"HKD"](10);
1596 domainEnabled ? domainID :
std::nullopt);
1597 BEAST_EXPECT(st.empty());
1598 BEAST_EXPECT(
equal(da, send_amt));
1599 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1605 auto const& send_amt = A1[
"HKD"](10);
1613 domainEnabled ? domainID :
std::nullopt);
1614 BEAST_EXPECT(st.empty());
1615 BEAST_EXPECT(
equal(da, send_amt));
1616 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1622 auto const& send_amt = A3[
"HKD"](10);
1630 domainEnabled ? domainID :
std::nullopt);
1631 BEAST_EXPECT(
equal(da, send_amt));
1632 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1639 auto const& send_amt = G2[
"HKD"](10);
1647 domainEnabled ? domainID :
std::nullopt);
1648 BEAST_EXPECT(
equal(da, send_amt));
1649 BEAST_EXPECT(
equal(sa, G1[
"HKD"](10)));
1661 auto const& send_amt = G2[
"HKD"](10);
1669 domainEnabled ? domainID :
std::nullopt);
1670 BEAST_EXPECT(
equal(da, send_amt));
1671 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1684 auto const& send_amt = A2[
"HKD"](10);
1692 domainEnabled ? domainID :
std::nullopt);
1693 BEAST_EXPECT(
equal(da, send_amt));
1694 BEAST_EXPECT(
equal(sa, A1[
"HKD"](10)));
1709 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1710 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
1713 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1714 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1718 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1719 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
1722 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
1725 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)));
1729 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1730 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
1733 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
1736 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
1742 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1743 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1744 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)));
1745 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
1752 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)));
1753 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
1756 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
1767 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1768 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
1771 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(domainID));
1772 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)), domain(domainID));
1778 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)),
1781 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
1789 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1790 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(domainID));
1791 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)), domain(domainID));
1792 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
1799 [](Env& env, Account M1, Account M2, Account G1, Account G2,
uint256 domainID) {
1800 env(
offer(M1, G1[
"HKD"](1000), G2[
"HKD"](1000)), domain(domainID));
1801 env(
offer(M2,
XRP(10000), G2[
"HKD"](1000)),
1804 env(
offer(M2, G1[
"HKD"](1000),
XRP(10000)),
1815 testcase(
"AMM not used in domain path");
1816 using namespace jtx;
1818 PermissionedDEX
const permDex(env);
1819 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = permDex;
1820 AMM const amm(env, alice,
XRP(10), USD(50));
1825 auto const& send_amt =
XRP(1);
1830 BEAST_EXPECT(st.empty());
1853 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)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Currency const & xrpCurrency()
XRP currency.
AccountID const & xrpAccount()
Compute AccountID from public key.