1#include <test/jtx/AMM.h>
2#include <test/jtx/AMMTest.h>
3#include <test/jtx/Account.h>
4#include <test/jtx/Env.h>
5#include <test/jtx/TestHelpers.h>
6#include <test/jtx/amount.h>
7#include <test/jtx/balance.h>
8#include <test/jtx/domain.h>
9#include <test/jtx/envconfig.h>
10#include <test/jtx/jtx_json.h>
11#include <test/jtx/offer.h>
12#include <test/jtx/owners.h>
13#include <test/jtx/paths.h>
14#include <test/jtx/pay.h>
15#include <test/jtx/permissioned_dex.h>
16#include <test/jtx/rate.h>
17#include <test/jtx/sendmax.h>
18#include <test/jtx/ter.h>
19#include <test/jtx/trust.h>
20#include <test/jtx/txflags.h>
22#include <xrpld/core/Config.h>
23#include <xrpld/rpc/RPCHandler.h>
24#include <xrpld/rpc/Role.h>
25#include <xrpld/rpc/detail/Tuning.h>
27#include <xrpl/basics/base_uint.h>
28#include <xrpl/beast/unit_test/suite.h>
29#include <xrpl/core/Job.h>
30#include <xrpl/core/JobQueue.h>
31#include <xrpl/json/json_reader.h>
32#include <xrpl/json/json_value.h>
33#include <xrpl/protocol/AccountID.h>
34#include <xrpl/protocol/ApiVersion.h>
35#include <xrpl/protocol/Indexes.h>
36#include <xrpl/protocol/Issue.h>
37#include <xrpl/protocol/SField.h>
38#include <xrpl/protocol/STAmount.h>
39#include <xrpl/protocol/STParsedJSON.h>
40#include <xrpl/protocol/STPathSet.h>
41#include <xrpl/protocol/TER.h>
42#include <xrpl/protocol/TxFlags.h>
43#include <xrpl/protocol/UintTypes.h>
44#include <xrpl/protocol/jss.h>
45#include <xrpl/resource/Charge.h>
46#include <xrpl/resource/Consumer.h>
47#include <xrpl/resource/Fees.h>
67 jv[jss::command] =
"ripple_path_find";
68 jv[jss::source_account] =
toBase58(src);
74 while ((numSrc--) != 0u)
82 jv[jss::destination_account] = d;
85 j[jss::currency] =
"USD";
86 j[jss::value] =
"0.01";
104 cfg->pathSearchOld = 7;
106 cfg->pathSearchMax = 10;
122 template <
class Rep,
class Period>
127 auto b =
cv_.wait_for(lk, relTime, [
this] {
return signaled_; });
153 auto& app = env.
app();
160 .loadType = loadType,
161 .netOps = app.getOPs(),
162 .ledgerMaster = app.getLedgerMaster(),
172 params[jss::command] =
"ripple_path_find";
173 params[jss::source_account] =
toBase58(src);
174 params[jss::destination_account] =
toBase58(dst);
182 j[jss::currency] =
to_string(saSrcCurrency.value());
186 params[jss::domain] =
to_string(*domain);
190 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
191 context.
params = std::move(params);
197 using namespace std::chrono_literals;
199 BEAST_EXPECT(!result.
isMember(jss::error));
214 findPathsRequest(env, src, dst, saDstAmount, saSendMax, saSrcCurrency, domain);
215 BEAST_EXPECT(!result.
isMember(jss::error));
218 if (result.
isMember(jss::destination_amount))
223 if (result.
isMember(jss::alternatives))
225 auto const& alts = result[jss::alternatives];
228 auto const&
path = alts[0u];
230 if (
path.isMember(jss::source_amount))
233 if (
path.isMember(jss::destination_amount))
236 if (
path.isMember(jss::paths_computed))
239 p[
"Paths"] =
path[jss::paths_computed];
243 paths = po.
object->getFieldPathSet(sfPaths);
248 return std::make_tuple(std::move(paths), std::move(sa), std::move(da));
255 using namespace std::chrono_literals;
258 auto const gw =
Account(
"gateway");
259 env.
fund(
XRP(10000),
"alice",
"bob", gw);
261 env.
trust(gw[
"USD"](100),
"alice",
"bob");
264 auto& app = env.
app();
271 .loadType = loadType,
272 .netOps = app.getOPs(),
273 .ledgerMaster = app.getLedgerMaster(),
284 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
291 BEAST_EXPECT(!result.
isMember(jss::error));
294 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
301 BEAST_EXPECT(result.
isMember(jss::error));
306 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
313 BEAST_EXPECT(!result.
isMember(jss::error));
317 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
324 BEAST_EXPECT(result.
isMember(jss::error));
330 testcase(
"no direct path no intermediary no alternatives");
333 env.
fund(
XRP(10000),
"alice",
"bob");
336 auto const result =
findPaths(env,
"alice",
"bob",
Account(
"bob")[
"USD"](5));
337 BEAST_EXPECT(std::get<0>(result).empty());
343 testcase(
"direct path no intermediary");
346 env.
fund(
XRP(10000),
"alice",
"bob");
353 BEAST_EXPECT(st.
empty());
363 auto const gw =
Account(
"gateway");
364 auto const usd = gw[
"USD"];
365 env.
fund(
XRP(10000),
"alice",
"bob", gw);
367 env.
trust(usd(600),
"alice");
368 env.
trust(usd(700),
"bob");
369 env(
pay(gw,
"alice", usd(70)));
370 env(
pay(
"alice",
"bob", usd(24)));
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)));
399 env,
"alice",
"bob",
Account(
"bob")[
"USD"](5), std::nullopt, std::nullopt, domainID);
410 env.
fund(
XRP(10000),
"alice",
"bob");
418 findPaths(env,
"alice",
"bob",
XRP(5), std::nullopt, std::nullopt, domainID);
419 BEAST_EXPECT(std::get<0>(result).empty());
426 std::string(
"path find consume all") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
431 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
441 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan",
"edward"});
461 auto const gw =
Account(
"gateway");
462 auto const usd = gw[
"USD"];
463 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
465 env.
trust(usd(100),
"bob",
"carol");
467 env(
pay(gw,
"carol", usd(100)));
473 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"gateway"});
478 env(
offer(
"carol",
XRP(100), usd(100)));
493 BEAST_EXPECT(st.
empty());
502 BEAST_EXPECT(sa ==
XRP(100));
517 BEAST_EXPECT(st.
empty());
526 std::string(
"alternative path consume both") + (domainEnabled ?
" w/ " :
" w/o ") +
530 auto const gw =
Account(
"gateway");
531 auto const usd = gw[
"USD"];
532 auto const gw2 =
Account(
"gateway2");
533 auto const gw2Usd = gw2[
"USD"];
534 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
536 env.
trust(usd(600),
"alice");
537 env.
trust(gw2Usd(800),
"alice");
538 env.
trust(usd(700),
"bob");
539 env.
trust(gw2Usd(900),
"bob");
544 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
545 env(
pay(gw,
"alice", usd(70)),
Domain(*domainID));
546 env(
pay(gw2,
"alice", gw2Usd(70)),
Domain(*domainID));
547 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
553 env(
pay(gw,
"alice", usd(70)));
554 env(
pay(gw2,
"alice", gw2Usd(70)));
572 std::string(
"alternative paths consume best transfer") +
573 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
576 auto const gw =
Account(
"gateway");
577 auto const usd = gw[
"USD"];
578 auto const gw2 =
Account(
"gateway2");
579 auto const gw2Usd = gw2[
"USD"];
580 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
583 env.
trust(usd(600),
"alice");
584 env.
trust(gw2Usd(800),
"alice");
585 env.
trust(usd(700),
"bob");
586 env.
trust(gw2Usd(900),
"bob");
591 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
592 env(
pay(gw,
"alice", usd(70)),
Domain(*domainID));
593 env(
pay(gw2,
"alice", gw2Usd(70)),
Domain(*domainID));
594 env(
pay(
"alice",
"bob", usd(70)),
Domain(*domainID));
598 env(
pay(gw,
"alice", usd(70)));
599 env(
pay(gw2,
"alice", gw2Usd(70)));
600 env(
pay(
"alice",
"bob", usd(70)));
615 testcase(
"alternative paths - consume best transfer first");
618 auto const gw =
Account(
"gateway");
619 auto const usd = gw[
"USD"];
620 auto const gw2 =
Account(
"gateway2");
621 auto const gw2Usd = gw2[
"USD"];
622 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
625 env.
trust(usd(600),
"alice");
626 env.
trust(gw2Usd(800),
"alice");
627 env.
trust(usd(700),
"bob");
628 env.
trust(gw2Usd(900),
"bob");
629 env(
pay(gw,
"alice", usd(70)));
630 env(
pay(gw2,
"alice", gw2Usd(70)));
631 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
648 std::string(
"alternative paths - limit returned paths to best quality") +
649 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
652 auto const gw =
Account(
"gateway");
653 auto const usd = gw[
"USD"];
654 auto const gw2 =
Account(
"gateway2");
655 auto const gw2Usd = gw2[
"USD"];
656 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
658 env(
rate(
"carol", 1.1));
659 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
661 env.
trust(usd(800),
"alice",
"bob");
662 env.
trust(gw2Usd(800),
"alice",
"bob");
666 env(
pay(gw2,
"alice", gw2Usd(100)));
668 env(
pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
670 env(
pay(gw,
"alice", usd(100)));
676 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan", gw, gw2});
682 env,
"alice",
"bob",
Account(
"bob")[
"USD"](5), std::nullopt, std::nullopt, domainID);
692 std::string(
"path negative: Issue #5") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
695 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
697 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
700 env(
pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
708 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan"});
712 env,
"alice",
"bob",
Account(
"bob")[
"USD"](25), std::nullopt, std::nullopt, domainID);
713 BEAST_EXPECT(std::get<0>(result).empty());
719 env,
"alice",
"bob",
Account(
"alice")[
"USD"](25), std::nullopt, std::nullopt, domainID);
720 BEAST_EXPECT(std::get<0>(result).empty());
740 testcase(
"path negative: ripple-client issue #23: smaller");
743 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
759 testcase(
"path negative: ripple-client issue #23: larger");
762 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
789 auto const gw =
Account(
"gateway");
790 auto const aud = gw[
"AUD"];
791 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
795 env.
trust(aud(100),
"bob",
"carol");
797 env(
pay(gw,
"carol", aud(50)));
803 domainID =
setupDomain(env, {
"alice",
"bob",
"carol", gw});
811 env(
offer(
"carol",
XRP(50), aud(50)));
821 env,
"alice",
"bob",
Account(
"bob")[
"USD"](25), std::nullopt, std::nullopt, domainID);
822 BEAST_EXPECT(std::get<0>(result).empty());
831 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
850 env.
fund(
XRP(10000),
"alice",
"bob");
853 Json(
"{\"" + sfQualityIn.fieldName +
"\": 2000}"),
854 Json(
"{\"" + sfQualityOut.fieldName +
"\": 1400000000}"));
861 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
867 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
871 "HighQualityIn" : 2000,
872 "HighQualityOut" : 1400000000,
873 "LedgerEntryType" : "RippleState",
876 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
885 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
886 BEAST_EXPECT(*it == jvL[it.memberName()]);
905 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
911 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
915 "LedgerEntryType" : "RippleState",
918 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
927 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
928 BEAST_EXPECT(*it == jvL[it.memberName()]);
942 env.
fund(
XRP(10000),
"alice",
"bob");
945 env(
pay(
"bob",
"alice",
Account(
"bob")[
"USD"](50)));
954 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
961 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
965 "LedgerEntryType" : "RippleState",
969 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
978 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
979 BEAST_EXPECT(*it == jvL[it.memberName()]);
981 env(
pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
990 std::string(
"Path Find: XRP -> XRP and XRP -> IOU") +
991 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1002 env.fund(
XRP(100000), a1);
1003 env.fund(
XRP(10000), a2);
1004 env.fund(
XRP(1000), a3, g1, g2, g3, m1);
1007 env.trust(g1[
"XYZ"](5000), a1);
1008 env.trust(g3[
"ABC"](5000), a1);
1009 env.trust(g2[
"XYZ"](5000), a2);
1010 env.trust(g3[
"ABC"](5000), a2);
1011 env.trust(a2[
"ABC"](1000), a3);
1012 env.trust(g1[
"XYZ"](100000), m1);
1013 env.trust(g2[
"XYZ"](100000), m1);
1014 env.trust(g3[
"ABC"](100000), m1);
1017 env(
pay(g1, a1, g1[
"XYZ"](3500)));
1018 env(
pay(g3, a1, g3[
"ABC"](1200)));
1019 env(
pay(g2, m1, g2[
"XYZ"](25000)));
1020 env(
pay(g3, m1, g3[
"ABC"](25000)));
1026 domainID =
setupDomain(env, {a1, a2, a3, g1, g2, g3, m1});
1027 env(
offer(m1, g1[
"XYZ"](1000), g2[
"XYZ"](1000)),
Domain(*domainID));
1033 env(
offer(m1, g1[
"XYZ"](1000), g2[
"XYZ"](1000)));
1034 env(
offer(m1,
XRP(10000), g3[
"ABC"](1000)));
1042 auto const& sendAmt =
XRP(10);
1045 BEAST_EXPECT(
equal(da, sendAmt));
1046 BEAST_EXPECT(st.
empty());
1052 auto const& sendAmt =
XRP(200);
1055 BEAST_EXPECT(
equal(da, sendAmt));
1056 BEAST_EXPECT(st.
empty());
1060 auto const& sendAmt = g3[
"ABC"](10);
1063 BEAST_EXPECT(
equal(da, sendAmt));
1069 auto const& sendAmt = a2[
"ABC"](1);
1072 BEAST_EXPECT(
equal(da, sendAmt));
1078 auto const& sendAmt = a3[
"ABC"](1);
1081 BEAST_EXPECT(
equal(da, sendAmt));
1091 std::string(
"Path Find: non-XRP -> XRP") + (domainEnabled ?
" w/ " :
" w/o ") +
1093 using namespace jtx;
1100 env.fund(
XRP(1000), a1, a2, g3);
1101 env.fund(
XRP(11000), m1);
1104 env.trust(g3[
"ABC"](1000), a1, a2);
1105 env.trust(g3[
"ABC"](100000), m1);
1108 env(
pay(g3, a1, g3[
"ABC"](1000)));
1109 env(
pay(g3, a2, g3[
"ABC"](1000)));
1110 env(
pay(g3, m1, g3[
"ABC"](1200)));
1113 std::optional<uint256> domainID;
1121 env(
offer(m1, g3[
"ABC"](1000),
XRP(10000)));
1126 auto const& sendAmt =
XRP(10);
1130 findPaths(env, a1, a2, sendAmt, std::nullopt, a2[
"ABC"].currency, domainID);
1131 BEAST_EXPECT(
equal(da, sendAmt));
1132 BEAST_EXPECT(
equal(sa, a1[
"ABC"](1)));
1141 findPaths(env, a1, a2, sendAmt, std::nullopt, a2[
"ABC"].currency);
1142 BEAST_EXPECT(
equal(da, sendAmt));
1143 BEAST_EXPECT(st.empty());
1151 std::string(
"Path Find: Bitstamp and SnapSwap, liquidity with no offers") +
1152 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1153 using namespace jtx;
1161 env.fund(
XRP(1000), g1Bs, g2Sw, a1, a2);
1162 env.fund(
XRP(11000), m1);
1165 env.trust(g1Bs[
"HKD"](2000), a1);
1166 env.trust(g2Sw[
"HKD"](2000), a2);
1167 env.trust(g1Bs[
"HKD"](100000), m1);
1168 env.trust(g2Sw[
"HKD"](100000), m1);
1171 env(
pay(g1Bs, a1, g1Bs[
"HKD"](1000)));
1172 env(
pay(g2Sw, a2, g2Sw[
"HKD"](1000)));
1176 env(
pay(g1Bs, m1, g1Bs[
"HKD"](1200)));
1177 env(
pay(g2Sw, m1, g2Sw[
"HKD"](5000)));
1182 domainID =
setupDomain(env, {a1, a2, g1Bs, g2Sw, m1});
1188 auto const& sendAmt = a2[
"HKD"](10);
1190 findPaths(env, a1, a2, sendAmt, std::nullopt, a2[
"HKD"].currency, domainID);
1191 BEAST_EXPECT(
equal(da, sendAmt));
1192 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1193 BEAST_EXPECT(
same(st,
stpath(g1Bs, m1, g2Sw)));
1197 auto const& sendAmt = a1[
"HKD"](10);
1199 findPaths(env, a2, a1, sendAmt, std::nullopt, a1[
"HKD"].currency, domainID);
1200 BEAST_EXPECT(
equal(da, sendAmt));
1201 BEAST_EXPECT(
equal(sa, a2[
"HKD"](10)));
1202 BEAST_EXPECT(
same(st,
stpath(g2Sw, m1, g1Bs)));
1206 auto const& sendAmt = a2[
"HKD"](10);
1208 findPaths(env, g1Bs, a2, sendAmt, std::nullopt, a1[
"HKD"].currency, domainID);
1209 BEAST_EXPECT(
equal(da, sendAmt));
1210 BEAST_EXPECT(
equal(sa, g1Bs[
"HKD"](10)));
1215 auto const& sendAmt = m1[
"HKD"](10);
1217 findPaths(env, m1, g1Bs, sendAmt, std::nullopt, a1[
"HKD"].currency, domainID);
1218 BEAST_EXPECT(
equal(da, sendAmt));
1219 BEAST_EXPECT(
equal(sa, m1[
"HKD"](10)));
1220 BEAST_EXPECT(st.empty());
1224 auto const& sendAmt = a1[
"HKD"](10);
1226 findPaths(env, g2Sw, a1, sendAmt, std::nullopt, a1[
"HKD"].currency, domainID);
1227 BEAST_EXPECT(
equal(da, sendAmt));
1228 BEAST_EXPECT(
equal(sa, g2Sw[
"HKD"](10)));
1237 std::string(
"Path Find: non-XRP -> non-XRP, same currency") +
1238 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1239 using namespace jtx;
1252 env.fund(
XRP(1000), a1, a2, a3, g1, g2, g3, g4);
1253 env.fund(
XRP(10000), a4);
1254 env.fund(
XRP(11000), m1, m2);
1257 env.trust(g1[
"HKD"](2000), a1);
1258 env.trust(g2[
"HKD"](2000), a2);
1259 env.trust(g1[
"HKD"](2000), a3);
1260 env.trust(g1[
"HKD"](100000), m1);
1261 env.trust(g2[
"HKD"](100000), m1);
1262 env.trust(g1[
"HKD"](100000), m2);
1263 env.trust(g2[
"HKD"](100000), m2);
1266 env(
pay(g1, a1, g1[
"HKD"](1000)));
1267 env(
pay(g2, a2, g2[
"HKD"](1000)));
1268 env(
pay(g1, a3, g1[
"HKD"](1000)));
1269 env(
pay(g1, m1, g1[
"HKD"](1200)));
1270 env(
pay(g2, m1, g2[
"HKD"](5000)));
1271 env(
pay(g1, m2, g1[
"HKD"](1200)));
1272 env(
pay(g2, m2, g2[
"HKD"](5000)));
1275 std::optional<uint256> domainID;
1278 domainID =
setupDomain(env, {a1, a2, a3, a4, g1, g2, g3, g4, m1, m2});
1279 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
Domain(*domainID));
1285 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)));
1286 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)));
1287 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)));
1296 auto const& sendAmt = g1[
"HKD"](10);
1298 findPaths(env, a1, g1, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1299 BEAST_EXPECT(st.empty());
1300 BEAST_EXPECT(
equal(da, sendAmt));
1301 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1307 auto const& sendAmt = a1[
"HKD"](10);
1309 findPaths(env, a1, g1, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1310 BEAST_EXPECT(st.empty());
1311 BEAST_EXPECT(
equal(da, sendAmt));
1318 auto const& sendAmt = a3[
"HKD"](10);
1320 findPaths(env, a1, a3, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1321 BEAST_EXPECT(
equal(da, sendAmt));
1322 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1329 auto const& sendAmt = g2[
"HKD"](10);
1331 findPaths(env, g1, g2, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1332 BEAST_EXPECT(
equal(da, sendAmt));
1333 BEAST_EXPECT(
equal(sa, g1[
"HKD"](10)));
1345 auto const& sendAmt = g2[
"HKD"](10);
1347 findPaths(env, a1, g2, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1348 BEAST_EXPECT(
equal(da, sendAmt));
1349 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1362 auto const& sendAmt = a2[
"HKD"](10);
1364 findPaths(env, a1, a2, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1365 BEAST_EXPECT(
equal(da, sendAmt));
1366 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1380 std::string(
"Path Find: non-XRP -> non-XRP, same currency)") +
1381 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1382 using namespace jtx;
1391 env.fund(
XRP(11000), m1);
1392 env.fund(
XRP(1000), a1, a2, a3, g1, g2);
1395 env.trust(g1[
"HKD"](2000), a1);
1396 env.trust(g2[
"HKD"](2000), a2);
1397 env.trust(a2[
"HKD"](2000), a3);
1398 env.trust(g1[
"HKD"](100000), m1);
1399 env.trust(g2[
"HKD"](100000), m1);
1402 env(
pay(g1, a1, g1[
"HKD"](1000)));
1403 env(
pay(g2, a2, g2[
"HKD"](1000)));
1404 env(
pay(g1, m1, g1[
"HKD"](5000)));
1405 env(
pay(g2, m1, g2[
"HKD"](5000)));
1408 std::optional<uint256> domainID;
1411 domainID =
setupDomain(env, {a1, a2, a3, g1, g2, m1});
1412 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
Domain(*domainID));
1416 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)));
1421 auto const& sendAmt = a2[
"HKD"](10);
1425 findPaths(env, g1, a2, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1426 BEAST_EXPECT(
equal(da, sendAmt));
1427 BEAST_EXPECT(
equal(sa, g1[
"HKD"](10)));
1434 testcase(std::string(
"Receive max") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1436 using namespace jtx;
1437 auto const alice =
Account(
"alice");
1438 auto const bob =
Account(
"bob");
1439 auto const charlie =
Account(
"charlie");
1440 auto const gw =
Account(
"gw");
1441 auto const usd = gw[
"USD"];
1445 env.fund(
XRP(10000), alice, bob, charlie, gw);
1447 env.trust(usd(100), alice, bob, charlie);
1449 env(
pay(gw, charlie, usd(10)));
1455 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1461 env(
offer(charlie,
XRP(10), usd(10)));
1466 findPaths(env, alice, bob, usd(-1),
XRP(100).value(), std::nullopt, domainID);
1467 BEAST_EXPECT(sa ==
XRP(10));
1468 BEAST_EXPECT(
equal(da, usd(10)));
1469 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1471 auto const& pathElem = st[0][0];
1473 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
1474 pathElem.getCurrency() == usd.currency);
1480 env.fund(
XRP(10000), alice, bob, charlie, gw);
1482 env.trust(usd(100), alice, bob, charlie);
1484 env(
pay(gw, alice, usd(10)));
1487 std::optional<uint256> domainID;
1490 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1496 env(
offer(charlie, usd(10),
XRP(10)));
1501 findPaths(env, alice, bob,
drops(-1), usd(100).value(), std::nullopt, domainID);
1502 BEAST_EXPECT(sa == usd(10));
1504 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1506 auto const& pathElem = st[0][0];
1508 pathElem.isOffer() && pathElem.getIssuerID() ==
xrpAccount() &&
1517 using namespace jtx;
1522 auto const alice =
Account(
"alice");
1523 auto const bob =
Account(
"bob");
1524 auto const george =
Account(
"george");
1525 auto const usd = george[
"USD"];
1526 auto test = [&](
std::string casename,
bool aliceRipple,
bool bobRipple,
bool expectPath) {
1530 env.fund(
XRP(10000),
noripple(alice, bob, george));
1534 env(
trust(alice, usd(100), aliceRipple ? tfClearNoRipple : tfSetNoRipple));
1535 env(
trust(george, alice[
"USD"](100), aliceRipple ? tfClearNoRipple : tfSetNoRipple));
1536 env(
trust(bob, usd(100), bobRipple ? tfClearNoRipple : tfSetNoRipple));
1537 env(
trust(george, bob[
"USD"](100), bobRipple ? tfClearNoRipple : tfSetNoRipple));
1539 env(
pay(george, alice, usd(70)));
1542 auto [st, sa, da] =
findPaths(env,
"alice",
"bob",
Account(
"bob")[
"USD"](5));
1543 BEAST_EXPECT(
equal(da, bob[
"USD"](5)));
1547 BEAST_EXPECT(st.size() == 1);
1549 BEAST_EXPECT(
equal(sa, alice[
"USD"](5)));
1553 BEAST_EXPECT(st.empty());
1557 test(
"ripple -> ripple",
true,
true,
true);
1558 test(
"ripple -> no ripple",
true,
false,
true);
1559 test(
"no ripple -> ripple",
false,
true,
true);
1560 test(
"no ripple -> no ripple",
false,
false,
false);
1567 using namespace jtx;
1572 auto testPathfind = [&](
auto func,
bool const domainEnabled =
false) {
1585 env.fund(
XRP(1000), a1, a2, a3, g1, g2, g3, g4);
1586 env.fund(
XRP(10000), a4);
1587 env.fund(
XRP(11000), m1, m2);
1590 env.trust(g1[
"HKD"](2000), a1);
1591 env.trust(g2[
"HKD"](2000), a2);
1592 env.trust(g1[
"HKD"](2000), a3);
1593 env.trust(g1[
"HKD"](100000), m1);
1594 env.trust(g2[
"HKD"](100000), m1);
1595 env.trust(g1[
"HKD"](100000), m2);
1596 env.trust(g2[
"HKD"](100000), m2);
1599 env(
pay(g1, a1, g1[
"HKD"](1000)));
1600 env(
pay(g2, a2, g2[
"HKD"](1000)));
1601 env(
pay(g1, a3, g1[
"HKD"](1000)));
1602 env(
pay(g1, m1, g1[
"HKD"](1200)));
1603 env(
pay(g2, m1, g2[
"HKD"](5000)));
1604 env(
pay(g1, m2, g1[
"HKD"](1200)));
1605 env(
pay(g2, m2, g2[
"HKD"](5000)));
1608 std::optional<uint256> domainID =
1609 setupDomain(env, {a1, a2, a3, a4, g1, g2, g3, g4, m1, m2});
1610 BEAST_EXPECT(domainID);
1612 func(env, m1, m2, g1, g2, *domainID);
1620 auto const& sendAmt = g1[
"HKD"](10);
1628 domainEnabled ? domainID : std::nullopt);
1629 BEAST_EXPECT(st.empty());
1630 BEAST_EXPECT(
equal(da, sendAmt));
1631 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1637 auto const& sendAmt = a1[
"HKD"](10);
1645 domainEnabled ? domainID : std::nullopt);
1646 BEAST_EXPECT(st.empty());
1647 BEAST_EXPECT(
equal(da, sendAmt));
1648 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1654 auto const& sendAmt = a3[
"HKD"](10);
1662 domainEnabled ? domainID : std::nullopt);
1663 BEAST_EXPECT(
equal(da, sendAmt));
1664 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1671 auto const& sendAmt = g2[
"HKD"](10);
1679 domainEnabled ? domainID : std::nullopt);
1680 BEAST_EXPECT(
equal(da, sendAmt));
1681 BEAST_EXPECT(
equal(sa, g1[
"HKD"](10)));
1693 auto const& sendAmt = g2[
"HKD"](10);
1701 domainEnabled ? domainID : std::nullopt);
1702 BEAST_EXPECT(
equal(da, sendAmt));
1703 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1716 auto const& sendAmt = a2[
"HKD"](10);
1724 domainEnabled ? domainID : std::nullopt);
1725 BEAST_EXPECT(
equal(da, sendAmt));
1726 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1742 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1745 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)));
1746 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)));
1751 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1754 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1757 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)));
1762 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1765 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1768 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1775 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)));
1776 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)));
1777 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1784 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)));
1785 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1788 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1800 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1810 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1813 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1822 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
Domain(domainID));
1824 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1832 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
Domain(domainID));
1833 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1836 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1847 testcase(
"AMM not used in domain path");
1848 using namespace jtx;
1850 PermissionedDEX
const permDex(env);
1851 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = permDex;
1852 AMM
const amm(env, alice_,
XRP(10), USD(50));
1857 auto const& sendAmt =
XRP(1);
1861 findPaths(env, bob_, carol_, sendAmt, std::nullopt, USD.currency, domainID);
1862 BEAST_EXPECT(st.empty());
1865 std::tie(st, sa, da) =
findPaths(env, bob_, carol_, sendAmt, std::nullopt, USD.currency);
1885 for (
bool const domainEnabled : {
false,
true})
TestcaseT testcase
Memberspace for declaring test cases.
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.
An endpoint that consumes resources.
json::Value getJson(JsonOptions=JsonOptions::Values::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 waitFor(std::chrono::duration< Rep, Period > const &relTime)
void pathFind06(bool const domainEnabled)
void issuesPathNegativeRippleClientIssue23Larger()
void run() override
Runs the suite.
void issuesPathNegativeIssue(bool const domainEnabled)
void alternativePathsConsumeBestTransferFirst()
void xrpToXrp(bool const domainEnabled)
void pathFind01(bool const domainEnabled)
void pathFind04(bool const domainEnabled)
void receiveMax(bool const domainEnabled)
void alternativePathsLimitReturnedPathsToBestQuality(bool const domainEnabled)
void pathFind05(bool const domainEnabled)
void viaOffersViaGateway(bool const domainEnabled)
void trustAutoClearTrustNormalClear()
void paymentAutoPathFind()
void directPathNoIntermediary()
void alternativePathConsumeBoth(bool const domainEnabled)
void norippleCombinations()
void issuesPathNegativeRippleClientIssue23Smaller()
auto findPathsRequest(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 pathFind02(bool const domainEnabled)
void indirectPathsPathFind()
void noDirectPathNoIntermediaryNoAlternatives()
void qualityPathsQualitySetAndTest()
void pathFind(bool const domainEnabled)
std::tuple< STPathSet, STAmount, STAmount > findPaths(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 pathFindConsumeAll(bool const domainEnabled)
void trustAutoClearTrustAutoClear()
void alternativePathsConsumeBestTransfer(bool const domainEnabled)
void sourceCurrenciesLimit()
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.
SLE::const_pointer 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.
@ Array
array value (ordered list)
@ Object
object value (collection of name/value pairs).
static constexpr int kMaxAutoSrcCur
Maximum number of auto source currencies in a path find request.
static constexpr int kMaxSrcCur
Maximum number of source currencies allowed in a path find request.
static constexpr auto kApiVersionIfUnspecified
Status doCommand(RPC::JsonContext &context, json::Value &result)
Execute an RPC command and store the results in a json::Value.
Charge const kFeeReferenceRpc
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
XrpT const XRP
Converts to XRP Issue or STAmount.
bool same(STPathSet const &st1, Args const &... args)
uint256 setupDomain(jtx::Env &env, std::vector< jtx::Account > const &accounts, jtx::Account const &domainOwner, std::string const &credType)
STPathElement ipe(Asset const &asset)
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
bool equal(STAmount const &sa1, STAmount const &sa2)
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
STPath stpath(Args const &... args)
json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
json::Value rpf(jtx::Account const &src, jtx::Account const &dst, std::uint32_t numSrc)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
BaseUInt< 256 > Domain
Domain is a 256-bit hash representing a specific domain.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Currency const & xrpCurrency()
XRP currency.
std::string to_string(BaseUInt< Bits, Tag > const &a)
STAmount amountFromJson(SField const &name, json::Value const &v)
AccountID const & xrpAccount()
Compute AccountID from public key.
std::shared_ptr< JobQueue::Coro > coro