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