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/envconfig.h>
39 #include <thread>
40 
41 namespace ripple {
42 namespace test {
43 
44 //------------------------------------------------------------------------------
45 
47 rpf(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 
77 class Path_test : public beast::unit_test::suite
78 {
79  jtx::Env
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 
94 public:
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,
146  Role::USER,
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,
254  Role::USER,
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");
755 
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
850  path_find_01()
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
937  path_find_02()
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
974  path_find_04()
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 
1387 BEAST_DEFINE_TESTSUITE(Path, app, ripple);
1388 
1389 } // namespace test
1390 } // namespace ripple
ripple::test::Path_test::path_find_01
void path_find_01()
Definition: Path_test.cpp:785
ripple::test::Path_test::quality_paths_quality_set_and_test
void quality_paths_quality_set_and_test()
Definition: Path_test.cpp:701
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:328
ripple::RPC::apiVersionIfUnspecified
constexpr unsigned int apiVersionIfUnspecified
Definition: RPCHelpers.h:241
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:95
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:133
ripple::sfGeneric
const SField sfGeneric(access, 0)
Definition: SField.h:356
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:100
ripple::test::jtx::Env::require
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:479
ripple::test::Path_test::trust_auto_clear_trust_auto_clear
void trust_auto_clear_trust_auto_clear()
Definition: Path_test.cpp:755
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:232
ripple::test::Path_test::run
void run() override
Definition: Path_test.cpp:1284
ripple::test::Path_test::gate::wait_for
bool wait_for(std::chrono::duration< Rep, Period > const &rel_time)
Definition: Path_test.cpp:107
ripple::test::Path_test::path_find
void path_find()
Definition: Path_test.cpp:364
ripple::SField::fieldName
const std::string fieldName
Definition: SField.h:159
ripple::tfSetNoRipple
constexpr std::uint32_t tfSetNoRipple
Definition: TxFlags.h:110
ripple::STAmount::getJson
Json::Value getJson(JsonOptions) const override
Definition: STAmount.cpp:653
ripple::test::Path_test::path_find_04
void path_find_04()
Definition: Path_test.cpp:909
std::chrono::duration
ripple::keylet::offer
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:231
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:77
ripple::test::jtx::amm::pay
Json::Value pay(Account const &account, AccountID const &to, STAmount const &amount)
Definition: AMM.cpp:746
ripple::sfQualityIn
const SF_UINT32 sfQualityIn
ripple::test::jtx::Env::journal
const beast::Journal journal
Definition: Env.h:145
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:615
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:989
ripple::test::Path_test::issues_path_negative_issue
void issues_path_negative_issue()
Definition: Path_test.cpp:577
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:242
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:386
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:542
ripple::test::Path_test::gate::mutex_
std::mutex mutex_
Definition: Path_test.cpp:99
ripple::test::Path_test::alternative_paths_consume_best_transfer
void alternative_paths_consume_best_transfer()
Definition: Path_test.cpp:482
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:398
std::tie
T tie(T... args)
ripple::test::Path_test::payment_auto_path_find
void payment_auto_path_find()
Definition: Path_test.cpp:345
ripple::test::rpf
Json::Value rpf(jtx::Account const &src, jtx::Account const &dst, std::uint32_t num_src)
Definition: Path_test.cpp:47
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:453
ripple::test::Path_test::no_direct_path_no_intermediary_no_alternatives
void no_direct_path_no_intermediary_no_alternatives()
Definition: Path_test.cpp:315
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:511
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:1118
std::unique_lock
STL class.
ripple::test::jtx::stpath
STPath stpath(Args const &... args)
Definition: TestHelpers.h:124
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:46
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:927
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:202
ripple::test::Path_test::gate::cv_
std::condition_variable cv_
Definition: Path_test.cpp:98
ripple::test::Path_test::gate::signal
void signal()
Definition: Path_test.cpp:116
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:89
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:65
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:186
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:1162
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:1221
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:125
ripple::test::Path_test::via_offers_via_gateway
void via_offers_via_gateway()
Definition: Path_test.cpp:661
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:275
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:634
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:725
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:872
ripple::test::Path_test::indirect_paths_path_find
void indirect_paths_path_find()
Definition: Path_test.cpp:683
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:80
ripple::xrpCurrency
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:115
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:117
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)