rippled
PaySteps.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 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/impl/Steps.h>
21 #include <ripple/basics/IOUAmount.h>
22 #include <ripple/basics/XRPAmount.h>
23 #include <ripple/basics/contract.h>
24 #include <ripple/json/json_writer.h>
25 #include <ripple/ledger/ReadView.h>
26 #include <ripple/protocol/Feature.h>
27 
28 #include <algorithm>
29 #include <numeric>
30 #include <sstream>
31 
32 namespace ripple {
33 
34 // Check equal with tolerance
35 bool
36 checkNear(IOUAmount const& expected, IOUAmount const& actual)
37 {
38  double const ratTol = 0.001;
39  if (abs(expected.exponent() - actual.exponent()) > 1)
40  return false;
41 
42  if (actual.exponent() < -20)
43  return true;
44 
45  auto const a = (expected.exponent() < actual.exponent())
46  ? expected.mantissa() / 10
47  : expected.mantissa();
48  auto const b = (actual.exponent() < expected.exponent())
49  ? actual.mantissa() / 10
50  : actual.mantissa();
51  if (a == b)
52  return true;
53 
54  double const diff = std::abs(a - b);
55  auto const r = diff / std::max(std::abs(a), std::abs(b));
56  return r <= ratTol;
57 };
58 
59 bool
60 checkNear(XRPAmount const& expected, XRPAmount const& actual)
61 {
62  return expected == actual;
63 };
64 
65 static bool
67 {
69  return false;
70  return isXRP(pe.getAccountID());
71 };
72 
75  StrandContext const& ctx,
76  STPathElement const* e1,
77  STPathElement const* e2,
78  Issue const& curIssue)
79 {
80  auto& j = ctx.j;
81 
82  if (ctx.isFirst && e1->isAccount() &&
84  isXRP(e1->getCurrency()))
85  {
86  return make_XRPEndpointStep(ctx, e1->getAccountID());
87  }
88 
89  if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
90  return make_XRPEndpointStep(ctx, e2->getAccountID());
91 
92  if (e1->isAccount() && e2->isAccount())
93  {
94  return make_DirectStepI(
95  ctx, e1->getAccountID(), e2->getAccountID(), curIssue.currency);
96  }
97 
98  if (e1->isOffer() && e2->isAccount())
99  {
100  // should already be taken care of
101  JLOG(j.error())
102  << "Found offer/account payment step. Aborting payment strand.";
103  assert(0);
105  }
106 
107  assert(
110  auto const outCurrency = e2->getNodeType() & STPathElement::typeCurrency
111  ? e2->getCurrency()
112  : curIssue.currency;
113  auto const outIssuer = e2->getNodeType() & STPathElement::typeIssuer
114  ? e2->getIssuerID()
115  : curIssue.account;
116 
117  if (isXRP(curIssue.currency) && isXRP(outCurrency))
118  {
119  JLOG(j.info()) << "Found xrp/xrp offer payment step";
121  }
122 
123  assert(e2->isOffer());
124 
125  if (isXRP(outCurrency))
126  return make_BookStepIX(ctx, curIssue);
127 
128  if (isXRP(curIssue.currency))
129  return make_BookStepXI(ctx, {outCurrency, outIssuer});
130 
131  return make_BookStepII(ctx, curIssue, {outCurrency, outIssuer});
132 }
133 
136  ReadView const& view,
137  AccountID const& src,
138  AccountID const& dst,
139  Issue const& deliver,
140  std::optional<Quality> const& limitQuality,
141  std::optional<Issue> const& sendMaxIssue,
142  STPath const& path,
143  bool ownerPaysTransferFee,
144  OfferCrossing offerCrossing,
145  AMMContext& ammContext,
146  beast::Journal j)
147 {
148  if (isXRP(src) || isXRP(dst) || !isConsistent(deliver) ||
149  (sendMaxIssue && !isConsistent(*sendMaxIssue)))
150  return {temBAD_PATH, Strand{}};
151 
152  if ((sendMaxIssue && sendMaxIssue->account == noAccount()) ||
153  (src == noAccount()) || (dst == noAccount()) ||
154  (deliver.account == noAccount()))
155  return {temBAD_PATH, Strand{}};
156 
157  for (auto const& pe : path)
158  {
159  auto const t = pe.getNodeType();
160 
161  if ((t & ~STPathElement::typeAll) || !t)
162  return {temBAD_PATH, Strand{}};
163 
164  bool const hasAccount = t & STPathElement::typeAccount;
165  bool const hasIssuer = t & STPathElement::typeIssuer;
166  bool const hasCurrency = t & STPathElement::typeCurrency;
167 
168  if (hasAccount && (hasIssuer || hasCurrency))
169  return {temBAD_PATH, Strand{}};
170 
171  if (hasIssuer && isXRP(pe.getIssuerID()))
172  return {temBAD_PATH, Strand{}};
173 
174  if (hasAccount && isXRP(pe.getAccountID()))
175  return {temBAD_PATH, Strand{}};
176 
177  if (hasCurrency && hasIssuer &&
178  isXRP(pe.getCurrency()) != isXRP(pe.getIssuerID()))
179  return {temBAD_PATH, Strand{}};
180 
181  if (hasIssuer && (pe.getIssuerID() == noAccount()))
182  return {temBAD_PATH, Strand{}};
183 
184  if (hasAccount && (pe.getAccountID() == noAccount()))
185  return {temBAD_PATH, Strand{}};
186  }
187 
188  Issue curIssue = [&] {
189  auto const& currency =
190  sendMaxIssue ? sendMaxIssue->currency : deliver.currency;
191  if (isXRP(currency))
192  return xrpIssue();
193  return Issue{currency, src};
194  }();
195 
196  auto hasCurrency = [](STPathElement const pe) {
197  return pe.getNodeType() & STPathElement::typeCurrency;
198  };
199 
201  // reserve enough for the path, the implied source, destination,
202  // sendmax and deliver.
203  normPath.reserve(4 + path.size());
204  {
205  normPath.emplace_back(
206  STPathElement::typeAll, src, curIssue.currency, curIssue.account);
207 
208  if (sendMaxIssue && sendMaxIssue->account != src &&
209  (path.empty() || !path[0].isAccount() ||
210  path[0].getAccountID() != sendMaxIssue->account))
211  {
212  normPath.emplace_back(
213  sendMaxIssue->account, std::nullopt, std::nullopt);
214  }
215 
216  for (auto const& i : path)
217  normPath.push_back(i);
218 
219  {
220  // Note that for offer crossing (only) we do use an offer book
221  // even if all that is changing is the Issue.account.
222  STPathElement const& lastCurrency =
223  *std::find_if(normPath.rbegin(), normPath.rend(), hasCurrency);
224  if ((lastCurrency.getCurrency() != deliver.currency) ||
225  (offerCrossing &&
226  lastCurrency.getIssuerID() != deliver.account))
227  {
228  normPath.emplace_back(
229  std::nullopt, deliver.currency, deliver.account);
230  }
231  }
232 
233  if (!((normPath.back().isAccount() &&
234  normPath.back().getAccountID() == deliver.account) ||
235  (dst == deliver.account)))
236  {
237  normPath.emplace_back(deliver.account, std::nullopt, std::nullopt);
238  }
239 
240  if (!normPath.back().isAccount() ||
241  normPath.back().getAccountID() != dst)
242  {
243  normPath.emplace_back(dst, std::nullopt, std::nullopt);
244  }
245  }
246 
247  if (normPath.size() < 2)
248  return {temBAD_PATH, Strand{}};
249 
250  auto const strandSrc = normPath.front().getAccountID();
251  auto const strandDst = normPath.back().getAccountID();
252  bool const isDefaultPath = path.empty();
253 
254  Strand result;
255  result.reserve(2 * normPath.size());
256 
257  /* A strand may not include the same account node more than once
258  in the same currency. In a direct step, an account will show up
259  at most twice: once as a src and once as a dst (hence the two element
260  array). The strandSrc and strandDst will only show up once each.
261  */
263  // A strand may not include the same offer book more than once
264  boost::container::flat_set<Issue> seenBookOuts;
265  seenDirectIssues[0].reserve(normPath.size());
266  seenDirectIssues[1].reserve(normPath.size());
267  seenBookOuts.reserve(normPath.size());
268  auto ctx = [&](bool isLast = false) {
269  return StrandContext{
270  view,
271  result,
272  strandSrc,
273  strandDst,
274  deliver,
275  limitQuality,
276  isLast,
277  ownerPaysTransferFee,
278  offerCrossing,
280  seenDirectIssues,
281  seenBookOuts,
282  ammContext,
283  j};
284  };
285 
286  for (std::size_t i = 0; i < normPath.size() - 1; ++i)
287  {
288  /* Iterate through the path elements considering them in pairs.
289  The first element of the pair is `cur` and the second element is
290  `next`. When an offer is one of the pairs, the step created will be
291  for `next`. This means when `cur` is an offer and `next` is an
292  account then no step is created, as a step has already been created
293  for that offer.
294  */
296  auto cur = &normPath[i];
297  auto const next = &normPath[i + 1];
298 
299  if (cur->isAccount())
300  curIssue.account = cur->getAccountID();
301  else if (cur->hasIssuer())
302  curIssue.account = cur->getIssuerID();
303 
304  if (cur->hasCurrency())
305  {
306  curIssue.currency = cur->getCurrency();
307  if (isXRP(curIssue.currency))
308  curIssue.account = xrpAccount();
309  }
310 
311  if (cur->isAccount() && next->isAccount())
312  {
313  if (!isXRP(curIssue.currency) &&
314  curIssue.account != cur->getAccountID() &&
315  curIssue.account != next->getAccountID())
316  {
317  JLOG(j.trace()) << "Inserting implied account";
318  auto msr = make_DirectStepI(
319  ctx(),
320  cur->getAccountID(),
321  curIssue.account,
322  curIssue.currency);
323  if (msr.first != tesSUCCESS)
324  return {msr.first, Strand{}};
325  result.push_back(std::move(msr.second));
326  impliedPE.emplace(
328  curIssue.account,
329  xrpCurrency(),
330  xrpAccount());
331  cur = &*impliedPE;
332  }
333  }
334  else if (cur->isAccount() && next->isOffer())
335  {
336  if (curIssue.account != cur->getAccountID())
337  {
338  JLOG(j.trace()) << "Inserting implied account before offer";
339  auto msr = make_DirectStepI(
340  ctx(),
341  cur->getAccountID(),
342  curIssue.account,
343  curIssue.currency);
344  if (msr.first != tesSUCCESS)
345  return {msr.first, Strand{}};
346  result.push_back(std::move(msr.second));
347  impliedPE.emplace(
349  curIssue.account,
350  xrpCurrency(),
351  xrpAccount());
352  cur = &*impliedPE;
353  }
354  }
355  else if (cur->isOffer() && next->isAccount())
356  {
357  if (curIssue.account != next->getAccountID() &&
358  !isXRP(next->getAccountID()))
359  {
360  if (isXRP(curIssue))
361  {
362  if (i != normPath.size() - 2)
363  return {temBAD_PATH, Strand{}};
364  else
365  {
366  // Last step. insert xrp endpoint step
367  auto msr =
368  make_XRPEndpointStep(ctx(), next->getAccountID());
369  if (msr.first != tesSUCCESS)
370  return {msr.first, Strand{}};
371  result.push_back(std::move(msr.second));
372  }
373  }
374  else
375  {
376  JLOG(j.trace()) << "Inserting implied account after offer";
377  auto msr = make_DirectStepI(
378  ctx(),
379  curIssue.account,
380  next->getAccountID(),
381  curIssue.currency);
382  if (msr.first != tesSUCCESS)
383  return {msr.first, Strand{}};
384  result.push_back(std::move(msr.second));
385  }
386  }
387  continue;
388  }
389 
390  if (!next->isOffer() && next->hasCurrency() &&
391  next->getCurrency() != curIssue.currency)
392  {
393  // Should never happen
394  assert(0);
395  return {temBAD_PATH, Strand{}};
396  }
397 
398  auto s = toStep(
399  ctx(/*isLast*/ i == normPath.size() - 2), cur, next, curIssue);
400  if (s.first == tesSUCCESS)
401  result.emplace_back(std::move(s.second));
402  else
403  {
404  JLOG(j.debug()) << "toStep failed: " << s.first;
405  return {s.first, Strand{}};
406  }
407  }
408 
409  auto checkStrand = [&]() -> bool {
410  auto stepAccts = [](Step const& s) -> std::pair<AccountID, AccountID> {
411  if (auto r = s.directStepAccts())
412  return *r;
413  if (auto const r = s.bookStepBook())
414  return std::make_pair(r->in.account, r->out.account);
415  Throw<FlowException>(
416  tefEXCEPTION, "Step should be either a direct or book step");
417  return std::make_pair(xrpAccount(), xrpAccount());
418  };
419 
420  auto curAcc = src;
421  auto curIss = [&] {
422  auto& currency =
423  sendMaxIssue ? sendMaxIssue->currency : deliver.currency;
424  if (isXRP(currency))
425  return xrpIssue();
426  return Issue{currency, src};
427  }();
428 
429  for (auto const& s : result)
430  {
431  auto const accts = stepAccts(*s);
432  if (accts.first != curAcc)
433  return false;
434 
435  if (auto const b = s->bookStepBook())
436  {
437  if (curIss != b->in)
438  return false;
439  curIss = b->out;
440  }
441  else
442  {
443  curIss.account = accts.second;
444  }
445 
446  curAcc = accts.second;
447  }
448  if (curAcc != dst)
449  return false;
450  if (curIss.currency != deliver.currency)
451  return false;
452  if (curIss.account != deliver.account && curIss.account != dst)
453  return false;
454  return true;
455  };
456 
457  if (!checkStrand())
458  {
459  JLOG(j.warn()) << "Flow check strand failed";
460  assert(0);
461  return {temBAD_PATH, Strand{}};
462  }
463 
464  return {tesSUCCESS, std::move(result)};
465 }
466 
469  ReadView const& view,
470  AccountID const& src,
471  AccountID const& dst,
472  Issue const& deliver,
473  std::optional<Quality> const& limitQuality,
474  std::optional<Issue> const& sendMax,
475  STPathSet const& paths,
476  bool addDefaultPath,
477  bool ownerPaysTransferFee,
478  OfferCrossing offerCrossing,
479  AMMContext& ammContext,
480  beast::Journal j)
481 {
482  std::vector<Strand> result;
483  result.reserve(1 + paths.size());
484  // Insert the strand into result if it is not already part of the vector
485  auto insert = [&](Strand s) {
486  bool const hasStrand =
487  std::find(result.begin(), result.end(), s) != result.end();
488 
489  if (!hasStrand)
490  result.emplace_back(std::move(s));
491  };
492 
493  if (addDefaultPath)
494  {
495  auto sp = toStrand(
496  view,
497  src,
498  dst,
499  deliver,
500  limitQuality,
501  sendMax,
502  STPath(),
503  ownerPaysTransferFee,
504  offerCrossing,
505  ammContext,
506  j);
507  auto const ter = sp.first;
508  auto& strand = sp.second;
509 
510  if (ter != tesSUCCESS)
511  {
512  JLOG(j.trace()) << "failed to add default path";
513  if (isTemMalformed(ter) || paths.empty())
514  {
515  return {ter, std::vector<Strand>{}};
516  }
517  }
518  else if (strand.empty())
519  {
520  JLOG(j.trace()) << "toStrand failed";
521  Throw<FlowException>(
522  tefEXCEPTION, "toStrand returned tes & empty strand");
523  }
524  else
525  {
526  insert(std::move(strand));
527  }
528  }
529  else if (paths.empty())
530  {
531  JLOG(j.debug()) << "Flow: Invalid transaction: No paths and direct "
532  "ripple not allowed.";
534  }
535 
536  TER lastFailTer = tesSUCCESS;
537  for (auto const& p : paths)
538  {
539  auto sp = toStrand(
540  view,
541  src,
542  dst,
543  deliver,
544  limitQuality,
545  sendMax,
546  p,
547  ownerPaysTransferFee,
548  offerCrossing,
549  ammContext,
550  j);
551  auto ter = sp.first;
552  auto& strand = sp.second;
553 
554  if (ter != tesSUCCESS)
555  {
556  lastFailTer = ter;
557  JLOG(j.trace()) << "failed to add path: ter: " << ter
558  << "path: " << p.getJson(JsonOptions::none);
559  if (isTemMalformed(ter))
560  return {ter, std::vector<Strand>{}};
561  }
562  else if (strand.empty())
563  {
564  JLOG(j.trace()) << "toStrand failed";
565  Throw<FlowException>(
566  tefEXCEPTION, "toStrand returned tes & empty strand");
567  }
568  else
569  {
570  insert(std::move(strand));
571  }
572  }
573 
574  if (result.empty())
575  return {lastFailTer, std::move(result)};
576 
577  return {tesSUCCESS, std::move(result)};
578 }
579 
581  ReadView const& view_,
582  std::vector<std::unique_ptr<Step>> const& strand_,
583  // A strand may not include an inner node that
584  // replicates the source or destination.
585  AccountID const& strandSrc_,
586  AccountID const& strandDst_,
587  Issue const& strandDeliver_,
588  std::optional<Quality> const& limitQuality_,
589  bool isLast_,
590  bool ownerPaysTransferFee_,
591  OfferCrossing offerCrossing_,
592  bool isDefaultPath_,
593  std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_,
594  boost::container::flat_set<Issue>& seenBookOuts_,
595  AMMContext& ammContext_,
596  beast::Journal j_)
597  : view(view_)
598  , strandSrc(strandSrc_)
599  , strandDst(strandDst_)
600  , strandDeliver(strandDeliver_)
601  , limitQuality(limitQuality_)
602  , isFirst(strand_.empty())
603  , isLast(isLast_)
604  , ownerPaysTransferFee(ownerPaysTransferFee_)
605  , offerCrossing(offerCrossing_)
606  , isDefaultPath(isDefaultPath_)
607  , strandSize(strand_.size())
608  , prevStep(!strand_.empty() ? strand_.back().get() : nullptr)
609  , seenDirectIssues(seenDirectIssues_)
610  , seenBookOuts(seenBookOuts_)
611  , ammContext(ammContext_)
612  , j(j_)
613 {
614 }
615 
616 template <class InAmt, class OutAmt>
617 bool
618 isDirectXrpToXrp(Strand const& strand)
619 {
620  return false;
621 }
622 
623 template <>
624 bool
626 {
627  return (strand.size() == 2);
628 }
629 
630 template bool
631 isDirectXrpToXrp<XRPAmount, IOUAmount>(Strand const& strand);
632 template bool
633 isDirectXrpToXrp<IOUAmount, XRPAmount>(Strand const& strand);
634 template bool
635 isDirectXrpToXrp<IOUAmount, IOUAmount>(Strand const& strand);
636 
637 } // namespace ripple
ripple::StrandContext
Context needed to build Strand Steps and for error checking.
Definition: Steps.h:525
sstream
ripple::OfferCrossing
OfferCrossing
Definition: Steps.h:42
ripple::IOUAmount::exponent
int exponent() const noexcept
Definition: IOUAmount.h:163
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
ripple::STPathElement::isOffer
bool isOffer() const
Definition: STPathSet.h:323
ripple::isConsistent
bool isConsistent(Book const &book)
Definition: Book.cpp:25
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:308
ripple::make_BookStepXI
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1368
ripple::STPathElement::typeAll
@ typeAll
Definition: STPathSet.h:52
ripple::make_BookStepII
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1356
std::pair
std::vector::reserve
T reserve(T... args)
std::vector
STL class.
std::find_if
T find_if(T... args)
std::vector::size
T size(T... args)
ripple::toStrand
std::pair< TER, Strand > toStrand(ReadView const &view, AccountID const &src, AccountID const &dst, Issue const &deliver, std::optional< Quality > const &limitQuality, std::optional< Issue > const &sendMaxIssue, STPath const &path, bool ownerPaysTransferFee, OfferCrossing offerCrossing, AMMContext &ammContext, beast::Journal j)
Create a Strand for the specified path.
Definition: PaySteps.cpp:135
ripple::STPathElement::getCurrency
Currency const & getCurrency() const
Definition: STPathSet.h:361
ripple::isDefaultPath
static bool isDefaultPath(STPath const &path)
Definition: Pathfinder.cpp:455
ripple::checkNear
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:36
ripple::Issue::currency
Currency currency
Definition: Issue.h:38
ripple::isDirectXrpToXrp< IOUAmount, IOUAmount >
template bool isDirectXrpToXrp< IOUAmount, IOUAmount >(Strand const &strand)
std::optional::emplace
T emplace(T... args)
beast::Journal::warn
Stream warn() const
Definition: Journal.h:326
ripple::make_DirectStepI
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
Definition: DirectStep.cpp:976
ripple::IOUAmount
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:43
std::vector::back
T back(T... args)
ripple::temBAD_PATH
@ temBAD_PATH
Definition: TER.h:95
std::vector::front
T front(T... args)
ripple::STPathElement::typeCurrency
@ typeCurrency
Definition: STPathSet.h:49
ripple::STPathSet
Definition: STPathSet.h:176
algorithm
ripple::STPathSet::empty
bool empty() const
Definition: STPathSet.h:503
ripple::STPathElement::typeIssuer
@ typeIssuer
Definition: STPathSet.h:50
std::vector::push_back
T push_back(T... args)
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:82
ripple::toStep
static std::pair< TER, std::unique_ptr< Step > > toStep(StrandContext const &ctx, STPathElement const *e1, STPathElement const *e2, Issue const &curIssue)
Definition: PaySteps.cpp:74
ripple::STPathElement::getNodeType
auto getNodeType() const
Definition: STPathSet.h:317
ripple::STPathSet::size
std::vector< STPath >::size_type size() const
Definition: STPathSet.h:497
ripple::AMMContext
Maintains AMM info per overall payment engine execution and individual iteration.
Definition: AMMContext.h:35
ripple::JsonOptions::none
@ none
ripple::TERSubset< CanCvtToTER >
ripple::make_XRPEndpointStep
std::pair< TER, std::unique_ptr< Step > > make_XRPEndpointStep(StrandContext const &ctx, AccountID const &acc)
Definition: XRPEndpointStep.cpp:399
ripple::Step
A step in a payment path.
Definition: Steps.h:82
std::array
STL class.
ripple::xrpAccount
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:168
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
ripple::StrandContext::isLast
const bool isLast
true if Step is last in Strand
Definition: Steps.h:533
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::isDirectXrpToXrp
bool isDirectXrpToXrp(Strand const &strand)
Definition: PaySteps.cpp:618
ripple::StrandContext::j
const beast::Journal j
Definition: Steps.h:554
ripple::STPathElement::getIssuerID
AccountID const & getIssuerID() const
Definition: STPathSet.h:367
std::vector::rend
T rend(T... args)
ripple::isDirectXrpToXrp< XRPAmount, XRPAmount >
bool isDirectXrpToXrp< XRPAmount, XRPAmount >(Strand const &strand)
Definition: PaySteps.cpp:625
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:54
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::IOUAmount::mantissa
std::int64_t mantissa() const noexcept
Definition: IOUAmount.h:169
ripple::StrandContext::StrandContext
StrandContext(ReadView const &view_, std::vector< std::unique_ptr< Step >> const &strand_, AccountID const &strandSrc_, AccountID const &strandDst_, Issue const &strandDeliver_, std::optional< Quality > const &limitQuality_, bool isLast_, bool ownerPaysTransferFee_, OfferCrossing offerCrossing_, bool isDefaultPath_, std::array< boost::container::flat_set< Issue >, 2 > &seenDirectIssues_, boost::container::flat_set< Issue > &seenBookOuts_, AMMContext &ammContext_, beast::Journal j_)
StrandContext constructor.
Definition: PaySteps.cpp:580
ripple::abs
constexpr Number abs(Number x) noexcept
Definition: Number.h:327
ripple::STPathElement
Definition: STPathSet.h:34
ripple::isXRPAccount
static bool isXRPAccount(STPathElement const &pe)
Definition: PaySteps.cpp:66
std::vector::begin
T begin(T... args)
ripple::toStrands
std::pair< TER, std::vector< Strand > > toStrands(ReadView const &view, AccountID const &src, AccountID const &dst, Issue const &deliver, std::optional< Quality > const &limitQuality, std::optional< Issue > const &sendMax, STPathSet const &paths, bool addDefaultPath, bool ownerPaysTransferFee, OfferCrossing offerCrossing, AMMContext &ammContext, beast::Journal j)
Create a Strand for each specified path (including the default path, if indicated)
Definition: PaySteps.cpp:468
ripple::STPathElement::typeAccount
@ typeAccount
Definition: STPathSet.h:47
ripple::temRIPPLE_EMPTY
@ temRIPPLE_EMPTY
Definition: TER.h:112
ripple::StrandContext::isFirst
const bool isFirst
true if Step is first in Strand
Definition: Steps.h:532
std::vector::empty
T empty(T... args)
ripple::xrpIssue
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:105
std::optional< Quality >
beast::Journal::debug
Stream debug() const
Definition: Journal.h:314
std::size_t
std::make_pair
T make_pair(T... args)
std::vector::end
T end(T... args)
ripple::tefEXCEPTION
@ tefEXCEPTION
Definition: TER.h:166
numeric
std::max
T max(T... args)
ripple::isDirectXrpToXrp< IOUAmount, XRPAmount >
template bool isDirectXrpToXrp< IOUAmount, XRPAmount >(Strand const &strand)
ripple::STPathElement::isAccount
bool isAccount() const
Definition: STPathSet.h:329
ripple::make_BookStepIX
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1362
std::unique_ptr
STL class.
ripple::STPath
Definition: STPathSet.h:118
ripple::isDirectXrpToXrp< XRPAmount, IOUAmount >
template bool isDirectXrpToXrp< XRPAmount, IOUAmount >(Strand const &strand)
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:236
ripple::noAccount
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:175
ripple::xrpCurrency
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:115
ripple::isTemMalformed
bool isTemMalformed(TER x)
Definition: TER.h:619
ripple::Issue::account
AccountID account
Definition: Issue.h:39
std::vector::rbegin
T rbegin(T... args)
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:118
ripple::XRPAmount
Definition: XRPAmount.h:46
ripple::STPathElement::getAccountID
AccountID const & getAccountID() const
Definition: STPathSet.h:355