1#include <test/jtx/Account.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/TestHelpers.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/balance.h>
6#include <test/jtx/domain.h>
7#include <test/jtx/envconfig.h>
8#include <test/jtx/mpt.h>
9#include <test/jtx/offer.h>
10#include <test/jtx/pay.h>
11#include <test/jtx/permissioned_dex.h>
13#include <xrpld/core/Config.h>
14#include <xrpld/rpc/Context.h>
15#include <xrpld/rpc/RPCHandler.h>
16#include <xrpld/rpc/Role.h>
17#include <xrpld/rpc/detail/RPCHelpers.h>
18#include <xrpld/rpc/detail/Tuning.h>
20#include <xrpl/basics/base_uint.h>
21#include <xrpl/beast/unit_test/suite.h>
22#include <xrpl/core/Job.h>
23#include <xrpl/core/JobQueue.h>
24#include <xrpl/json/json_value.h>
25#include <xrpl/protocol/AccountID.h>
26#include <xrpl/protocol/ApiVersion.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/STAmount.h>
29#include <xrpl/protocol/STPathSet.h>
30#include <xrpl/protocol/UintTypes.h>
31#include <xrpl/protocol/jss.h>
32#include <xrpl/resource/Charge.h>
33#include <xrpl/resource/Consumer.h>
34#include <xrpl/resource/Fees.h>
52 jv[jss::command] =
"ripple_path_find";
53 jv[jss::source_account] =
toBase58(src);
59 for (
auto const&
id : numSrc)
67 jv[jss::destination_account] = d;
90 cfg->pathSearchOld = 7;
92 cfg->pathSearchMax = 10;
102 using namespace std::chrono_literals;
105 auto const gw =
Account(
"gateway");
106 auto const alice =
Account(
"alice");
107 auto const bob =
Account(
"bob");
109 env.
fund(
XRP(10'000),
"alice",
"bob", gw);
112 MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 100});
114 auto& app = env.
app();
121 .loadType = loadType,
122 .netOps = app.getOPs(),
123 .ledgerMaster = app.getLedgerMaster(),
138 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
145 BEAST_EXPECT(!result.
isMember(jss::error));
149 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
156 BEAST_EXPECT(result.
isMember(jss::error));
162 auto curm =
MPTTester({.env = env, .issuer = alice, .holders = {bob}});
165 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
172 BEAST_EXPECT(!result.
isMember(jss::error));
175 auto curm =
MPTTester({.env = env, .issuer = alice, .holders = {bob}});
176 app.getJobQueue().postCoro(
JtClient,
"RPC-Client", [&](
auto const& coro) {
183 BEAST_EXPECT(result.
isMember(jss::error));
189 testcase(
"no direct path no intermediary no alternatives");
194 env.
fund(
XRP(10'000),
"alice",
"bob");
196 auto usdm =
MPTTester({.env = env, .issuer =
"bob"});
198 auto const result =
findPaths(env,
"alice",
"bob", usdm(5));
199 BEAST_EXPECT(std::get<0>(result).empty());
205 testcase(
"direct path no intermediary");
208 env.
fund(
XRP(10'000),
"alice",
"bob");
210 MPT const usd =
MPTTester({.env = env, .issuer =
"alice", .holders = {
"bob"}});
215 BEAST_EXPECT(st.empty());
216 BEAST_EXPECT(
equal(sa, usd(5)));
225 auto const gw =
Account(
"gateway");
226 env.
fund(
XRP(10'000),
"alice",
"bob", gw);
227 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {
"alice",
"bob"}});
228 env(
pay(gw,
"alice", usd(70)));
229 env(
pay(
"alice",
"bob", usd(24)));
240 auto const gw =
Account(
"gateway");
241 env.
fund(
XRP(10'000),
"alice",
"bob", gw);
242 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {
"alice",
"bob"}});
243 env(
pay(gw,
"alice", usd(70)));
244 env(
pay(gw,
"bob", usd(50)));
254 env,
"alice",
"bob", usd(5), std::nullopt, std::nullopt, std::nullopt, domainID);
257 BEAST_EXPECT(st.empty());
258 BEAST_EXPECT(
equal(sa, usd(5)));
259 BEAST_EXPECT(
equal(da, usd(5)));
266 std::string(
"path find consume all") + (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
271 auto const gw =
Account(
"gateway");
272 env.
fund(
XRP(10'000),
"alice",
"bob",
"carol", gw);
273 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {
"bob",
"carol"}});
275 env(
pay(gw,
"carol", usd(100)));
279 domainID =
setupDomain(env, {
"alice",
"bob",
"carol",
"gateway"});
284 env(
offer(
"carol",
XRP(100), usd(100)));
300 BEAST_EXPECT(st.
empty());
310 if (BEAST_EXPECT(st.
size() == 1 && st[0].
size() == 1))
312 auto const& pathElem = st[0][0];
314 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
317 BEAST_EXPECT(sa ==
XRP(100));
318 BEAST_EXPECT(
equal(da, usd(100)));
332 BEAST_EXPECT(st.
empty());
341 std::string(
"alternative path consume best transfer") +
342 (domainEnabled ?
" w/ " :
" w/o ") +
"domain");
345 auto const gw =
Account(
"gateway");
346 auto const gw2 =
Account(
"gateway2");
347 env.
fund(
XRP(10'000),
"alice",
"bob", gw, gw2);
348 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {
"alice",
"bob"}});
350 {.env = env, .issuer = gw2, .holders = {
"alice",
"bob"}, .transferFee = 1'000});
354 domainID =
setupDomain(env, {
"alice",
"bob",
"gateway",
"gateway2"});
355 env(
pay(gw,
"alice", usd(70)),
Domain(*domainID));
356 env(
pay(gw2,
"alice", gw2Usd(70)),
Domain(*domainID));
357 env(
pay(
"alice",
"bob", usd(70)),
Domain(*domainID));
361 env(
pay(gw,
"alice", usd(70)));
362 env(
pay(gw2,
"alice", gw2Usd(70)));
363 env(
pay(
"alice",
"bob", usd(70)));
376 auto const alice =
Account(
"alice");
377 auto const bob =
Account(
"bob");
378 auto const charlie =
Account(
"charlie");
383 env.
fund(
XRP(10'000), alice, bob, charlie, gw);
385 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob, charlie}});
386 env(
pay(gw, charlie, usd(10)));
391 domainID =
setupDomain(env, {alice, bob, charlie, gw});
396 env(
offer(charlie,
XRP(10), usd(10)));
400 env, alice, bob, usd(-1),
XRP(100).value(), std::nullopt, std::nullopt, domainID);
401 BEAST_EXPECT(sa ==
XRP(10));
402 BEAST_EXPECT(
equal(da, usd(10)));
403 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
405 auto const& pathElem = st[0][0];
407 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
408 pathElem.getMPTID() == usd.
mpt());
414 env.
fund(
XRP(10'000), alice, bob, charlie, gw);
416 MPT const usd =
MPTTester({.env = env, .issuer = gw, .holders = {alice, bob, charlie}});
417 env(
pay(gw, alice, usd(10)));
422 domainID =
setupDomain(env, {alice, bob, charlie, gw});
427 env(
offer(charlie, usd(10),
XRP(10)));
431 env, alice, bob,
drops(-1), usd(100).value(), std::nullopt, std::nullopt, domainID);
432 BEAST_EXPECT(sa == usd(10));
434 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
436 auto const& pathElem = st[0][0];
438 pathElem.isOffer() && pathElem.getIssuerID() ==
xrpAccount() &&
451 for (
auto const domainEnabled : {
false,
true})
TestcaseT testcase
Memberspace for declaring test cases.
bool isMember(char const *key) const
Return true if the object has a member named key.
An endpoint that consumes resources.
std::vector< STPath >::size_type size() const
void pathFindConsumeAll(bool const domainEnabled)
void noDirectPathNoIntermediaryNoAlternatives()
void receiveMax(bool const domainEnabled)
void sourceCurrenciesLimit()
void directPathNoIntermediary()
void paymentAutoPathFind()
void run() override
Runs the suite.
void alternativePathsConsumeBestTransfer(bool const domainEnabled)
void pathFind(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.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
beast::Journal const journal
void require(Args const &... args)
Check a set of requirements.
bool waitFor(std::chrono::duration< Rep, Period > const &relTime)
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
Converts to MPT Issue or STAmount.
xrpl::MPTID const & mpt() const
@ 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
static json::Value rpf(jtx::Account const &src, jtx::Account const &dst, xrpl::test::jtx::MPT const &usd, std::vector< MPTID > const &numSrc)
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
XrpT const XRP
Converts to XRP Issue or STAmount.
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::optional< PathAsset > const &srcAsset, std::optional< AccountID > const &srcIssuer, std::optional< uint256 > const &domain)
uint256 setupDomain(jtx::Env &env, std::vector< jtx::Account > const &accounts, jtx::Account const &domainOwner, std::string const &credType)
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
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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)
AccountID const & xrpAccount()
Compute AccountID from public key.
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
std::shared_ptr< JobQueue::Coro > coro