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/basics/hardened_hash.h>
29#include <xrpl/beast/unit_test/suite.h>
30#include <xrpl/core/Job.h>
31#include <xrpl/core/JobQueue.h>
32#include <xrpl/json/json_reader.h>
33#include <xrpl/json/json_value.h>
34#include <xrpl/protocol/AccountID.h>
35#include <xrpl/protocol/ApiVersion.h>
36#include <xrpl/protocol/Indexes.h>
37#include <xrpl/protocol/Issue.h>
38#include <xrpl/protocol/SField.h>
39#include <xrpl/protocol/STAmount.h>
40#include <xrpl/protocol/STParsedJSON.h>
41#include <xrpl/protocol/STPathSet.h>
42#include <xrpl/protocol/TER.h>
43#include <xrpl/protocol/TxFlags.h>
44#include <xrpl/protocol/UintTypes.h>
45#include <xrpl/protocol/jss.h>
46#include <xrpl/resource/Charge.h>
47#include <xrpl/resource/Consumer.h>
48#include <xrpl/resource/Fees.h>
73 jv[jss::command] =
"ripple_path_find";
74 jv[jss::source_account] =
toBase58(src);
80 while ((numSrc--) != 0u)
88 jv[jss::destination_account] = d;
91 j[jss::currency] =
"USD";
92 j[jss::value] =
"0.01";
110 cfg->pathSearchOld = 7;
112 cfg->pathSearchMax = 10;
128 template <
class Rep,
class Period>
133 auto b =
cv_.wait_for(lk, relTime, [
this] {
return signaled_; });
160 auto& app = env.
app();
167 .loadType = loadType,
168 .netOps = app.getOPs(),
169 .ledgerMaster = app.getLedgerMaster(),
179 params[jss::command] =
"ripple_path_find";
180 params[jss::source_account] =
toBase58(src);
181 params[jss::destination_account] =
toBase58(dst);
189 j[jss::currency] =
to_string(saSrcCurrency.value());
193 j[jss::issuer] =
toBase58(*saSrcIssuer);
197 params[jss::domain] =
to_string(*domain);
201 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
202 context.
params = std::move(params);
208 using namespace std::chrono_literals;
210 BEAST_EXPECT(!result.
isMember(jss::error));
226 env, src, dst, saDstAmount, saSendMax, saSrcCurrency, domain, saSrcIssuer);
227 BEAST_EXPECT(!result.
isMember(jss::error));
230 if (result.
isMember(jss::destination_amount))
235 if (result.
isMember(jss::alternatives))
237 auto const& alts = result[jss::alternatives];
240 auto const&
path = alts[0u];
242 if (
path.isMember(jss::source_amount))
245 if (
path.isMember(jss::destination_amount))
248 if (
path.isMember(jss::paths_computed))
251 p[
"Paths"] =
path[jss::paths_computed];
255 paths = po.
object->getFieldPathSet(sfPaths);
260 return std::make_tuple(std::move(paths), std::move(sa), std::move(da));
267 using namespace std::chrono_literals;
270 auto const gw =
Account(
"gateway");
271 env.
fund(
XRP(10000),
"alice",
"bob", gw);
273 env.
trust(gw[
"USD"](100),
"alice",
"bob");
276 auto& app = env.
app();
283 .loadType = loadType,
284 .netOps = app.getOPs(),
285 .ledgerMaster = app.getLedgerMaster(),
296 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
303 BEAST_EXPECT(!result.
isMember(jss::error));
306 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
313 BEAST_EXPECT(result.
isMember(jss::error));
318 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
325 BEAST_EXPECT(!result.
isMember(jss::error));
329 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
336 BEAST_EXPECT(result.
isMember(jss::error));
342 testcase(
"source currency issuer selection");
346 auto const alice =
Account(
"alice");
347 auto const bob =
Account(
"bob");
348 auto const gateway =
Account(
"gateway");
350 env.
fund(
XRP(10000), alice, bob, gateway);
353 auto const usd = gateway[
"USD"];
354 env.
trust(usd(600), alice);
355 env.
trust(usd(700), bob);
356 env.
trust(alice[
"USD"](700), bob);
357 env(
pay(gateway, alice, usd(70)));
358 env(
pay(gateway, bob, usd(50)));
374 alice[
"USD"](100).value(),
378 auto const& alternatives = result[jss::alternatives];
379 BEAST_EXPECT(alternatives.size() == 1);
382 BEAST_EXPECTS(
equal(sa, usd(100)), sa.getFullText());
383 BEAST_EXPECTS(
equal(da, bob[
"USD"](100)), da.getFullText());
389 testcase(
"no direct path no intermediary no alternatives");
392 env.
fund(
XRP(10000),
"alice",
"bob");
395 auto const result =
findPaths(env,
"alice",
"bob",
Account(
"bob")[
"USD"](5));
396 BEAST_EXPECT(std::get<0>(result).empty());
402 testcase(
"direct path no intermediary");
405 env.
fund(
XRP(10000),
"alice",
"bob");
412 BEAST_EXPECT(st.
empty());
422 auto const gw =
Account(
"gateway");
423 auto const usd = gw[
"USD"];
424 env.
fund(
XRP(10000),
"alice",
"bob", gw);
426 env.
trust(usd(600),
"alice");
427 env.
trust(usd(700),
"bob");
428 env(
pay(gw,
"alice", usd(70)));
429 env(
pay(
"alice",
"bob", usd(24)));
442 auto const gw =
Account(
"gateway");
443 auto const usd = gw[
"USD"];
444 env.
fund(
XRP(10000),
"alice",
"bob", gw);
446 env.
trust(usd(600),
"alice");
447 env.
trust(usd(700),
"bob");
448 env(
pay(gw,
"alice", usd(70)));
449 env(
pay(gw,
"bob", usd(50)));
458 env,
"alice",
"bob",
Account(
"bob")[
"USD"](5), std::nullopt, std::nullopt, domainID);
469 env.
fund(
XRP(10000),
"alice",
"bob");
477 findPaths(env,
"alice",
"bob",
XRP(5), std::nullopt, std::nullopt, domainID);
478 BEAST_EXPECT(std::get<0>(result).empty());
485 std::string(
"path find consume all") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
490 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
500 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan",
"edward"});
520 auto const gw =
Account(
"gateway");
521 auto const usd = gw[
"USD"];
522 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
524 env.
trust(usd(100),
"bob",
"carol");
526 env(
pay(gw,
"carol", usd(100)));
532 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"gateway"});
537 env(
offer(
"carol",
XRP(100), usd(100)));
552 BEAST_EXPECT(st.
empty());
561 BEAST_EXPECT(sa ==
XRP(100));
576 BEAST_EXPECT(st.
empty());
585 std::string(
"alternative path consume both") + (domainEnabled ?
" w/ " :
" w/o ") +
589 auto const gw =
Account(
"gateway");
590 auto const usd = gw[
"USD"];
591 auto const gw2 =
Account(
"gateway2");
592 auto const gw2Usd = gw2[
"USD"];
593 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
595 env.
trust(usd(600),
"alice");
596 env.
trust(gw2Usd(800),
"alice");
597 env.
trust(usd(700),
"bob");
598 env.
trust(gw2Usd(900),
"bob");
603 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
604 env(
pay(gw,
"alice", usd(70)),
Domain(*domainID));
605 env(
pay(gw2,
"alice", gw2Usd(70)),
Domain(*domainID));
606 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](140)),
612 env(
pay(gw,
"alice", usd(70)));
613 env(
pay(gw2,
"alice", gw2Usd(70)));
631 std::string(
"alternative paths consume best transfer") +
632 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
635 auto const gw =
Account(
"gateway");
636 auto const usd = gw[
"USD"];
637 auto const gw2 =
Account(
"gateway2");
638 auto const gw2Usd = gw2[
"USD"];
639 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
642 env.
trust(usd(600),
"alice");
643 env.
trust(gw2Usd(800),
"alice");
644 env.
trust(usd(700),
"bob");
645 env.
trust(gw2Usd(900),
"bob");
650 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
651 env(
pay(gw,
"alice", usd(70)),
Domain(*domainID));
652 env(
pay(gw2,
"alice", gw2Usd(70)),
Domain(*domainID));
653 env(
pay(
"alice",
"bob", usd(70)),
Domain(*domainID));
657 env(
pay(gw,
"alice", usd(70)));
658 env(
pay(gw2,
"alice", gw2Usd(70)));
659 env(
pay(
"alice",
"bob", usd(70)));
674 testcase(
"alternative paths - consume best transfer first");
677 auto const gw =
Account(
"gateway");
678 auto const usd = gw[
"USD"];
679 auto const gw2 =
Account(
"gateway2");
680 auto const gw2Usd = gw2[
"USD"];
681 env.
fund(
XRP(10000),
"alice",
"bob", gw, gw2);
684 env.
trust(usd(600),
"alice");
685 env.
trust(gw2Usd(800),
"alice");
686 env.
trust(usd(700),
"bob");
687 env.
trust(gw2Usd(900),
"bob");
688 env(
pay(gw,
"alice", usd(70)));
689 env(
pay(gw2,
"alice", gw2Usd(70)));
690 env(
pay(
"alice",
"bob",
Account(
"bob")[
"USD"](77)),
707 std::string(
"alternative paths - limit returned paths to best quality") +
708 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
711 auto const gw =
Account(
"gateway");
712 auto const usd = gw[
"USD"];
713 auto const gw2 =
Account(
"gateway2");
714 auto const gw2Usd = gw2[
"USD"];
715 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan", gw, gw2);
717 env(
rate(
"carol", 1.1));
718 env.
trust(
Account(
"carol")[
"USD"](800),
"alice",
"bob");
720 env.
trust(usd(800),
"alice",
"bob");
721 env.
trust(gw2Usd(800),
"alice",
"bob");
725 env(
pay(gw2,
"alice", gw2Usd(100)));
727 env(
pay(
"carol",
"alice",
Account(
"carol")[
"USD"](100)));
729 env(
pay(gw,
"alice", usd(100)));
735 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan", gw, gw2});
741 env,
"alice",
"bob",
Account(
"bob")[
"USD"](5), std::nullopt, std::nullopt, domainID);
751 std::string(
"path negative: Issue #5") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
754 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
756 env.
trust(
Account(
"bob")[
"USD"](100),
"alice",
"carol",
"dan");
759 env(
pay(
"bob",
"carol",
Account(
"bob")[
"USD"](75)));
767 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"dan"});
771 env,
"alice",
"bob",
Account(
"bob")[
"USD"](25), std::nullopt, std::nullopt, domainID);
772 BEAST_EXPECT(std::get<0>(result).empty());
778 env,
"alice",
"bob",
Account(
"alice")[
"USD"](25), std::nullopt, std::nullopt, domainID);
779 BEAST_EXPECT(std::get<0>(result).empty());
799 testcase(
"path negative: ripple-client issue #23: smaller");
802 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan");
818 testcase(
"path negative: ripple-client issue #23: larger");
821 env.
fund(
XRP(10000),
"alice",
"bob",
"carol",
"dan",
"edward");
848 auto const gw =
Account(
"gateway");
849 auto const aud = gw[
"AUD"];
850 env.
fund(
XRP(10000),
"alice",
"bob",
"carol", gw);
854 env.
trust(aud(100),
"bob",
"carol");
856 env(
pay(gw,
"carol", aud(50)));
862 domainID =
setupDomain(env, {
"alice",
"bob",
"carol", gw});
870 env(
offer(
"carol",
XRP(50), aud(50)));
880 env,
"alice",
"bob",
Account(
"bob")[
"USD"](25), std::nullopt, std::nullopt, domainID);
881 BEAST_EXPECT(std::get<0>(result).empty());
890 env.
fund(
XRP(10000),
"alice",
"bob",
"carol");
909 env.
fund(
XRP(10000),
"alice",
"bob");
912 Json(
"{\"" + sfQualityIn.fieldName +
"\": 2000}"),
913 Json(
"{\"" + sfQualityOut.fieldName +
"\": 1400000000}"));
920 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
926 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
930 "HighQualityIn" : 2000,
931 "HighQualityOut" : 1400000000,
932 "LedgerEntryType" : "RippleState",
935 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
944 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
945 BEAST_EXPECT(*it == jvL[it.memberName()]);
964 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
970 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
974 "LedgerEntryType" : "RippleState",
977 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
986 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
987 BEAST_EXPECT(*it == jvL[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",
1037 for (
auto it = jv.
begin(); it != jv.
end(); ++it)
1038 BEAST_EXPECT(*it == jvL[it.memberName()]);
1040 env(
pay(
"alice",
"bob",
Account(
"alice")[
"USD"](50)));
1049 std::string(
"Path Find: XRP -> XRP and XRP -> IOU") +
1050 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1051 using namespace jtx;
1061 env.fund(
XRP(100000), a1);
1062 env.fund(
XRP(10000), a2);
1063 env.fund(
XRP(1000), a3, g1, g2, g3, m1);
1066 env.trust(g1[
"XYZ"](5000), a1);
1067 env.trust(g3[
"ABC"](5000), a1);
1068 env.trust(g2[
"XYZ"](5000), a2);
1069 env.trust(g3[
"ABC"](5000), a2);
1070 env.trust(a2[
"ABC"](1000), a3);
1071 env.trust(g1[
"XYZ"](100000), m1);
1072 env.trust(g2[
"XYZ"](100000), m1);
1073 env.trust(g3[
"ABC"](100000), m1);
1076 env(
pay(g1, a1, g1[
"XYZ"](3500)));
1077 env(
pay(g3, a1, g3[
"ABC"](1200)));
1078 env(
pay(g2, m1, g2[
"XYZ"](25000)));
1079 env(
pay(g3, m1, g3[
"ABC"](25000)));
1085 domainID =
setupDomain(env, {a1, a2, a3, g1, g2, g3, m1});
1086 env(
offer(m1, g1[
"XYZ"](1000), g2[
"XYZ"](1000)),
Domain(*domainID));
1092 env(
offer(m1, g1[
"XYZ"](1000), g2[
"XYZ"](1000)));
1093 env(
offer(m1,
XRP(10000), g3[
"ABC"](1000)));
1101 auto const& sendAmt =
XRP(10);
1104 BEAST_EXPECT(
equal(da, sendAmt));
1105 BEAST_EXPECT(st.
empty());
1111 auto const& sendAmt =
XRP(200);
1114 BEAST_EXPECT(
equal(da, sendAmt));
1115 BEAST_EXPECT(st.
empty());
1119 auto const& sendAmt = g3[
"ABC"](10);
1122 BEAST_EXPECT(
equal(da, sendAmt));
1128 auto const& sendAmt = a2[
"ABC"](1);
1131 BEAST_EXPECT(
equal(da, sendAmt));
1137 auto const& sendAmt = a3[
"ABC"](1);
1140 BEAST_EXPECT(
equal(da, sendAmt));
1150 std::string(
"Path Find: non-XRP -> XRP") + (domainEnabled ?
" w/ " :
" w/o ") +
1152 using namespace jtx;
1159 env.fund(
XRP(1000), a1, a2, g3);
1160 env.fund(
XRP(11000), m1);
1163 env.trust(g3[
"ABC"](1000), a1, a2);
1164 env.trust(g3[
"ABC"](100000), m1);
1167 env(
pay(g3, a1, g3[
"ABC"](1000)));
1168 env(
pay(g3, a2, g3[
"ABC"](1000)));
1169 env(
pay(g3, m1, g3[
"ABC"](1200)));
1172 std::optional<UInt256> domainID;
1180 env(
offer(m1, g3[
"ABC"](1000),
XRP(10000)));
1185 auto const& sendAmt =
XRP(10);
1189 findPaths(env, a1, a2, sendAmt, std::nullopt, a2[
"ABC"].currency, domainID);
1190 BEAST_EXPECT(
equal(da, sendAmt));
1191 BEAST_EXPECT(
equal(sa, a1[
"ABC"](1)));
1200 findPaths(env, a1, a2, sendAmt, std::nullopt, a2[
"ABC"].currency);
1201 BEAST_EXPECT(
equal(da, sendAmt));
1202 BEAST_EXPECT(st.empty());
1210 std::string(
"Path Find: Bitstamp and SnapSwap, liquidity with no offers") +
1211 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1212 using namespace jtx;
1220 env.fund(
XRP(1000), g1Bs, g2Sw, a1, a2);
1221 env.fund(
XRP(11000), m1);
1224 env.trust(g1Bs[
"HKD"](2000), a1);
1225 env.trust(g2Sw[
"HKD"](2000), a2);
1226 env.trust(g1Bs[
"HKD"](100000), m1);
1227 env.trust(g2Sw[
"HKD"](100000), m1);
1230 env(
pay(g1Bs, a1, g1Bs[
"HKD"](1000)));
1231 env(
pay(g2Sw, a2, g2Sw[
"HKD"](1000)));
1235 env(
pay(g1Bs, m1, g1Bs[
"HKD"](1200)));
1236 env(
pay(g2Sw, m1, g2Sw[
"HKD"](5000)));
1241 domainID =
setupDomain(env, {a1, a2, g1Bs, g2Sw, m1});
1247 auto const& sendAmt = a2[
"HKD"](10);
1249 findPaths(env, a1, a2, sendAmt, std::nullopt, a2[
"HKD"].currency, domainID);
1250 BEAST_EXPECT(
equal(da, sendAmt));
1251 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1252 BEAST_EXPECT(
same(st,
stpath(g1Bs, m1, g2Sw)));
1256 auto const& sendAmt = a1[
"HKD"](10);
1258 findPaths(env, a2, a1, sendAmt, std::nullopt, a1[
"HKD"].currency, domainID);
1259 BEAST_EXPECT(
equal(da, sendAmt));
1260 BEAST_EXPECT(
equal(sa, a2[
"HKD"](10)));
1261 BEAST_EXPECT(
same(st,
stpath(g2Sw, m1, g1Bs)));
1265 auto const& sendAmt = a2[
"HKD"](10);
1267 findPaths(env, g1Bs, a2, sendAmt, std::nullopt, a1[
"HKD"].currency, domainID);
1268 BEAST_EXPECT(
equal(da, sendAmt));
1269 BEAST_EXPECT(
equal(sa, g1Bs[
"HKD"](10)));
1274 auto const& sendAmt = m1[
"HKD"](10);
1276 findPaths(env, m1, g1Bs, sendAmt, std::nullopt, a1[
"HKD"].currency, domainID);
1277 BEAST_EXPECT(
equal(da, sendAmt));
1278 BEAST_EXPECT(
equal(sa, m1[
"HKD"](10)));
1279 BEAST_EXPECT(st.empty());
1283 auto const& sendAmt = a1[
"HKD"](10);
1285 findPaths(env, g2Sw, a1, sendAmt, std::nullopt, a1[
"HKD"].currency, domainID);
1286 BEAST_EXPECT(
equal(da, sendAmt));
1287 BEAST_EXPECT(
equal(sa, g2Sw[
"HKD"](10)));
1296 std::string(
"Path Find: non-XRP -> non-XRP, same currency") +
1297 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1298 using namespace jtx;
1311 env.fund(
XRP(1000), a1, a2, a3, g1, g2, g3, g4);
1312 env.fund(
XRP(10000), a4);
1313 env.fund(
XRP(11000), m1, m2);
1316 env.trust(g1[
"HKD"](2000), a1);
1317 env.trust(g2[
"HKD"](2000), a2);
1318 env.trust(g1[
"HKD"](2000), a3);
1319 env.trust(g1[
"HKD"](100000), m1);
1320 env.trust(g2[
"HKD"](100000), m1);
1321 env.trust(g1[
"HKD"](100000), m2);
1322 env.trust(g2[
"HKD"](100000), m2);
1325 env(
pay(g1, a1, g1[
"HKD"](1000)));
1326 env(
pay(g2, a2, g2[
"HKD"](1000)));
1327 env(
pay(g1, a3, g1[
"HKD"](1000)));
1328 env(
pay(g1, m1, g1[
"HKD"](1200)));
1329 env(
pay(g2, m1, g2[
"HKD"](5000)));
1330 env(
pay(g1, m2, g1[
"HKD"](1200)));
1331 env(
pay(g2, m2, g2[
"HKD"](5000)));
1334 std::optional<UInt256> domainID;
1337 domainID =
setupDomain(env, {a1, a2, a3, a4, g1, g2, g3, g4, m1, m2});
1338 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
Domain(*domainID));
1344 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)));
1345 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)));
1346 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)));
1355 auto const& sendAmt = g1[
"HKD"](10);
1357 findPaths(env, a1, g1, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1358 BEAST_EXPECT(st.empty());
1359 BEAST_EXPECT(
equal(da, sendAmt));
1360 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1366 auto const& sendAmt = a1[
"HKD"](10);
1368 findPaths(env, a1, g1, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1369 BEAST_EXPECT(st.empty());
1370 BEAST_EXPECT(
equal(da, sendAmt));
1377 auto const& sendAmt = a3[
"HKD"](10);
1379 findPaths(env, a1, a3, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1380 BEAST_EXPECT(
equal(da, sendAmt));
1381 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1388 auto const& sendAmt = g2[
"HKD"](10);
1390 findPaths(env, g1, g2, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1391 BEAST_EXPECT(
equal(da, sendAmt));
1392 BEAST_EXPECT(
equal(sa, g1[
"HKD"](10)));
1404 auto const& sendAmt = g2[
"HKD"](10);
1406 findPaths(env, a1, g2, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1407 BEAST_EXPECT(
equal(da, sendAmt));
1408 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1421 auto const& sendAmt = a2[
"HKD"](10);
1423 findPaths(env, a1, a2, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1424 BEAST_EXPECT(
equal(da, sendAmt));
1425 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1439 std::string(
"Path Find: non-XRP -> non-XRP, same currency)") +
1440 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1441 using namespace jtx;
1450 env.fund(
XRP(11000), m1);
1451 env.fund(
XRP(1000), a1, a2, a3, g1, g2);
1454 env.trust(g1[
"HKD"](2000), a1);
1455 env.trust(g2[
"HKD"](2000), a2);
1456 env.trust(a2[
"HKD"](2000), a3);
1457 env.trust(g1[
"HKD"](100000), m1);
1458 env.trust(g2[
"HKD"](100000), m1);
1461 env(
pay(g1, a1, g1[
"HKD"](1000)));
1462 env(
pay(g2, a2, g2[
"HKD"](1000)));
1463 env(
pay(g1, m1, g1[
"HKD"](5000)));
1464 env(
pay(g2, m1, g2[
"HKD"](5000)));
1467 std::optional<UInt256> domainID;
1470 domainID =
setupDomain(env, {a1, a2, a3, g1, g2, m1});
1471 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
Domain(*domainID));
1475 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)));
1480 auto const& sendAmt = a2[
"HKD"](10);
1484 findPaths(env, g1, a2, sendAmt, std::nullopt, g1[
"HKD"].currency, domainID);
1485 BEAST_EXPECT(
equal(da, sendAmt));
1486 BEAST_EXPECT(
equal(sa, g1[
"HKD"](10)));
1493 testcase(std::string(
"Receive max") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
1495 using namespace jtx;
1496 auto const alice =
Account(
"alice");
1497 auto const bob =
Account(
"bob");
1498 auto const charlie =
Account(
"charlie");
1499 auto const gw =
Account(
"gw");
1500 auto const usd = gw[
"USD"];
1504 env.fund(
XRP(10000), alice, bob, charlie, gw);
1506 env.trust(usd(100), alice, bob, charlie);
1508 env(
pay(gw, charlie, usd(10)));
1514 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1520 env(
offer(charlie,
XRP(10), usd(10)));
1525 findPaths(env, alice, bob, usd(-1),
XRP(100).value(), std::nullopt, domainID);
1526 BEAST_EXPECT(sa ==
XRP(10));
1527 BEAST_EXPECT(
equal(da, usd(10)));
1528 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1530 auto const& pathElem = st[0][0];
1532 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
1533 pathElem.getCurrency() == usd.currency);
1539 env.fund(
XRP(10000), alice, bob, charlie, gw);
1541 env.trust(usd(100), alice, bob, charlie);
1543 env(
pay(gw, alice, usd(10)));
1546 std::optional<UInt256> domainID;
1549 domainID =
setupDomain(env, {alice, bob, charlie, gw});
1555 env(
offer(charlie, usd(10),
XRP(10)));
1560 findPaths(env, alice, bob,
drops(-1), usd(100).value(), std::nullopt, domainID);
1561 BEAST_EXPECT(sa == usd(10));
1563 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1565 auto const& pathElem = st[0][0];
1567 pathElem.isOffer() && pathElem.getIssuerID() ==
xrpAccount() &&
1576 using namespace jtx;
1581 auto const alice =
Account(
"alice");
1582 auto const bob =
Account(
"bob");
1583 auto const george =
Account(
"george");
1584 auto const usd = george[
"USD"];
1585 auto test = [&](
std::string casename,
bool aliceRipple,
bool bobRipple,
bool expectPath) {
1589 env.fund(
XRP(10000),
noripple(alice, bob, george));
1593 env(
trust(alice, usd(100), aliceRipple ? tfClearNoRipple : tfSetNoRipple));
1594 env(
trust(george, alice[
"USD"](100), aliceRipple ? tfClearNoRipple : tfSetNoRipple));
1595 env(
trust(bob, usd(100), bobRipple ? tfClearNoRipple : tfSetNoRipple));
1596 env(
trust(george, bob[
"USD"](100), bobRipple ? tfClearNoRipple : tfSetNoRipple));
1598 env(
pay(george, alice, usd(70)));
1601 auto [st, sa, da] =
findPaths(env,
"alice",
"bob",
Account(
"bob")[
"USD"](5));
1602 BEAST_EXPECT(
equal(da, bob[
"USD"](5)));
1606 BEAST_EXPECT(st.size() == 1);
1608 BEAST_EXPECT(
equal(sa, alice[
"USD"](5)));
1612 BEAST_EXPECT(st.empty());
1616 test(
"ripple -> ripple",
true,
true,
true);
1617 test(
"ripple -> no ripple",
true,
false,
true);
1618 test(
"no ripple -> ripple",
false,
true,
true);
1619 test(
"no ripple -> no ripple",
false,
false,
false);
1626 using namespace jtx;
1631 auto testPathfind = [&](
auto func,
bool const domainEnabled =
false) {
1644 env.fund(
XRP(1000), a1, a2, a3, g1, g2, g3, g4);
1645 env.fund(
XRP(10000), a4);
1646 env.fund(
XRP(11000), m1, m2);
1649 env.trust(g1[
"HKD"](2000), a1);
1650 env.trust(g2[
"HKD"](2000), a2);
1651 env.trust(g1[
"HKD"](2000), a3);
1652 env.trust(g1[
"HKD"](100000), m1);
1653 env.trust(g2[
"HKD"](100000), m1);
1654 env.trust(g1[
"HKD"](100000), m2);
1655 env.trust(g2[
"HKD"](100000), m2);
1658 env(
pay(g1, a1, g1[
"HKD"](1000)));
1659 env(
pay(g2, a2, g2[
"HKD"](1000)));
1660 env(
pay(g1, a3, g1[
"HKD"](1000)));
1661 env(
pay(g1, m1, g1[
"HKD"](1200)));
1662 env(
pay(g2, m1, g2[
"HKD"](5000)));
1663 env(
pay(g1, m2, g1[
"HKD"](1200)));
1664 env(
pay(g2, m2, g2[
"HKD"](5000)));
1667 std::optional<UInt256> domainID =
1668 setupDomain(env, {a1, a2, a3, a4, g1, g2, g3, g4, m1, m2});
1669 BEAST_EXPECT(domainID);
1671 func(env, m1, m2, g1, g2, *domainID);
1679 auto const& sendAmt = g1[
"HKD"](10);
1687 domainEnabled ? domainID : std::nullopt);
1688 BEAST_EXPECT(st.empty());
1689 BEAST_EXPECT(
equal(da, sendAmt));
1690 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1696 auto const& sendAmt = a1[
"HKD"](10);
1704 domainEnabled ? domainID : std::nullopt);
1705 BEAST_EXPECT(st.empty());
1706 BEAST_EXPECT(
equal(da, sendAmt));
1707 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1713 auto const& sendAmt = a3[
"HKD"](10);
1721 domainEnabled ? domainID : std::nullopt);
1722 BEAST_EXPECT(
equal(da, sendAmt));
1723 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1730 auto const& sendAmt = g2[
"HKD"](10);
1738 domainEnabled ? domainID : std::nullopt);
1739 BEAST_EXPECT(
equal(da, sendAmt));
1740 BEAST_EXPECT(
equal(sa, g1[
"HKD"](10)));
1752 auto const& sendAmt = g2[
"HKD"](10);
1760 domainEnabled ? domainID : std::nullopt);
1761 BEAST_EXPECT(
equal(da, sendAmt));
1762 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1775 auto const& sendAmt = a2[
"HKD"](10);
1783 domainEnabled ? domainID : std::nullopt);
1784 BEAST_EXPECT(
equal(da, sendAmt));
1785 BEAST_EXPECT(
equal(sa, a1[
"HKD"](10)));
1801 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1804 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)));
1805 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)));
1810 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1813 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1816 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)));
1821 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1824 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1827 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1834 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)));
1835 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)));
1836 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1843 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)));
1844 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1847 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1859 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1869 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
1872 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1881 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
Domain(domainID));
1883 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1891 env(
offer(m1, g1[
"HKD"](1000), g2[
"HKD"](1000)),
Domain(domainID));
1892 env(
offer(m2,
XRP(10000), g2[
"HKD"](1000)),
1895 env(
offer(m2, g1[
"HKD"](1000),
XRP(10000)),
1906 testcase(
"AMM not used in domain path");
1907 using namespace jtx;
1909 PermissionedDEX
const permDex(env);
1910 auto const& [gw_, domainOwner, alice_, bob_, carol_, USD, domainID, credType] = permDex;
1911 AMM
const amm(env, alice_,
XRP(10), USD(50));
1916 auto const& sendAmt =
XRP(1);
1920 findPaths(env, bob_, carol_, sendAmt, std::nullopt, USD.currency, domainID);
1921 BEAST_EXPECT(st.empty());
1924 std::tie(st, sa, da) =
findPaths(env, bob_, carol_, sendAmt, std::nullopt, USD.currency);
1931 testcase(
"STPathSet::assembleAdd deduplication — O(N^2) regression");
1933 static constexpr std::string_view kAccount1 =
"A3F19C7B2E5D08146FB93A7C0E2D5184BC6F3A09";
1934 static constexpr std::string_view kAccount2 =
"1D7E4B90C2A6F3851E0B9D47A2C5F8136E0A4B7D";
1935 static constexpr std::string_view kAccount3 =
"F08C36A1D95E27B40CA1F63E8D204B7950E1C3A6";
1936 static constexpr std::string_view kAccount4 =
"4B6209E7F1A3C85D0E94B27Af3D6018C5A7E92B4";
1937 static constexpr std::string_view kAccount5 =
"9E2D7041BCA3F6589D013E7B2A4C6F80159D3E7A";
1938 static constexpr std::string_view kAccount6 =
"7C5A91E384F2D06BA19C4E73D820F516B3A9C0E4";
1939 static constexpr std::string_view kAccount7 =
"2F8B043C6A1E9D75B0C38E14F6A2D509731BC4E8";
1940 static constexpr std::string_view kAccount8 =
"E61D9A30F47C285BA0D31E96C7B4F802513A8D6F";
1942 static constexpr AccountID kAccountID1{kAccount1};
1943 static constexpr AccountID kAccountID2{kAccount2};
1944 static constexpr AccountID kAccountID3{kAccount3};
1945 static constexpr AccountID kAccountID4{kAccount4};
1946 static constexpr AccountID kAccountID5{kAccount5};
1947 static constexpr AccountID kAccountID6{kAccount6};
1948 static constexpr AccountID kAccountID7{kAccount7};
1949 static constexpr AccountID kAccountID8{kAccount8};
1951 auto ps = STPathSet{STPathSet::DeduplicationTag{}};
1953 auto createPathElements = [](
auto const& account1,
auto const& account2) {
1954 auto base = STPath{};
1963 auto [base, tail] = createPathElements(kAccountID1, kAccountID2);
1965 for (
auto i = 0uz; i < 10000; ++i)
1967 ps.assembleAdd(base, tail);
1970 BEAST_EXPECT(ps.size() == 1);
1974 auto [base, tail] = createPathElements(kAccountID3, kAccountID4);
1975 ps.assembleAdd(base, tail);
1979 auto [base, tail] = createPathElements(kAccountID5, kAccountID6);
1980 ps.assembleAdd(base, tail);
1984 auto [base, tail] = createPathElements(kAccountID7, kAccountID8);
1986 auto before = ps.size();
1988 for (
auto i = 0uz; i < 10000; ++i)
1990 ps.assembleAdd(base, tail);
1993 BEAST_EXPECT(ps.size() - before == 1);
1997 auto [base, tail] = createPathElements(kAccountID1, kAccountID3);
1999 copy.pushBack(tail);
2001 auto before = ps.size();
2004 ps.assembleAdd(base, tail);
2006 BEAST_EXPECT(ps.size() - before == 1);
2010 auto [base, tail] = createPathElements(kAccountID2, kAccountID4);
2012 copy.pushBack(tail);
2014 auto before = ps.size();
2016 ps.emplaceBack(copy);
2017 ps.assembleAdd(base, tail);
2019 BEAST_EXPECT(ps.size() - before == 1);
2022 BEAST_EXPECT(ps.size() == 6);
2028 testcase(
"STPathSet::pushBack/emplaceBack deduplication");
2036 static constexpr AccountID kAccountID1{
"A3F19C7B2E5D08146FB93A7C0E2D5184BC6F3A09"};
2037 static constexpr AccountID kAccountID2{
"1D7E4B90C2A6F3851E0B9D47A2C5F8136E0A4B7D"};
2038 static constexpr AccountID kAccountID3{
"F08C36A1D95E27B40CA1F63E8D204B7950E1C3A6"};
2040 auto makePath = [](
AccountID const& account) {
2046 auto const first = makePath(kAccountID1);
2047 auto const second = makePath(kAccountID2);
2048 auto const third = makePath(kAccountID3);
2053 auto ps = STPathSet{STPathSet::DeduplicationTag{}};
2055 BEAST_EXPECT(ps.pushBack(first));
2056 BEAST_EXPECT(ps.size() == 1);
2058 BEAST_EXPECT(!ps.pushBack(first));
2059 BEAST_EXPECT(ps.size() == 1);
2062 BEAST_EXPECT(!ps.emplaceBack(first));
2063 BEAST_EXPECT(ps.size() == 1);
2065 BEAST_EXPECT(ps.emplaceBack(second));
2066 BEAST_EXPECT(ps.size() == 2);
2069 BEAST_EXPECT(!ps.pushBack(second));
2070 BEAST_EXPECT(ps.size() == 2);
2073 BEAST_EXPECT(ps.emplaceBack(std::vector<STPathElement>{third.front()}));
2074 BEAST_EXPECT(ps.size() == 3);
2075 BEAST_EXPECT(!ps.pushBack(third));
2076 BEAST_EXPECT(ps.size() == 3);
2079 BEAST_EXPECT(ps[0] == first);
2080 BEAST_EXPECT(ps[1] == second);
2081 BEAST_EXPECT(ps[2] == third);
2087 auto plain = STPathSet{};
2088 BEAST_EXPECT(plain.pushBack(first));
2089 BEAST_EXPECT(plain.pushBack(first));
2090 BEAST_EXPECT(plain.emplaceBack(first));
2091 BEAST_EXPECT(plain.size() == 3);
2093 auto named = STPathSet{sfPaths};
2094 BEAST_EXPECT(named.pushBack(first));
2095 BEAST_EXPECT(named.pushBack(first));
2096 BEAST_EXPECT(named.size() == 2);
2103 testcase(
"STPathElement hash injectivity");
2105 auto const zeroCurrency =
2109 BEAST_EXPECT(!(zeroCurrency == zeroMPT));
2111 auto path = [](std::vector<STPathElement>
const& elements) {
2113 for (
auto const& element : elements)
2114 p.pushBack(element);
2118 auto const currencyFirst = path({zeroCurrency, zeroMPT});
2119 auto const mptFirst =
path({zeroMPT, zeroCurrency});
2121 BEAST_EXPECT(!(currencyFirst == mptFirst));
2124 BEAST_EXPECT(hasher(currencyFirst) != hasher(mptFirst));
2128 auto orderings = 0uz;
2131 auto elements = std::vector<STPathElement>{};
2132 for (
auto const isMPT : mask)
2134 elements.push_back(isMPT != 0 ? zeroMPT : zeroCurrency);
2136 hashes.insert(hasher(path(elements)));
2140 BEAST_EXPECT(orderings == 6);
2141 BEAST_EXPECT(hashes.size() == orderings);
2144 for (
auto const& p : {currencyFirst, mptFirst})
2148 BEAST_EXPECT(seen.size() == 2);
2155 static constexpr AccountID kAccount{
"A3F19C7B2E5D08146FB93A7C0E2D5184BC6F3A09"};
2156 static constexpr AccountID kIssuer{
"1D7E4B90C2A6F3851E0B9D47A2C5F8136E0A4B7D"};
2158 auto const equivalent = std::vector<std::pair<STPathElement, STPathElement>>{
2160 {STPathElement{kAccount, PathAsset{
xrpCurrency()}, kIssuer,
true},
2161 STPathElement{kAccount, PathAsset{
xrpCurrency()}, kIssuer,
false}},
2164 STPathElement{kAccount, PathAsset{
xrpCurrency()}, kIssuer,
false}},
2167 for (
auto const& [lhs, rhs] : equivalent)
2169 BEAST_EXPECT(lhs.getNodeType() != rhs.getNodeType());
2170 BEAST_EXPECT(lhs == rhs);
2172 auto const lhsPath =
path({lhs});
2173 auto const rhsPath =
path({rhs});
2174 BEAST_EXPECT(hasher(lhsPath) == hasher(rhsPath));
2177 equal.emplace(lhsPath);
2178 equal.emplace(rhsPath);
2179 BEAST_EXPECT(
equal.size() == 1);
2186 testcase(
"STPathSet deserialization preserves duplicate paths");
2193 static constexpr AccountID kAccountID1{
"A3F19C7B2E5D08146FB93A7C0E2D5184BC6F3A09"};
2194 static constexpr AccountID kAccountID2{
"1D7E4B90C2A6F3851E0B9D47A2C5F8136E0A4B7D"};
2196 auto const element =
2197 STPathElement{kAccountID1, PathAsset{
xrpCurrency()}, kAccountID2,
true};
2199 auto path = STPath{};
2200 path.pushBack(element);
2202 static constexpr auto kDuplicates = 64uz;
2204 auto original = STPathSet{sfPaths};
2205 for (
auto i = 0uz; i < kDuplicates; ++i)
2207 original.pushBack(path);
2211 BEAST_EXPECT(original.size() == kDuplicates);
2213 auto s = Serializer{};
2216 auto sit = SerialIter{s.slice()};
2217 auto const parsed = STPathSet{sit, sfPaths};
2220 BEAST_EXPECT(parsed.size() == kDuplicates);
2221 BEAST_EXPECT(parsed.isEquivalent(original));
2224 auto serialized = Serializer{};
2225 parsed.add(serialized);
2226 BEAST_EXPECT(serialized.getData() == s.getData());
2229 auto appended = parsed;
2230 appended.pushBack(path);
2231 BEAST_EXPECT(appended.size() == kDuplicates + 1);
2255 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.
Seed functor once per construction.
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.
An endpoint that consumes resources.
std::condition_variable cv_
bool waitFor(std::chrono::duration< Rep, Period > const &relTime)
void pathFind06(bool const domainEnabled)
void issuesPathNegativeRippleClientIssue23Larger()
void testAssembleAddDeduplication()
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)
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, std::optional< AccountID > const &saSrcIssuer=std::nullopt)
void receiveMax(bool const domainEnabled)
void alternativePathsLimitReturnedPathsToBestQuality(bool const domainEnabled)
void testPathHashInjectivity()
void pathFind05(bool const domainEnabled)
void viaOffersViaGateway(bool const domainEnabled)
void trustAutoClearTrustNormalClear()
void paymentAutoPathFind()
void directPathNoIntermediary()
void alternativePathConsumeBoth(bool const domainEnabled)
void norippleCombinations()
void testPushBackDeduplication()
void issuesPathNegativeRippleClientIssue23Smaller()
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, std::optional< AccountID > const &saSrcIssuer=std::nullopt)
void testDeserializationPreservesDuplicates()
void pathFind02(bool const domainEnabled)
void indirectPathsPathFind()
void noDirectPathNoIntermediaryNoAlternatives()
void sourceCurrencyIssuerSelection()
void qualityPathsQualitySetAndTest()
void pathFind(bool const domainEnabled)
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).
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Charge const kFeeReferenceRpc
static constexpr int kMaxSrcCur
Maximum number of source currencies allowed in a path find request.
static constexpr int kMaxAutoSrcCur
Maximum number of auto source currencies 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.
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)
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.
UInt256 setupDomain(jtx::Env &env, std::vector< jtx::Account > const &accounts, jtx::Account const &domainOwner, std::string const &credType)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
STPath stpath(Args const &... args)
json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
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.
std::unordered_set< Value, Hash, Pred, Allocator > HardenedHashSet
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.
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Currency const & xrpCurrency()
XRP currency.
std::string to_string(BaseUInt< Bits, Tag > const &a)
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
STAmount amountFromJson(SField const &name, json::Value const &v)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
AccountID const & xrpAccount()
Compute AccountID from public key.
T next_permutation(T... args)
std::shared_ptr< JobQueue::Coro > coro