rippled
Loading...
Searching...
No Matches
Path_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2015 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx.h>
21#include <test/jtx/envconfig.h>
22#include <xrpld/app/paths/AccountCurrencies.h>
23#include <xrpld/core/JobQueue.h>
24#include <xrpld/rpc/Context.h>
25#include <xrpld/rpc/RPCHandler.h>
26#include <xrpld/rpc/detail/RPCHelpers.h>
27#include <xrpld/rpc/detail/Tuning.h>
28#include <xrpl/beast/unit_test.h>
29#include <xrpl/json/json_reader.h>
30#include <xrpl/protocol/STParsedJSON.h>
31#include <xrpl/protocol/TxFlags.h>
32#include <xrpl/protocol/jss.h>
33#include <xrpl/resource/Fees.h>
34
35#include <mutex>
36
37namespace ripple {
38namespace test {
39
40//------------------------------------------------------------------------------
41
43rpf(jtx::Account const& src, jtx::Account const& dst, std::uint32_t num_src)
44{
46 jv[jss::command] = "ripple_path_find";
47 jv[jss::source_account] = toBase58(src);
48
49 if (num_src > 0)
50 {
51 auto& sc = (jv[jss::source_currencies] = Json::arrayValue);
53 while (num_src--)
54 {
55 j[jss::currency] = std::to_string(num_src + 100);
56 sc.append(j);
57 }
58 }
59
60 auto const d = toBase58(dst);
61 jv[jss::destination_account] = d;
62
63 Json::Value& j = (jv[jss::destination_amount] = Json::objectValue);
64 j[jss::currency] = "USD";
65 j[jss::value] = "0.01";
66 j[jss::issuer] = d;
67
68 return jv;
69}
70
71//------------------------------------------------------------------------------
72
74{
77 {
78 // These tests were originally written with search parameters that are
79 // different from the current defaults. This function creates an env
80 // with the search parameters that the tests were written for.
81 using namespace jtx;
82 return Env(*this, envconfig([](std::unique_ptr<Config> cfg) {
83 cfg->PATH_SEARCH_OLD = 7;
84 cfg->PATH_SEARCH = 7;
85 cfg->PATH_SEARCH_MAX = 10;
86 return cfg;
87 }));
88 }
89
90public:
91 class gate
92 {
93 private:
96 bool signaled_ = false;
97
98 public:
99 // Thread safe, blocks until signaled or period expires.
100 // Returns `true` if signaled.
101 template <class Rep, class Period>
102 bool
104 {
106 auto b = cv_.wait_for(lk, rel_time, [this] { return signaled_; });
107 signaled_ = false;
108 return b;
109 }
110
111 void
113 {
115 signaled_ = true;
116 cv_.notify_all();
117 }
118 };
119
120 auto
122 jtx::Env& env,
123 jtx::Account const& src,
124 jtx::Account const& dst,
125 STAmount const& saDstAmount,
126 std::optional<STAmount> const& saSendMax = std::nullopt,
127 std::optional<Currency> const& saSrcCurrency = std::nullopt)
128 {
129 using namespace jtx;
130
131 auto& app = env.app();
134
135 RPC::JsonContext context{
136 {env.journal,
137 app,
138 loadType,
139 app.getOPs(),
140 app.getLedgerMaster(),
141 c,
143 {},
144 {},
146 {},
147 {}};
148
150 params[jss::command] = "ripple_path_find";
151 params[jss::source_account] = toBase58(src);
152 params[jss::destination_account] = toBase58(dst);
153 params[jss::destination_amount] =
154 saDstAmount.getJson(JsonOptions::none);
155 if (saSendMax)
156 params[jss::send_max] = saSendMax->getJson(JsonOptions::none);
157 if (saSrcCurrency)
158 {
159 auto& sc = params[jss::source_currencies] = Json::arrayValue;
161 j[jss::currency] = to_string(saSrcCurrency.value());
162 sc.append(j);
163 }
164
165 Json::Value result;
166 gate g;
167 app.getJobQueue().postCoro(
168 jtCLIENT, "RPC-Client", [&](auto const& coro) {
169 context.params = std::move(params);
170 context.coro = coro;
171 RPC::doCommand(context, result);
172 g.signal();
173 });
174
175 using namespace std::chrono_literals;
176 BEAST_EXPECT(g.wait_for(5s));
177 BEAST_EXPECT(!result.isMember(jss::error));
178 return result;
179 }
180
183 jtx::Env& env,
184 jtx::Account const& src,
185 jtx::Account const& dst,
186 STAmount const& saDstAmount,
187 std::optional<STAmount> const& saSendMax = std::nullopt,
188 std::optional<Currency> const& saSrcCurrency = std::nullopt)
189 {
191 env, src, dst, saDstAmount, saSendMax, saSrcCurrency);
192 BEAST_EXPECT(!result.isMember(jss::error));
193
194 STAmount da;
195 if (result.isMember(jss::destination_amount))
196 da = amountFromJson(sfGeneric, result[jss::destination_amount]);
197
198 STAmount sa;
200 if (result.isMember(jss::alternatives))
201 {
202 auto const& alts = result[jss::alternatives];
203 if (alts.size() > 0)
204 {
205 auto const& path = alts[0u];
206
207 if (path.isMember(jss::source_amount))
208 sa = amountFromJson(sfGeneric, path[jss::source_amount]);
209
210 if (path.isMember(jss::destination_amount))
211 da = amountFromJson(
212 sfGeneric, path[jss::destination_amount]);
213
214 if (path.isMember(jss::paths_computed))
215 {
216 Json::Value p;
217 p["Paths"] = path[jss::paths_computed];
218 STParsedJSONObject po("generic", p);
219 paths = po.object->getFieldPathSet(sfPaths);
220 }
221 }
222 }
223
224 return std::make_tuple(std::move(paths), std::move(sa), std::move(da));
225 }
226
227 void
229 {
230 testcase("source currency limits");
231 using namespace std::chrono_literals;
232 using namespace jtx;
233 Env env = pathTestEnv();
234 auto const gw = Account("gateway");
235 env.fund(XRP(10000), "alice", "bob", gw);
236 env.trust(gw["USD"](100), "alice", "bob");
237 env.close();
238
239 auto& app = env.app();
242
243 RPC::JsonContext context{
244 {env.journal,
245 app,
246 loadType,
247 app.getOPs(),
248 app.getLedgerMaster(),
249 c,
251 {},
252 {},
254 {},
255 {}};
256 Json::Value result;
257 gate g;
258 // Test RPC::Tuning::max_src_cur source currencies.
259 app.getJobQueue().postCoro(
260 jtCLIENT, "RPC-Client", [&](auto const& coro) {
261 context.params = rpf(
262 Account("alice"), Account("bob"), RPC::Tuning::max_src_cur);
263 context.coro = coro;
264 RPC::doCommand(context, result);
265 g.signal();
266 });
267 BEAST_EXPECT(g.wait_for(5s));
268 BEAST_EXPECT(!result.isMember(jss::error));
269
270 // Test more than RPC::Tuning::max_src_cur source currencies.
271 app.getJobQueue().postCoro(
272 jtCLIENT, "RPC-Client", [&](auto const& coro) {
273 context.params =
274 rpf(Account("alice"),
275 Account("bob"),
277 context.coro = coro;
278 RPC::doCommand(context, result);
279 g.signal();
280 });
281 BEAST_EXPECT(g.wait_for(5s));
282 BEAST_EXPECT(result.isMember(jss::error));
283
284 // Test RPC::Tuning::max_auto_src_cur source currencies.
285 for (auto i = 0; i < (RPC::Tuning::max_auto_src_cur - 1); ++i)
286 env.trust(Account("alice")[std::to_string(i + 100)](100), "bob");
287 app.getJobQueue().postCoro(
288 jtCLIENT, "RPC-Client", [&](auto const& coro) {
289 context.params = rpf(Account("alice"), Account("bob"), 0);
290 context.coro = coro;
291 RPC::doCommand(context, result);
292 g.signal();
293 });
294 BEAST_EXPECT(g.wait_for(5s));
295 BEAST_EXPECT(!result.isMember(jss::error));
296
297 // Test more than RPC::Tuning::max_auto_src_cur source currencies.
298 env.trust(Account("alice")["AUD"](100), "bob");
299 app.getJobQueue().postCoro(
300 jtCLIENT, "RPC-Client", [&](auto const& coro) {
301 context.params = rpf(Account("alice"), Account("bob"), 0);
302 context.coro = coro;
303 RPC::doCommand(context, result);
304 g.signal();
305 });
306 BEAST_EXPECT(g.wait_for(5s));
307 BEAST_EXPECT(result.isMember(jss::error));
308 }
309
310 void
312 {
313 testcase("no direct path no intermediary no alternatives");
314 using namespace jtx;
315 Env env = pathTestEnv();
316 env.fund(XRP(10000), "alice", "bob");
317
318 auto const result =
319 find_paths(env, "alice", "bob", Account("bob")["USD"](5));
320 BEAST_EXPECT(std::get<0>(result).empty());
321 }
322
323 void
325 {
326 testcase("direct path no intermediary");
327 using namespace jtx;
328 Env env = pathTestEnv();
329 env.fund(XRP(10000), "alice", "bob");
330 env.trust(Account("alice")["USD"](700), "bob");
331
332 STPathSet st;
333 STAmount sa;
334 std::tie(st, sa, std::ignore) =
335 find_paths(env, "alice", "bob", Account("bob")["USD"](5));
336 BEAST_EXPECT(st.empty());
337 BEAST_EXPECT(equal(sa, Account("alice")["USD"](5)));
338 }
339
340 void
342 {
343 testcase("payment auto path find");
344 using namespace jtx;
345 Env env = pathTestEnv();
346 auto const gw = Account("gateway");
347 auto const USD = gw["USD"];
348 env.fund(XRP(10000), "alice", "bob", gw);
349 env.trust(USD(600), "alice");
350 env.trust(USD(700), "bob");
351 env(pay(gw, "alice", USD(70)));
352 env(pay("alice", "bob", USD(24)));
353 env.require(balance("alice", USD(46)));
354 env.require(balance(gw, Account("alice")["USD"](-46)));
355 env.require(balance("bob", USD(24)));
356 env.require(balance(gw, Account("bob")["USD"](-24)));
357 }
358
359 void
361 {
362 testcase("path find");
363 using namespace jtx;
364 Env env = pathTestEnv();
365 auto const gw = Account("gateway");
366 auto const USD = gw["USD"];
367 env.fund(XRP(10000), "alice", "bob", gw);
368 env.trust(USD(600), "alice");
369 env.trust(USD(700), "bob");
370 env(pay(gw, "alice", USD(70)));
371 env(pay(gw, "bob", USD(50)));
372
373 STPathSet st;
374 STAmount sa;
375 std::tie(st, sa, std::ignore) =
376 find_paths(env, "alice", "bob", Account("bob")["USD"](5));
377 BEAST_EXPECT(same(st, stpath("gateway")));
378 BEAST_EXPECT(equal(sa, Account("alice")["USD"](5)));
379 }
380
381 void
383 {
384 using namespace jtx;
385 testcase("XRP to XRP");
386 Env env = pathTestEnv();
387 env.fund(XRP(10000), "alice", "bob");
388
389 auto const result = find_paths(env, "alice", "bob", XRP(5));
390 BEAST_EXPECT(std::get<0>(result).empty());
391 }
392
393 void
395 {
396 testcase("path find consume all");
397 using namespace jtx;
398
399 {
400 Env env = pathTestEnv();
401 env.fund(XRP(10000), "alice", "bob", "carol", "dan", "edward");
402 env.trust(Account("alice")["USD"](10), "bob");
403 env.trust(Account("bob")["USD"](10), "carol");
404 env.trust(Account("carol")["USD"](10), "edward");
405 env.trust(Account("alice")["USD"](100), "dan");
406 env.trust(Account("dan")["USD"](100), "edward");
407
408 STPathSet st;
409 STAmount sa;
410 STAmount da;
411 std::tie(st, sa, da) = find_paths(
412 env, "alice", "edward", Account("edward")["USD"](-1));
413 BEAST_EXPECT(same(st, stpath("dan"), stpath("bob", "carol")));
414 BEAST_EXPECT(equal(sa, Account("alice")["USD"](110)));
415 BEAST_EXPECT(equal(da, Account("edward")["USD"](110)));
416 }
417
418 {
419 Env env = pathTestEnv();
420 auto const gw = Account("gateway");
421 auto const USD = gw["USD"];
422 env.fund(XRP(10000), "alice", "bob", "carol", gw);
423 env.trust(USD(100), "bob", "carol");
424 env(pay(gw, "carol", USD(100)));
425 env(offer("carol", XRP(100), USD(100)));
426
427 STPathSet st;
428 STAmount sa;
429 STAmount da;
430 std::tie(st, sa, da) = find_paths(
431 env,
432 "alice",
433 "bob",
434 Account("bob")["AUD"](-1),
435 std::optional<STAmount>(XRP(100000000)));
436 BEAST_EXPECT(st.empty());
437 std::tie(st, sa, da) = find_paths(
438 env,
439 "alice",
440 "bob",
441 Account("bob")["USD"](-1),
442 std::optional<STAmount>(XRP(100000000)));
443 BEAST_EXPECT(sa == XRP(100));
444 BEAST_EXPECT(equal(da, Account("bob")["USD"](100)));
445 }
446 }
447
448 void
450 {
451 testcase("alternative path consume both");
452 using namespace jtx;
453 Env env = pathTestEnv();
454 auto const gw = Account("gateway");
455 auto const USD = gw["USD"];
456 auto const gw2 = Account("gateway2");
457 auto const gw2_USD = gw2["USD"];
458 env.fund(XRP(10000), "alice", "bob", gw, gw2);
459 env.trust(USD(600), "alice");
460 env.trust(gw2_USD(800), "alice");
461 env.trust(USD(700), "bob");
462 env.trust(gw2_USD(900), "bob");
463 env(pay(gw, "alice", USD(70)));
464 env(pay(gw2, "alice", gw2_USD(70)));
465 env(pay("alice", "bob", Account("bob")["USD"](140)),
466 paths(Account("alice")["USD"]));
467 env.require(balance("alice", USD(0)));
468 env.require(balance("alice", gw2_USD(0)));
469 env.require(balance("bob", USD(70)));
470 env.require(balance("bob", gw2_USD(70)));
471 env.require(balance(gw, Account("alice")["USD"](0)));
472 env.require(balance(gw, Account("bob")["USD"](-70)));
473 env.require(balance(gw2, Account("alice")["USD"](0)));
474 env.require(balance(gw2, Account("bob")["USD"](-70)));
475 }
476
477 void
479 {
480 testcase("alternative paths consume best transfer");
481 using namespace jtx;
482 Env env = pathTestEnv();
483 auto const gw = Account("gateway");
484 auto const USD = gw["USD"];
485 auto const gw2 = Account("gateway2");
486 auto const gw2_USD = gw2["USD"];
487 env.fund(XRP(10000), "alice", "bob", gw, gw2);
488 env(rate(gw2, 1.1));
489 env.trust(USD(600), "alice");
490 env.trust(gw2_USD(800), "alice");
491 env.trust(USD(700), "bob");
492 env.trust(gw2_USD(900), "bob");
493 env(pay(gw, "alice", USD(70)));
494 env(pay(gw2, "alice", gw2_USD(70)));
495 env(pay("alice", "bob", USD(70)));
496 env.require(balance("alice", USD(0)));
497 env.require(balance("alice", gw2_USD(70)));
498 env.require(balance("bob", USD(70)));
499 env.require(balance("bob", gw2_USD(0)));
500 env.require(balance(gw, Account("alice")["USD"](0)));
501 env.require(balance(gw, Account("bob")["USD"](-70)));
502 env.require(balance(gw2, Account("alice")["USD"](-70)));
503 env.require(balance(gw2, Account("bob")["USD"](0)));
504 }
505
506 void
508 {
509 testcase("alternative paths - consume best transfer first");
510 using namespace jtx;
511 Env env = pathTestEnv();
512 auto const gw = Account("gateway");
513 auto const USD = gw["USD"];
514 auto const gw2 = Account("gateway2");
515 auto const gw2_USD = gw2["USD"];
516 env.fund(XRP(10000), "alice", "bob", gw, gw2);
517 env(rate(gw2, 1.1));
518 env.trust(USD(600), "alice");
519 env.trust(gw2_USD(800), "alice");
520 env.trust(USD(700), "bob");
521 env.trust(gw2_USD(900), "bob");
522 env(pay(gw, "alice", USD(70)));
523 env(pay(gw2, "alice", gw2_USD(70)));
524 env(pay("alice", "bob", Account("bob")["USD"](77)),
525 sendmax(Account("alice")["USD"](100)),
526 paths(Account("alice")["USD"]));
527 env.require(balance("alice", USD(0)));
528 env.require(balance("alice", gw2_USD(62.3)));
529 env.require(balance("bob", USD(70)));
530 env.require(balance("bob", gw2_USD(7)));
531 env.require(balance(gw, Account("alice")["USD"](0)));
532 env.require(balance(gw, Account("bob")["USD"](-70)));
533 env.require(balance(gw2, Account("alice")["USD"](-62.3)));
534 env.require(balance(gw2, Account("bob")["USD"](-7)));
535 }
536
537 void
539 {
540 testcase("alternative paths - limit returned paths to best quality");
541 using namespace jtx;
542 Env env = pathTestEnv();
543 auto const gw = Account("gateway");
544 auto const USD = gw["USD"];
545 auto const gw2 = Account("gateway2");
546 auto const gw2_USD = gw2["USD"];
547 env.fund(XRP(10000), "alice", "bob", "carol", "dan", gw, gw2);
548 env(rate("carol", 1.1));
549 env.trust(Account("carol")["USD"](800), "alice", "bob");
550 env.trust(Account("dan")["USD"](800), "alice", "bob");
551 env.trust(USD(800), "alice", "bob");
552 env.trust(gw2_USD(800), "alice", "bob");
553 env.trust(Account("alice")["USD"](800), "dan");
554 env.trust(Account("bob")["USD"](800), "dan");
555 env(pay(gw2, "alice", gw2_USD(100)));
556 env(pay("carol", "alice", Account("carol")["USD"](100)));
557 env(pay(gw, "alice", USD(100)));
558
559 STPathSet st;
560 STAmount sa;
561 std::tie(st, sa, std::ignore) =
562 find_paths(env, "alice", "bob", Account("bob")["USD"](5));
563 BEAST_EXPECT(same(
564 st,
565 stpath("gateway"),
566 stpath("gateway2"),
567 stpath("dan"),
568 stpath("carol")));
569 BEAST_EXPECT(equal(sa, Account("alice")["USD"](5)));
570 }
571
572 void
574 {
575 testcase("path negative: Issue #5");
576 using namespace jtx;
577 Env env = pathTestEnv();
578 env.fund(XRP(10000), "alice", "bob", "carol", "dan");
579 env.trust(Account("bob")["USD"](100), "alice", "carol", "dan");
580 env.trust(Account("alice")["USD"](100), "dan");
581 env.trust(Account("carol")["USD"](100), "dan");
582 env(pay("bob", "carol", Account("bob")["USD"](75)));
583 env.require(balance("bob", Account("carol")["USD"](-75)));
584 env.require(balance("carol", Account("bob")["USD"](75)));
585
586 auto result =
587 find_paths(env, "alice", "bob", Account("bob")["USD"](25));
588 BEAST_EXPECT(std::get<0>(result).empty());
589
590 env(pay("alice", "bob", Account("alice")["USD"](25)), ter(tecPATH_DRY));
591
592 result = find_paths(env, "alice", "bob", Account("alice")["USD"](25));
593 BEAST_EXPECT(std::get<0>(result).empty());
594
595 env.require(balance("alice", Account("bob")["USD"](0)));
596 env.require(balance("alice", Account("dan")["USD"](0)));
597 env.require(balance("bob", Account("alice")["USD"](0)));
598 env.require(balance("bob", Account("carol")["USD"](-75)));
599 env.require(balance("bob", Account("dan")["USD"](0)));
600 env.require(balance("carol", Account("bob")["USD"](75)));
601 env.require(balance("carol", Account("dan")["USD"](0)));
602 env.require(balance("dan", Account("alice")["USD"](0)));
603 env.require(balance("dan", Account("bob")["USD"](0)));
604 env.require(balance("dan", Account("carol")["USD"](0)));
605 }
606
607 // alice -- limit 40 --> bob
608 // alice --> carol --> dan --> bob
609 // Balance of 100 USD Bob - Balance of 37 USD -> Rod
610 void
612 {
613 testcase("path negative: ripple-client issue #23: smaller");
614 using namespace jtx;
615 Env env = pathTestEnv();
616 env.fund(XRP(10000), "alice", "bob", "carol", "dan");
617 env.trust(Account("alice")["USD"](40), "bob");
618 env.trust(Account("dan")["USD"](20), "bob");
619 env.trust(Account("alice")["USD"](20), "carol");
620 env.trust(Account("carol")["USD"](20), "dan");
621 env(pay("alice", "bob", Account("bob")["USD"](55)),
622 paths(Account("alice")["USD"]));
623 env.require(balance("bob", Account("alice")["USD"](40)));
624 env.require(balance("bob", Account("dan")["USD"](15)));
625 }
626
627 // alice -120 USD-> edward -25 USD-> bob
628 // alice -25 USD-> carol -75 USD -> dan -100 USD-> bob
629 void
631 {
632 testcase("path negative: ripple-client issue #23: larger");
633 using namespace jtx;
634 Env env = pathTestEnv();
635 env.fund(XRP(10000), "alice", "bob", "carol", "dan", "edward");
636 env.trust(Account("alice")["USD"](120), "edward");
637 env.trust(Account("edward")["USD"](25), "bob");
638 env.trust(Account("dan")["USD"](100), "bob");
639 env.trust(Account("alice")["USD"](25), "carol");
640 env.trust(Account("carol")["USD"](75), "dan");
641 env(pay("alice", "bob", Account("bob")["USD"](50)),
642 paths(Account("alice")["USD"]));
643 env.require(balance("alice", Account("edward")["USD"](-25)));
644 env.require(balance("alice", Account("carol")["USD"](-25)));
645 env.require(balance("bob", Account("edward")["USD"](25)));
646 env.require(balance("bob", Account("dan")["USD"](25)));
647 env.require(balance("carol", Account("alice")["USD"](25)));
648 env.require(balance("carol", Account("dan")["USD"](-25)));
649 env.require(balance("dan", Account("carol")["USD"](25)));
650 env.require(balance("dan", Account("bob")["USD"](-25)));
651 }
652
653 // carol holds gateway AUD, sells gateway AUD for XRP
654 // bob will hold gateway AUD
655 // alice pays bob gateway AUD using XRP
656 void
658 {
659 testcase("via gateway");
660 using namespace jtx;
661 Env env = pathTestEnv();
662 auto const gw = Account("gateway");
663 auto const AUD = gw["AUD"];
664 env.fund(XRP(10000), "alice", "bob", "carol", gw);
665 env(rate(gw, 1.1));
666 env.trust(AUD(100), "bob", "carol");
667 env(pay(gw, "carol", AUD(50)));
668 env(offer("carol", XRP(50), AUD(50)));
669 env(pay("alice", "bob", AUD(10)), sendmax(XRP(100)), paths(XRP));
670 env.require(balance("bob", AUD(10)));
671 env.require(balance("carol", AUD(39)));
672
673 auto const result =
674 find_paths(env, "alice", "bob", Account("bob")["USD"](25));
675 BEAST_EXPECT(std::get<0>(result).empty());
676 }
677
678 void
680 {
681 testcase("path find");
682 using namespace jtx;
683 Env env = pathTestEnv();
684 env.fund(XRP(10000), "alice", "bob", "carol");
685 env.trust(Account("alice")["USD"](1000), "bob");
686 env.trust(Account("bob")["USD"](1000), "carol");
687
688 STPathSet st;
689 STAmount sa;
690 std::tie(st, sa, std::ignore) =
691 find_paths(env, "alice", "carol", Account("carol")["USD"](5));
692 BEAST_EXPECT(same(st, stpath("bob")));
693 BEAST_EXPECT(equal(sa, Account("alice")["USD"](5)));
694 }
695
696 void
698 {
699 testcase("quality set and test");
700 using namespace jtx;
701 Env env = pathTestEnv();
702 env.fund(XRP(10000), "alice", "bob");
703 env(trust("bob", Account("alice")["USD"](1000)),
704 json("{\"" + sfQualityIn.fieldName + "\": 2000}"),
705 json("{\"" + sfQualityOut.fieldName + "\": 1400000000}"));
706
707 Json::Value jv;
709 R"({
710 "Balance" : {
711 "currency" : "USD",
712 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
713 "value" : "0"
714 },
715 "Flags" : 131072,
716 "HighLimit" : {
717 "currency" : "USD",
718 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
719 "value" : "1000"
720 },
721 "HighNode" : "0",
722 "HighQualityIn" : 2000,
723 "HighQualityOut" : 1400000000,
724 "LedgerEntryType" : "RippleState",
725 "LowLimit" : {
726 "currency" : "USD",
727 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
728 "value" : "0"
729 },
730 "LowNode" : "0"
731 })",
732 jv);
733
734 auto const jv_l =
735 env.le(keylet::line(
736 Account("bob").id(), Account("alice")["USD"].issue()))
737 ->getJson(JsonOptions::none);
738 for (auto it = jv.begin(); it != jv.end(); ++it)
739 BEAST_EXPECT(*it == jv_l[it.memberName()]);
740 }
741
742 void
744 {
745 testcase("trust normal clear");
746 using namespace jtx;
747 Env env = pathTestEnv();
748 env.fund(XRP(10000), "alice", "bob");
749 env.trust(Account("bob")["USD"](1000), "alice");
750 env.trust(Account("alice")["USD"](1000), "bob");
752 Json::Value jv;
754 R"({
755 "Balance" : {
756 "currency" : "USD",
757 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
758 "value" : "0"
759 },
760 "Flags" : 196608,
761 "HighLimit" : {
762 "currency" : "USD",
763 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
764 "value" : "1000"
765 },
766 "HighNode" : "0",
767 "LedgerEntryType" : "RippleState",
768 "LowLimit" : {
769 "currency" : "USD",
770 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
771 "value" : "1000"
772 },
773 "LowNode" : "0"
774 })",
775 jv);
776
777 auto const jv_l =
778 env.le(keylet::line(
779 Account("bob").id(), Account("alice")["USD"].issue()))
780 ->getJson(JsonOptions::none);
781 for (auto it = jv.begin(); it != jv.end(); ++it)
782 BEAST_EXPECT(*it == jv_l[it.memberName()]);
783
784 env.trust(Account("bob")["USD"](0), "alice");
785 env.trust(Account("alice")["USD"](0), "bob");
786 BEAST_EXPECT(
787 env.le(keylet::line(
788 Account("bob").id(), Account("alice")["USD"].issue())) ==
789 nullptr);
790 }
791
792 void
794 {
795 testcase("trust auto clear");
796 using namespace jtx;
797 Env env = pathTestEnv();
798 env.fund(XRP(10000), "alice", "bob");
799 env.trust(Account("bob")["USD"](1000), "alice");
800 env(pay("bob", "alice", Account("bob")["USD"](50)));
801 env.trust(Account("bob")["USD"](0), "alice");
802
803 Json::Value jv;
805 R"({
806 "Balance" :
807 {
808 "currency" : "USD",
809 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
810 "value" : "50"
811 },
812 "Flags" : 65536,
813 "HighLimit" :
814 {
815 "currency" : "USD",
816 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
817 "value" : "0"
818 },
819 "HighNode" : "0",
820 "LedgerEntryType" : "RippleState",
821 "LowLimit" :
822 {
823 "currency" : "USD",
824 "issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
825 "value" : "0"
826 },
827 "LowNode" : "0"
828 })",
829 jv);
830
831 auto const jv_l =
832 env.le(keylet::line(
833 Account("alice").id(), Account("bob")["USD"].issue()))
834 ->getJson(JsonOptions::none);
835 for (auto it = jv.begin(); it != jv.end(); ++it)
836 BEAST_EXPECT(*it == jv_l[it.memberName()]);
837
838 env(pay("alice", "bob", Account("alice")["USD"](50)));
839 BEAST_EXPECT(
840 env.le(keylet::line(
841 Account("alice").id(), Account("bob")["USD"].issue())) ==
842 nullptr);
843 }
844
845 void
847 {
848 testcase("Path Find: XRP -> XRP and XRP -> IOU");
849 using namespace jtx;
850 Env env = pathTestEnv();
851 Account A1{"A1"};
852 Account A2{"A2"};
853 Account A3{"A3"};
854 Account G1{"G1"};
855 Account G2{"G2"};
856 Account G3{"G3"};
857 Account M1{"M1"};
858
859 env.fund(XRP(100000), A1);
860 env.fund(XRP(10000), A2);
861 env.fund(XRP(1000), A3, G1, G2, G3, M1);
862 env.close();
863
864 env.trust(G1["XYZ"](5000), A1);
865 env.trust(G3["ABC"](5000), A1);
866 env.trust(G2["XYZ"](5000), A2);
867 env.trust(G3["ABC"](5000), A2);
868 env.trust(A2["ABC"](1000), A3);
869 env.trust(G1["XYZ"](100000), M1);
870 env.trust(G2["XYZ"](100000), M1);
871 env.trust(G3["ABC"](100000), M1);
872 env.close();
873
874 env(pay(G1, A1, G1["XYZ"](3500)));
875 env(pay(G3, A1, G3["ABC"](1200)));
876 env(pay(G2, M1, G2["XYZ"](25000)));
877 env(pay(G3, M1, G3["ABC"](25000)));
878 env.close();
879
880 env(offer(M1, G1["XYZ"](1000), G2["XYZ"](1000)));
881 env(offer(M1, XRP(10000), G3["ABC"](1000)));
882
883 STPathSet st;
884 STAmount sa, da;
885
886 {
887 auto const& send_amt = XRP(10);
888 std::tie(st, sa, da) =
889 find_paths(env, A1, A2, send_amt, std::nullopt, xrpCurrency());
890 BEAST_EXPECT(equal(da, send_amt));
891 BEAST_EXPECT(st.empty());
892 }
893
894 {
895 // no path should exist for this since dest account
896 // does not exist.
897 auto const& send_amt = XRP(200);
898 std::tie(st, sa, da) = find_paths(
899 env, A1, Account{"A0"}, send_amt, std::nullopt, xrpCurrency());
900 BEAST_EXPECT(equal(da, send_amt));
901 BEAST_EXPECT(st.empty());
902 }
903
904 {
905 auto const& send_amt = G3["ABC"](10);
906 std::tie(st, sa, da) =
907 find_paths(env, A2, G3, send_amt, std::nullopt, xrpCurrency());
908 BEAST_EXPECT(equal(da, send_amt));
909 BEAST_EXPECT(equal(sa, XRP(100)));
910 BEAST_EXPECT(same(st, stpath(IPE(G3["ABC"]))));
911 }
912
913 {
914 auto const& send_amt = A2["ABC"](1);
915 std::tie(st, sa, da) =
916 find_paths(env, A1, A2, send_amt, std::nullopt, xrpCurrency());
917 BEAST_EXPECT(equal(da, send_amt));
918 BEAST_EXPECT(equal(sa, XRP(10)));
919 BEAST_EXPECT(same(st, stpath(IPE(G3["ABC"]), G3)));
920 }
921
922 {
923 auto const& send_amt = A3["ABC"](1);
924 std::tie(st, sa, da) =
925 find_paths(env, A1, A3, send_amt, std::nullopt, xrpCurrency());
926 BEAST_EXPECT(equal(da, send_amt));
927 BEAST_EXPECT(equal(sa, XRP(10)));
928 BEAST_EXPECT(same(st, stpath(IPE(G3["ABC"]), G3, A2)));
929 }
930 }
931
932 void
934 {
935 testcase("Path Find: non-XRP -> XRP");
936 using namespace jtx;
937 Env env = pathTestEnv();
938 Account A1{"A1"};
939 Account A2{"A2"};
940 Account G3{"G3"};
941 Account M1{"M1"};
942
943 env.fund(XRP(1000), A1, A2, G3);
944 env.fund(XRP(11000), M1);
945 env.close();
946
947 env.trust(G3["ABC"](1000), A1, A2);
948 env.trust(G3["ABC"](100000), M1);
949 env.close();
950
951 env(pay(G3, A1, G3["ABC"](1000)));
952 env(pay(G3, A2, G3["ABC"](1000)));
953 env(pay(G3, M1, G3["ABC"](1200)));
954 env.close();
955
956 env(offer(M1, G3["ABC"](1000), XRP(10000)));
957
958 STPathSet st;
959 STAmount sa, da;
960
961 auto const& send_amt = XRP(10);
962 std::tie(st, sa, da) =
963 find_paths(env, A1, A2, send_amt, std::nullopt, A2["ABC"].currency);
964 BEAST_EXPECT(equal(da, send_amt));
965 BEAST_EXPECT(equal(sa, A1["ABC"](1)));
966 BEAST_EXPECT(same(st, stpath(G3, IPE(xrpIssue()))));
967 }
968
969 void
971 {
972 testcase("Path Find: Bitstamp and SnapSwap, liquidity with no offers");
973 using namespace jtx;
974 Env env = pathTestEnv();
975 Account A1{"A1"};
976 Account A2{"A2"};
977 Account G1BS{"G1BS"};
978 Account G2SW{"G2SW"};
979 Account M1{"M1"};
980
981 env.fund(XRP(1000), G1BS, G2SW, A1, A2);
982 env.fund(XRP(11000), M1);
983 env.close();
984
985 env.trust(G1BS["HKD"](2000), A1);
986 env.trust(G2SW["HKD"](2000), A2);
987 env.trust(G1BS["HKD"](100000), M1);
988 env.trust(G2SW["HKD"](100000), M1);
989 env.close();
990
991 env(pay(G1BS, A1, G1BS["HKD"](1000)));
992 env(pay(G2SW, A2, G2SW["HKD"](1000)));
993 // SnapSwap wants to be able to set trust line quality settings so they
994 // can charge a fee when transactions ripple across. Liquidity
995 // provider, via trusting/holding both accounts
996 env(pay(G1BS, M1, G1BS["HKD"](1200)));
997 env(pay(G2SW, M1, G2SW["HKD"](5000)));
998 env.close();
999
1000 STPathSet st;
1001 STAmount sa, da;
1002
1003 {
1004 auto const& send_amt = A2["HKD"](10);
1005 std::tie(st, sa, da) = find_paths(
1006 env, A1, A2, send_amt, std::nullopt, A2["HKD"].currency);
1007 BEAST_EXPECT(equal(da, send_amt));
1008 BEAST_EXPECT(equal(sa, A1["HKD"](10)));
1009 BEAST_EXPECT(same(st, stpath(G1BS, M1, G2SW)));
1010 }
1011
1012 {
1013 auto const& send_amt = A1["HKD"](10);
1014 std::tie(st, sa, da) = find_paths(
1015 env, A2, A1, send_amt, std::nullopt, A1["HKD"].currency);
1016 BEAST_EXPECT(equal(da, send_amt));
1017 BEAST_EXPECT(equal(sa, A2["HKD"](10)));
1018 BEAST_EXPECT(same(st, stpath(G2SW, M1, G1BS)));
1019 }
1020
1021 {
1022 auto const& send_amt = A2["HKD"](10);
1023 std::tie(st, sa, da) = find_paths(
1024 env, G1BS, A2, send_amt, std::nullopt, A1["HKD"].currency);
1025 BEAST_EXPECT(equal(da, send_amt));
1026 BEAST_EXPECT(equal(sa, G1BS["HKD"](10)));
1027 BEAST_EXPECT(same(st, stpath(M1, G2SW)));
1028 }
1029
1030 {
1031 auto const& send_amt = M1["HKD"](10);
1032 std::tie(st, sa, da) = find_paths(
1033 env, M1, G1BS, send_amt, std::nullopt, A1["HKD"].currency);
1034 BEAST_EXPECT(equal(da, send_amt));
1035 BEAST_EXPECT(equal(sa, M1["HKD"](10)));
1036 BEAST_EXPECT(st.empty());
1037 }
1038
1039 {
1040 auto const& send_amt = A1["HKD"](10);
1041 std::tie(st, sa, da) = find_paths(
1042 env, G2SW, A1, send_amt, std::nullopt, A1["HKD"].currency);
1043 BEAST_EXPECT(equal(da, send_amt));
1044 BEAST_EXPECT(equal(sa, G2SW["HKD"](10)));
1045 BEAST_EXPECT(same(st, stpath(M1, G1BS)));
1046 }
1047 }
1048
1049 void
1050 path_find_05()
1051 {
1052 testcase("Path Find: non-XRP -> non-XRP, same currency");
1053 using namespace jtx;
1054 Env env = pathTestEnv();
1055 Account A1{"A1"};
1056 Account A2{"A2"};
1057 Account A3{"A3"};
1058 Account A4{"A4"};
1059 Account G1{"G1"};
1060 Account G2{"G2"};
1061 Account G3{"G3"};
1062 Account G4{"G4"};
1063 Account M1{"M1"};
1064 Account M2{"M2"};
1065
1066 env.fund(XRP(1000), A1, A2, A3, G1, G2, G3, G4);
1067 env.fund(XRP(10000), A4);
1068 env.fund(XRP(11000), M1, M2);
1069 env.close();
1070
1071 env.trust(G1["HKD"](2000), A1);
1072 env.trust(G2["HKD"](2000), A2);
1073 env.trust(G1["HKD"](2000), A3);
1074 env.trust(G1["HKD"](100000), M1);
1075 env.trust(G2["HKD"](100000), M1);
1076 env.trust(G1["HKD"](100000), M2);
1077 env.trust(G2["HKD"](100000), M2);
1078 env.close();
1079
1080 env(pay(G1, A1, G1["HKD"](1000)));
1081 env(pay(G2, A2, G2["HKD"](1000)));
1082 env(pay(G1, A3, G1["HKD"](1000)));
1083 env(pay(G1, M1, G1["HKD"](1200)));
1084 env(pay(G2, M1, G2["HKD"](5000)));
1085 env(pay(G1, M2, G1["HKD"](1200)));
1086 env(pay(G2, M2, G2["HKD"](5000)));
1087 env.close();
1088
1089 env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)));
1090 env(offer(M2, XRP(10000), G2["HKD"](1000)));
1091 env(offer(M2, G1["HKD"](1000), XRP(10000)));
1092
1093 STPathSet st;
1094 STAmount sa, da;
1095
1096 {
1097 // A) Borrow or repay --
1098 // Source -> Destination (repay source issuer)
1099 auto const& send_amt = G1["HKD"](10);
1100 std::tie(st, sa, da) = find_paths(
1101 env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency);
1102 BEAST_EXPECT(st.empty());
1103 BEAST_EXPECT(equal(da, send_amt));
1104 BEAST_EXPECT(equal(sa, A1["HKD"](10)));
1105 }
1106
1107 {
1108 // A2) Borrow or repay --
1109 // Source -> Destination (repay destination issuer)
1110 auto const& send_amt = A1["HKD"](10);
1111 std::tie(st, sa, da) = find_paths(
1112 env, A1, G1, send_amt, std::nullopt, G1["HKD"].currency);
1113 BEAST_EXPECT(st.empty());
1114 BEAST_EXPECT(equal(da, send_amt));
1115 BEAST_EXPECT(equal(sa, A1["HKD"](10)));
1116 }
1117
1118 {
1119 // B) Common gateway --
1120 // Source -> AC -> Destination
1121 auto const& send_amt = A3["HKD"](10);
1122 std::tie(st, sa, da) = find_paths(
1123 env, A1, A3, send_amt, std::nullopt, G1["HKD"].currency);
1124 BEAST_EXPECT(equal(da, send_amt));
1125 BEAST_EXPECT(equal(sa, A1["HKD"](10)));
1126 BEAST_EXPECT(same(st, stpath(G1)));
1127 }
1128
1129 {
1130 // C) Gateway to gateway --
1131 // Source -> OB -> Destination
1132 auto const& send_amt = G2["HKD"](10);
1133 std::tie(st, sa, da) = find_paths(
1134 env, G1, G2, send_amt, std::nullopt, G1["HKD"].currency);
1135 BEAST_EXPECT(equal(da, send_amt));
1136 BEAST_EXPECT(equal(sa, G1["HKD"](10)));
1137 BEAST_EXPECT(same(
1138 st,
1139 stpath(IPE(G2["HKD"])),
1140 stpath(M1),
1141 stpath(M2),
1142 stpath(IPE(xrpIssue()), IPE(G2["HKD"]))));
1143 }
1144
1145 {
1146 // D) User to unlinked gateway via order book --
1147 // Source -> AC -> OB -> Destination
1148 auto const& send_amt = G2["HKD"](10);
1149 std::tie(st, sa, da) = find_paths(
1150 env, A1, G2, send_amt, std::nullopt, G1["HKD"].currency);
1151 BEAST_EXPECT(equal(da, send_amt));
1152 BEAST_EXPECT(equal(sa, A1["HKD"](10)));
1153 BEAST_EXPECT(same(
1154 st,
1155 stpath(G1, M1),
1156 stpath(G1, M2),
1157 stpath(G1, IPE(G2["HKD"])),
1158 stpath(G1, IPE(xrpIssue()), IPE(G2["HKD"]))));
1159 }
1160
1161 {
1162 // I4) XRP bridge" --
1163 // Source -> AC -> OB to XRP -> OB from XRP -> AC -> Destination
1164 auto const& send_amt = A2["HKD"](10);
1165 std::tie(st, sa, da) = find_paths(
1166 env, A1, A2, send_amt, std::nullopt, G1["HKD"].currency);
1167 BEAST_EXPECT(equal(da, send_amt));
1168 BEAST_EXPECT(equal(sa, A1["HKD"](10)));
1169 BEAST_EXPECT(same(
1170 st,
1171 stpath(G1, M1, G2),
1172 stpath(G1, M2, G2),
1173 stpath(G1, IPE(G2["HKD"]), G2),
1174 stpath(G1, IPE(xrpIssue()), IPE(G2["HKD"]), G2)));
1175 }
1176 }
1177
1178 void
1179 path_find_06()
1180 {
1181 testcase("Path Find: non-XRP -> non-XRP, same currency)");
1182 using namespace jtx;
1183 Env env = pathTestEnv();
1184 Account A1{"A1"};
1185 Account A2{"A2"};
1186 Account A3{"A3"};
1187 Account G1{"G1"};
1188 Account G2{"G2"};
1189 Account M1{"M1"};
1190
1191 env.fund(XRP(11000), M1);
1192 env.fund(XRP(1000), A1, A2, A3, G1, G2);
1193 env.close();
1194
1195 env.trust(G1["HKD"](2000), A1);
1196 env.trust(G2["HKD"](2000), A2);
1197 env.trust(A2["HKD"](2000), A3);
1198 env.trust(G1["HKD"](100000), M1);
1199 env.trust(G2["HKD"](100000), M1);
1200 env.close();
1201
1202 env(pay(G1, A1, G1["HKD"](1000)));
1203 env(pay(G2, A2, G2["HKD"](1000)));
1204 env(pay(G1, M1, G1["HKD"](5000)));
1205 env(pay(G2, M1, G2["HKD"](5000)));
1206 env.close();
1207
1208 env(offer(M1, G1["HKD"](1000), G2["HKD"](1000)));
1209
1210 // E) Gateway to user
1211 // Source -> OB -> AC -> Destination
1212 auto const& send_amt = A2["HKD"](10);
1213 STPathSet st;
1214 STAmount sa, da;
1215 std::tie(st, sa, da) =
1216 find_paths(env, G1, A2, send_amt, std::nullopt, G1["HKD"].currency);
1217 BEAST_EXPECT(equal(da, send_amt));
1218 BEAST_EXPECT(equal(sa, G1["HKD"](10)));
1219 BEAST_EXPECT(same(st, stpath(M1, G2), stpath(IPE(G2["HKD"]), G2)));
1220 }
1221
1222 void
1223 receive_max()
1224 {
1225 testcase("Receive max");
1226 using namespace jtx;
1227 auto const alice = Account("alice");
1228 auto const bob = Account("bob");
1229 auto const charlie = Account("charlie");
1230 auto const gw = Account("gw");
1231 auto const USD = gw["USD"];
1232 {
1233 // XRP -> IOU receive max
1234 Env env = pathTestEnv();
1235 env.fund(XRP(10000), alice, bob, charlie, gw);
1236 env.close();
1237 env.trust(USD(100), alice, bob, charlie);
1238 env.close();
1239 env(pay(gw, charlie, USD(10)));
1240 env.close();
1241 env(offer(charlie, XRP(10), USD(10)));
1242 env.close();
1243 auto [st, sa, da] =
1244 find_paths(env, alice, bob, USD(-1), XRP(100).value());
1245 BEAST_EXPECT(sa == XRP(10));
1246 BEAST_EXPECT(equal(da, USD(10)));
1247 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1248 {
1249 auto const& pathElem = st[0][0];
1250 BEAST_EXPECT(
1251 pathElem.isOffer() && pathElem.getIssuerID() == gw.id() &&
1252 pathElem.getCurrency() == USD.currency);
1253 }
1254 }
1255 {
1256 // IOU -> XRP receive max
1257 Env env = pathTestEnv();
1258 env.fund(XRP(10000), alice, bob, charlie, gw);
1259 env.close();
1260 env.trust(USD(100), alice, bob, charlie);
1261 env.close();
1262 env(pay(gw, alice, USD(10)));
1263 env.close();
1264 env(offer(charlie, USD(10), XRP(10)));
1265 env.close();
1266 auto [st, sa, da] =
1267 find_paths(env, alice, bob, drops(-1), USD(100).value());
1268 BEAST_EXPECT(sa == USD(10));
1269 BEAST_EXPECT(equal(da, XRP(10)));
1270 if (BEAST_EXPECT(st.size() == 1 && st[0].size() == 1))
1271 {
1272 auto const& pathElem = st[0][0];
1273 BEAST_EXPECT(
1274 pathElem.isOffer() &&
1275 pathElem.getIssuerID() == xrpAccount() &&
1276 pathElem.getCurrency() == xrpCurrency());
1277 }
1278 }
1279 }
1281 void
1283 {
1284 using namespace jtx;
1285 // This test will create trust lines with various values of the noRipple
1286 // flag. alice <-> george <-> bob george will sort of act like a
1287 // gateway, but use a different name to avoid the usual assumptions
1288 // about gateways.
1289 auto const alice = Account("alice");
1290 auto const bob = Account("bob");
1291 auto const george = Account("george");
1292 auto const USD = george["USD"];
1293 auto test = [&](std::string casename,
1294 bool aliceRipple,
1295 bool bobRipple,
1296 bool expectPath) {
1297 testcase(casename);
1298
1299 Env env = pathTestEnv();
1300 env.fund(XRP(10000), noripple(alice, bob, george));
1301 env.close();
1302 // Set the same flags at both ends of the trustline, even though
1303 // only george's matter.
1304 env(trust(
1305 alice,
1306 USD(100),
1307 aliceRipple ? tfClearNoRipple : tfSetNoRipple));
1308 env(trust(
1309 george,
1310 alice["USD"](100),
1311 aliceRipple ? tfClearNoRipple : tfSetNoRipple));
1312 env(trust(
1313 bob, USD(100), bobRipple ? tfClearNoRipple : tfSetNoRipple));
1314 env(trust(
1315 george,
1316 bob["USD"](100),
1317 bobRipple ? tfClearNoRipple : tfSetNoRipple));
1318 env.close();
1319 env(pay(george, alice, USD(70)));
1320 env.close();
1321
1322 auto [st, sa, da] =
1323 find_paths(env, "alice", "bob", Account("bob")["USD"](5));
1324 BEAST_EXPECT(equal(da, bob["USD"](5)));
1325
1326 if (expectPath)
1327 {
1328 BEAST_EXPECT(st.size() == 1);
1329 BEAST_EXPECT(same(st, stpath("george")));
1330 BEAST_EXPECT(equal(sa, alice["USD"](5)));
1331 }
1332 else
1333 {
1334 BEAST_EXPECT(st.size() == 0);
1335 BEAST_EXPECT(equal(sa, XRP(0)));
1336 }
1337 };
1338 test("ripple -> ripple", true, true, true);
1339 test("ripple -> no ripple", true, false, true);
1340 test("no ripple -> ripple", false, true, true);
1341 test("no ripple -> no ripple", false, false, false);
1342 }
1343
1344 void
1345 run() override
1346 {
1351 path_find();
1365 xrp_to_xrp();
1366 receive_max();
1368
1369 // The following path_find_NN tests are data driven tests
1370 // that were originally implemented in js/coffee and migrated
1371 // here. The quantities and currencies used are taken directly from
1372 // those legacy tests, which in some cases probably represented
1373 // customer use cases.
1374
1375 path_find_01();
1376 path_find_02();
1377 path_find_04();
1378 path_find_05();
1379 path_find_06();
1380 }
1381};
1382
1383BEAST_DEFINE_TESTSUITE(Path, app, ripple);
1384
1385} // namespace test
1386} // namespace ripple
Unserialize a JSON document into a Value.
Definition: json_reader.h:39
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:78
Represents a JSON value.
Definition: json_value.h:148
const_iterator begin() const
const_iterator end() const
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:949
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
A consumption charge.
Definition: Charge.h:31
An endpoint that consumes resources.
Definition: Consumer.h:35
Json::Value getJson(JsonOptions) const override
Definition: STAmount.cpp:636
Holds the serialized result of parsing an input JSON object.
Definition: STParsedJSON.h:33
std::optional< STObject > object
The STObject if the parse was successful.
Definition: STParsedJSON.h:51
bool empty() const
Definition: STPathSet.h:508
std::condition_variable cv_
Definition: Path_test.cpp:94
bool wait_for(std::chrono::duration< Rep, Period > const &rel_time)
Definition: Path_test.cpp:103
void alternative_path_consume_both()
Definition: Path_test.cpp:449
void trust_auto_clear_trust_normal_clear()
Definition: Path_test.cpp:721
void alternative_paths_consume_best_transfer()
Definition: Path_test.cpp:478
void alternative_paths_limit_returned_paths_to_best_quality()
Definition: Path_test.cpp:538
void trust_auto_clear_trust_auto_clear()
Definition: Path_test.cpp:751
void direct_path_no_intermediary()
Definition: Path_test.cpp:324
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)
Definition: Path_test.cpp:182
void issues_path_negative_ripple_client_issue_23_larger()
Definition: Path_test.cpp:630
void issues_path_negative_ripple_client_issue_23_smaller()
Definition: Path_test.cpp:611
void alternative_paths_consume_best_transfer_first()
Definition: Path_test.cpp:507
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)
Definition: Path_test.cpp:121
void quality_paths_quality_set_and_test()
Definition: Path_test.cpp:697
void run() override
Runs the suite.
Definition: Path_test.cpp:1280
void no_direct_path_no_intermediary_no_alternatives()
Definition: Path_test.cpp:311
Immutable cryptographic account descriptor.
Definition: Account.h:39
A transaction testing environment.
Definition: Env.h:118
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:530
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:115
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition: Env.cpp:262
Application & app()
Definition: Env.h:256
beast::Journal const journal
Definition: Env.h:159
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:231
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:219
A balance matches.
Definition: balance.h:39
Inject raw JSON.
Definition: jtx_json.h:32
Add a path.
Definition: paths.h:57
Set Paths, SendMax on a JTx.
Definition: paths.h:34
Sets the SendMax on a JTx.
Definition: sendmax.h:32
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
T make_tuple(T... args)
@ arrayValue
array value (ordered list)
Definition: json_value.h:43
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
static int constexpr max_src_cur
Maximum number of source currencies allowed in a path find request.
static int constexpr max_auto_src_cur
Maximum number of auto source currencies in a path find request.
Status doCommand(RPC::JsonContext &context, Json::Value &result)
Execute an RPC command and store the results in a Json::Value.
Definition: RPCHandler.cpp:221
static constexpr auto apiVersionIfUnspecified
Definition: ApiVersion.h:59
Charge const feeReferenceRPC
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:235
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition: trust.cpp:31
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
bool same(STPathSet const &st1, Args const &... args)
Definition: TestHelpers.h:155
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition: rate.cpp:31
STPathElement IPE(Issue const &iss)
Definition: TestHelpers.cpp:80
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition: offer.cpp:28
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:104
STPath stpath(Args const &... args)
Definition: TestHelpers.h:146
Json::Value rpf(jtx::Account const &src, jtx::Account const &dst, std::uint32_t num_src)
Definition: Path_test.cpp:43
bool equal(std::unique_ptr< Step > const &s1, DirectStepInfo const &dsi)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:118
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:114
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:178
STAmount amountFromJson(SField const &name, Json::Value const &v)
Definition: STAmount.cpp:932
SField const sfGeneric
constexpr std::uint32_t tfClearNoRipple
Definition: TxFlags.h:114
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:119
@ tecPATH_DRY
Definition: TER.h:281
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
@ jtCLIENT
Definition: Job.h:44
constexpr std::uint32_t tfSetNoRipple
Definition: TxFlags.h:113
T tie(T... args)
T to_string(T... args)