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