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