rippled
Loading...
Searching...
No Matches
PathRequest.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 <xrpld/app/main/Application.h>
21#include <xrpld/app/misc/LoadFeeTrack.h>
22#include <xrpld/app/misc/NetworkOPs.h>
23#include <xrpld/app/paths/AccountCurrencies.h>
24#include <xrpld/app/paths/PathRequest.h>
25#include <xrpld/app/paths/PathRequests.h>
26#include <xrpld/app/paths/RippleCalc.h>
27#include <xrpld/app/paths/detail/PathfinderUtils.h>
28#include <xrpld/core/Config.h>
29#include <xrpld/rpc/detail/Tuning.h>
30
31#include <xrpl/basics/Log.h>
32#include <xrpl/beast/core/LexicalCast.h>
33#include <xrpl/protocol/ErrorCodes.h>
34#include <xrpl/protocol/RPCErr.h>
35#include <xrpl/protocol/UintTypes.h>
36
37#include <optional>
38#include <tuple>
39
40namespace ripple {
41
43 Application& app,
44 std::shared_ptr<InfoSub> const& subscriber,
45 int id,
46 PathRequests& owner,
47 beast::Journal journal)
48 : app_(app)
49 , m_journal(journal)
50 , mOwner(owner)
51 , wpSubscriber(subscriber)
52 , consumer_(subscriber->getConsumer())
53 , jvStatus(Json::objectValue)
54 , mLastIndex(0)
55 , mInProgress(false)
56 , iLevel(0)
57 , bLastSuccess(false)
58 , iIdentifier(id)
59 , created_(std::chrono::steady_clock::now())
60{
61 JLOG(m_journal.debug()) << iIdentifier << " created";
62}
63
65 Application& app,
66 std::function<void(void)> const& completion,
67 Resource::Consumer& consumer,
68 int id,
69 PathRequests& owner,
70 beast::Journal journal)
71 : app_(app)
72 , m_journal(journal)
73 , mOwner(owner)
74 , fCompletion(completion)
75 , consumer_(consumer)
76 , jvStatus(Json::objectValue)
77 , mLastIndex(0)
78 , mInProgress(false)
79 , iLevel(0)
80 , bLastSuccess(false)
81 , iIdentifier(id)
82 , created_(std::chrono::steady_clock::now())
83{
84 JLOG(m_journal.debug()) << iIdentifier << " created";
85}
86
88{
89 using namespace std::chrono;
90 auto stream = m_journal.info();
91 if (!stream)
92 return;
93
94 std::string fast, full;
95 if (quick_reply_ != steady_clock::time_point{})
96 {
97 fast = " fast:";
98 fast += std::to_string(
99 duration_cast<milliseconds>(quick_reply_ - created_).count());
100 fast += "ms";
101 }
102 if (full_reply_ != steady_clock::time_point{})
103 {
104 full = " full:";
105 full += std::to_string(
106 duration_cast<milliseconds>(full_reply_ - created_).count());
107 full += "ms";
108 }
109 stream
110 << iIdentifier << " complete:" << fast << full << " total:"
111 << duration_cast<milliseconds>(steady_clock::now() - created_).count()
112 << "ms";
113}
114
115bool
117{
119
120 // does this path request still need its first full path
121 return mLastIndex == 0;
122}
123
124bool
126{
128
129 if (mInProgress)
130 {
131 // Another thread is handling this
132 return false;
133 }
134
135 if (newOnly && (mLastIndex != 0))
136 {
137 // Only handling new requests, this isn't new
138 return false;
139 }
140
141 if (mLastIndex >= index)
142 {
143 return false;
144 }
145
146 mInProgress = true;
147 return true;
148}
149
150bool
152{
153 return bool(fCompletion);
154}
155
156void
158{
160
161 XRPL_ASSERT(
162 mInProgress, "ripple::PathRequest::updateComplete : in progress");
163 mInProgress = false;
164
165 if (fCompletion)
166 {
167 fCompletion();
169 }
170}
171
172bool
174{
175 if (!raSrcAccount || !raDstAccount)
176 return false;
177
178 if (!convert_all_ && (saSendMax || saDstAmount <= beast::zero))
179 {
180 // If send max specified, dst amt must be -1.
182 return false;
183 }
184
185 auto const& lrLedger = crCache->getLedger();
186
187 if (!lrLedger->exists(keylet::account(*raSrcAccount)))
188 {
189 // Source account does not exist.
191 return false;
192 }
193
194 auto const sleDest = lrLedger->read(keylet::account(*raDstAccount));
195
196 Json::Value& jvDestCur =
197 (jvStatus[jss::destination_currencies] = Json::arrayValue);
198
199 if (!sleDest)
200 {
202 if (!saDstAmount.native())
203 {
204 // Only XRP can be send to a non-existent account.
206 return false;
207 }
208
209 if (!convert_all_ &&
210 saDstAmount < STAmount(lrLedger->fees().accountReserve(0)))
211 {
212 // Payment must meet reserve.
214 return false;
215 }
216 }
217 else
218 {
219 bool const disallowXRP(sleDest->getFlags() & lsfDisallowXRP);
220
221 auto usDestCurrID =
222 accountDestCurrencies(*raDstAccount, crCache, !disallowXRP);
223
224 for (auto const& currency : usDestCurrID)
225 jvDestCur.append(to_string(currency));
226 jvStatus[jss::destination_tag] =
227 (sleDest->getFlags() & lsfRequireDestTag);
228 }
229
230 jvStatus[jss::ledger_hash] = to_string(lrLedger->info().hash);
231 jvStatus[jss::ledger_index] = lrLedger->seq();
232 return true;
233}
234
235/* If this is a normal path request, we want to run it once "fast" now
236 to give preliminary results.
237
238 If this is a legacy path request, we are only going to run it once,
239 and we can't run it in full now, so we don't want to run it at all.
240
241 If there's an error, we need to be sure to return it to the caller
242 in all cases.
243*/
247 Json::Value const& value)
248{
249 bool valid = false;
250
251 if (parseJson(value) != PFR_PJ_INVALID)
252 {
253 valid = isValid(cache);
254 if (!hasCompletion() && valid)
255 doUpdate(cache, true);
256 }
257
258 if (auto stream = m_journal.debug())
259 {
260 if (valid)
261 {
262 stream << iIdentifier << " valid: " << toBase58(*raSrcAccount);
263 stream << iIdentifier << " deliver: " << saDstAmount.getFullText();
264 }
265 else
266 {
267 stream << iIdentifier << " invalid";
268 }
269 }
270
271 return {valid, jvStatus};
272}
273
274int
276{
277 if (!jvParams.isMember(jss::source_account))
278 {
280 return PFR_PJ_INVALID;
281 }
282
283 if (!jvParams.isMember(jss::destination_account))
284 {
286 return PFR_PJ_INVALID;
287 }
288
289 if (!jvParams.isMember(jss::destination_amount))
290 {
292 return PFR_PJ_INVALID;
293 }
294
296 parseBase58<AccountID>(jvParams[jss::source_account].asString());
297 if (!raSrcAccount)
298 {
300 return PFR_PJ_INVALID;
301 }
302
304 parseBase58<AccountID>(jvParams[jss::destination_account].asString());
305 if (!raDstAccount)
306 {
308 return PFR_PJ_INVALID;
309 }
310
311 if (!amountFromJsonNoThrow(saDstAmount, jvParams[jss::destination_amount]))
312 {
314 return PFR_PJ_INVALID;
315 }
316
318
319 if ((saDstAmount.getCurrency().isZero() &&
322 (!convert_all_ && saDstAmount <= beast::zero))
323 {
325 return PFR_PJ_INVALID;
326 }
327
328 if (jvParams.isMember(jss::send_max))
329 {
330 // Send_max requires destination amount to be -1.
331 if (!convert_all_)
332 {
334 return PFR_PJ_INVALID;
335 }
336
338 if (!amountFromJsonNoThrow(*saSendMax, jvParams[jss::send_max]) ||
339 (saSendMax->getCurrency().isZero() &&
340 saSendMax->getIssuer().isNonZero()) ||
341 (saSendMax->getCurrency() == badCurrency()) ||
342 (*saSendMax <= beast::zero &&
343 *saSendMax != STAmount(saSendMax->issue(), 1u, 0, true)))
344 {
346 return PFR_PJ_INVALID;
347 }
348 }
349
350 if (jvParams.isMember(jss::source_currencies))
351 {
352 Json::Value const& jvSrcCurrencies = jvParams[jss::source_currencies];
353 if (!jvSrcCurrencies.isArray() || jvSrcCurrencies.size() == 0 ||
354 jvSrcCurrencies.size() > RPC::Tuning::max_src_cur)
355 {
357 return PFR_PJ_INVALID;
358 }
359
360 sciSourceCurrencies.clear();
361
362 for (auto const& c : jvSrcCurrencies)
363 {
364 // Mandatory currency
365 Currency srcCurrencyID;
366 if (!c.isObject() || !c.isMember(jss::currency) ||
367 !c[jss::currency].isString() ||
368 !to_currency(srcCurrencyID, c[jss::currency].asString()))
369 {
371 return PFR_PJ_INVALID;
372 }
373
374 // Optional issuer
375 AccountID srcIssuerID;
376 if (c.isMember(jss::issuer) &&
377 (!c[jss::issuer].isString() ||
378 !to_issuer(srcIssuerID, c[jss::issuer].asString())))
379 {
381 return PFR_PJ_INVALID;
382 }
383
384 if (srcCurrencyID.isZero())
385 {
386 if (srcIssuerID.isNonZero())
387 {
389 return PFR_PJ_INVALID;
390 }
391 }
392 else if (srcIssuerID.isZero())
393 {
394 srcIssuerID = *raSrcAccount;
395 }
396
397 if (saSendMax)
398 {
399 // If the currencies don't match, ignore the source currency.
400 if (srcCurrencyID == saSendMax->getCurrency())
401 {
402 // If neither is the source and they are not equal, then the
403 // source issuer is illegal.
404 if (srcIssuerID != *raSrcAccount &&
405 saSendMax->getIssuer() != *raSrcAccount &&
406 srcIssuerID != saSendMax->getIssuer())
407 {
409 return PFR_PJ_INVALID;
410 }
411
412 // If both are the source, use the source.
413 // Otherwise, use the one that's not the source.
414 if (srcIssuerID != *raSrcAccount)
415 {
416 sciSourceCurrencies.insert(
417 {srcCurrencyID, srcIssuerID});
418 }
419 else if (saSendMax->getIssuer() != *raSrcAccount)
420 {
421 sciSourceCurrencies.insert(
422 {srcCurrencyID, saSendMax->getIssuer()});
423 }
424 else
425 {
426 sciSourceCurrencies.insert(
427 {srcCurrencyID, *raSrcAccount});
428 }
429 }
430 }
431 else
432 {
433 sciSourceCurrencies.insert({srcCurrencyID, srcIssuerID});
434 }
435 }
436 }
437
438 if (jvParams.isMember(jss::id))
439 jvId = jvParams[jss::id];
440
441 if (jvParams.isMember(jss::domain))
442 {
443 uint256 num;
444 if (!jvParams[jss::domain].isString() ||
445 !num.parseHex(jvParams[jss::domain].asString()))
446 {
448 return PFR_PJ_INVALID;
449 }
450 else
451 {
452 domain = num;
453 }
454 }
455
456 return PFR_PJ_NOCHANGE;
457}
458
461{
462 JLOG(m_journal.debug()) << iIdentifier << " closed";
464 jvStatus[jss::closed] = true;
465 return jvStatus;
466}
467
470{
472 jvStatus[jss::status] = jss::success;
473 return jvStatus;
474}
475
476void
478{
479 JLOG(m_journal.info()) << iIdentifier << " aborting early";
480}
481
486 Currency const& currency,
487 STAmount const& dst_amount,
488 int const level,
489 std::function<bool(void)> const& continueCallback)
490{
491 auto i = currency_map.find(currency);
492 if (i != currency_map.end())
493 return i->second;
494 auto pathfinder = std::make_unique<Pathfinder>(
495 cache,
498 currency,
500 dst_amount,
501 saSendMax,
502 domain,
503 app_);
504 if (pathfinder->findPaths(level, continueCallback))
505 pathfinder->computePathRanks(max_paths_, continueCallback);
506 else
507 pathfinder.reset(); // It's a bad request - clear it.
508 return currency_map[currency] = std::move(pathfinder);
509}
510
511bool
514 int const level,
515 Json::Value& jvArray,
516 std::function<bool(void)> const& continueCallback)
517{
518 auto sourceCurrencies = sciSourceCurrencies;
519 if (sourceCurrencies.empty() && saSendMax)
520 {
521 sourceCurrencies.insert(saSendMax->issue());
522 }
523 if (sourceCurrencies.empty())
524 {
525 auto currencies = accountSourceCurrencies(*raSrcAccount, cache, true);
526 bool const sameAccount = *raSrcAccount == *raDstAccount;
527 for (auto const& c : currencies)
528 {
529 if (!sameAccount || c != saDstAmount.getCurrency())
530 {
531 if (sourceCurrencies.size() >= RPC::Tuning::max_auto_src_cur)
532 return false;
533 sourceCurrencies.insert(
534 {c, c.isZero() ? xrpAccount() : *raSrcAccount});
535 }
536 }
537 }
538
539 auto const dst_amount = convertAmount(saDstAmount, convert_all_);
541 for (auto const& issue : sourceCurrencies)
542 {
543 if (continueCallback && !continueCallback())
544 break;
545 JLOG(m_journal.debug())
546 << iIdentifier
547 << " Trying to find paths: " << STAmount(issue, 1).getFullText();
548
549 auto& pathfinder = getPathFinder(
550 cache,
551 currency_map,
552 issue.currency,
553 dst_amount,
554 level,
555 continueCallback);
556 if (!pathfinder)
557 {
558 JLOG(m_journal.debug()) << iIdentifier << " No paths found";
559 continue;
560 }
561
562 STPath fullLiquidityPath;
563 auto ps = pathfinder->getBestPaths(
565 fullLiquidityPath,
566 mContext[issue],
567 issue.account,
568 continueCallback);
569 mContext[issue] = ps;
570
571 auto const& sourceAccount = [&] {
572 if (!isXRP(issue.account))
573 return issue.account;
574
575 if (isXRP(issue.currency))
576 return xrpAccount();
577
578 return *raSrcAccount;
579 }();
580
581 STAmount saMaxAmount = saSendMax.value_or(
582 STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true));
583
584 JLOG(m_journal.debug())
585 << iIdentifier << " Paths found, calling rippleCalc";
586
588 if (convert_all_)
589 rcInput.partialPaymentAllowed = true;
590 auto sandbox =
591 std::make_unique<PaymentSandbox>(&*cache->getLedger(), tapNONE);
593 *sandbox,
594 saMaxAmount, // --> Amount to send is unlimited
595 // to get an estimate.
596 dst_amount, // --> Amount to deliver.
597 *raDstAccount, // --> Account to deliver to.
598 *raSrcAccount, // --> Account sending from.
599 ps, // --> Path set.
600 domain, // --> Domain.
601 app_.logs(),
602 &rcInput);
603
604 if (!convert_all_ && !fullLiquidityPath.empty() &&
605 (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL))
606 {
607 JLOG(m_journal.debug())
608 << iIdentifier << " Trying with an extra path element";
609
610 ps.push_back(fullLiquidityPath);
611 sandbox =
612 std::make_unique<PaymentSandbox>(&*cache->getLedger(), tapNONE);
614 *sandbox,
615 saMaxAmount, // --> Amount to send is unlimited
616 // to get an estimate.
617 dst_amount, // --> Amount to deliver.
618 *raDstAccount, // --> Account to deliver to.
619 *raSrcAccount, // --> Account sending from.
620 ps, // --> Path set.
621 domain, // --> Domain.
622 app_.logs());
623
624 if (rc.result() != tesSUCCESS)
625 {
626 JLOG(m_journal.warn())
627 << iIdentifier << " Failed with covering path "
628 << transHuman(rc.result());
629 }
630 else
631 {
632 JLOG(m_journal.debug())
633 << iIdentifier << " Extra path element gives "
634 << transHuman(rc.result());
635 }
636 }
637
638 if (rc.result() == tesSUCCESS)
639 {
641 rc.actualAmountIn.setIssuer(sourceAccount);
642 jvEntry[jss::source_amount] =
643 rc.actualAmountIn.getJson(JsonOptions::none);
644 jvEntry[jss::paths_computed] = ps.getJson(JsonOptions::none);
645
646 if (convert_all_)
647 jvEntry[jss::destination_amount] =
648 rc.actualAmountOut.getJson(JsonOptions::none);
649
650 if (hasCompletion())
651 {
652 // Old ripple_path_find API requires this
653 jvEntry[jss::paths_canonical] = Json::arrayValue;
654 }
655
656 jvArray.append(jvEntry);
657 }
658 else
659 {
660 JLOG(m_journal.debug()) << iIdentifier << " rippleCalc returns "
661 << transHuman(rc.result());
662 }
663 }
664
665 /* The resource fee is based on the number of source currencies used.
666 The minimum cost is 50 and the maximum is 400. The cost increases
667 after four source currencies, 50 - (4 * 4) = 34.
668 */
669 int const size = sourceCurrencies.size();
670 consumer_.charge({std::clamp(size * size + 34, 50, 400), "path update"});
671 return true;
672}
673
677 bool fast,
678 std::function<bool(void)> const& continueCallback)
679{
680 using namespace std::chrono;
681 JLOG(m_journal.debug())
682 << iIdentifier << " update " << (fast ? "fast" : "normal");
683
684 {
686
687 if (!isValid(cache))
688 return jvStatus;
689 }
690
691 Json::Value newStatus = Json::objectValue;
692
693 if (hasCompletion())
694 {
695 // Old ripple_path_find API gives destination_currencies
696 auto& destCurrencies =
697 (newStatus[jss::destination_currencies] = Json::arrayValue);
698 auto usCurrencies = accountDestCurrencies(*raDstAccount, cache, true);
699 for (auto const& c : usCurrencies)
700 destCurrencies.append(to_string(c));
701 }
702
703 newStatus[jss::source_account] = toBase58(*raSrcAccount);
704 newStatus[jss::destination_account] = toBase58(*raDstAccount);
705 newStatus[jss::destination_amount] = saDstAmount.getJson(JsonOptions::none);
706 newStatus[jss::full_reply] = !fast;
707
708 if (jvId)
709 newStatus[jss::id] = jvId;
710
711 bool loaded = app_.getFeeTrack().isLoadedLocal();
712
713 if (iLevel == 0)
714 {
715 // first pass
716 if (loaded || fast)
718 else
720 }
721 else if ((iLevel == app_.config().PATH_SEARCH_FAST) && !fast)
722 {
723 // leaving fast pathfinding
725 if (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))
726 --iLevel;
727 }
728 else if (bLastSuccess)
729 {
730 // decrement, if possible
731 if (iLevel > app_.config().PATH_SEARCH ||
732 (loaded && (iLevel > app_.config().PATH_SEARCH_FAST)))
733 --iLevel;
734 }
735 else
736 {
737 // adjust as needed
738 if (!loaded && (iLevel < app_.config().PATH_SEARCH_MAX))
739 ++iLevel;
740 if (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))
741 --iLevel;
742 }
743
744 JLOG(m_journal.debug()) << iIdentifier << " processing at level " << iLevel;
745
747 if (findPaths(cache, iLevel, jvArray, continueCallback))
748 {
749 bLastSuccess = jvArray.size() != 0;
750 newStatus[jss::alternatives] = std::move(jvArray);
751 }
752 else
753 {
754 bLastSuccess = false;
755 newStatus = rpcError(rpcINTERNAL);
756 }
757
758 if (fast && quick_reply_ == steady_clock::time_point{})
759 {
760 quick_reply_ = steady_clock::now();
761 mOwner.reportFast(duration_cast<milliseconds>(quick_reply_ - created_));
762 }
763 else if (!fast && full_reply_ == steady_clock::time_point{})
764 {
765 full_reply_ = steady_clock::now();
766 mOwner.reportFull(duration_cast<milliseconds>(full_reply_ - created_));
767 }
768
769 {
771 jvStatus = newStatus;
772 }
773
774 JLOG(m_journal.debug())
775 << iIdentifier << " update finished " << (fast ? "fast" : "normal");
776 return newStatus;
777}
778
781{
782 return wpSubscriber.lock();
783}
784
785} // namespace ripple
T clamp(T... args)
Represents a JSON value.
Definition json_value.h:149
bool isArray() const
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:60
Stream debug() const
Definition Journal.h:328
Stream info() const
Definition Journal.h:334
Stream warn() const
Definition Journal.h:340
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual Logs & logs()=0
int PATH_SEARCH
Definition Config.h:196
int PATH_SEARCH_MAX
Definition Config.h:198
int PATH_SEARCH_FAST
Definition Config.h:197
A currency issued by an account.
Definition Issue.h:33
Currency currency
Definition Issue.h:35
bool isLoadedLocal() const
std::optional< STAmount > saSendMax
std::function< void(void)> fCompletion
static unsigned int const max_paths_
bool findPaths(std::shared_ptr< RippleLineCache > const &, int const, Json::Value &, std::function< bool(void)> const &)
Finds and sets a PathSet in the JSON argument.
Json::Value doClose() override
std::weak_ptr< InfoSub > wpSubscriber
Json::Value doStatus(Json::Value const &) override
std::set< Issue > sciSourceCurrencies
int parseJson(Json::Value const &)
bool needsUpdate(bool newOnly, LedgerIndex index)
PathRequest(Application &app, std::shared_ptr< InfoSub > const &subscriber, int id, PathRequests &, beast::Journal journal)
std::recursive_mutex mLock
std::recursive_mutex mIndexLock
Resource::Consumer & consumer_
std::optional< AccountID > raSrcAccount
InfoSub::pointer getSubscriber() const
std::optional< uint256 > domain
Json::Value doUpdate(std::shared_ptr< RippleLineCache > const &, bool fast, std::function< bool(void)> const &continueCallback={})
PathRequests & mOwner
std::chrono::steady_clock::time_point full_reply_
LedgerIndex mLastIndex
std::pair< bool, Json::Value > doCreate(std::shared_ptr< RippleLineCache > const &, Json::Value const &)
Application & app_
Json::Value jvStatus
void doAborting() const
std::chrono::steady_clock::time_point quick_reply_
std::map< Issue, STPathSet > mContext
std::optional< AccountID > raDstAccount
std::chrono::steady_clock::time_point const created_
beast::Journal m_journal
std::unique_ptr< Pathfinder > const & getPathFinder(std::shared_ptr< RippleLineCache > const &, hash_map< Currency, std::unique_ptr< Pathfinder > > &, Currency const &, STAmount const &, int const, std::function< bool(void)> const &)
bool isValid(std::shared_ptr< RippleLineCache > const &crCache)
void reportFast(std::chrono::milliseconds ms)
void reportFull(std::chrono::milliseconds ms)
An endpoint that consumes resources.
Definition Consumer.h:35
Disposition charge(Charge const &fee, std::string const &context={})
Apply a load charge to the consumer.
Definition Consumer.cpp:106
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:795
Currency const & getCurrency() const
Definition STAmount.h:502
AccountID const & getIssuer() const
Definition STAmount.h:508
Issue const & issue() const
Definition STAmount.h:496
std::string getFullText() const override
Definition STAmount.cpp:696
bool native() const noexcept
Definition STAmount.h:458
bool empty() const
Definition STPathSet.h:404
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:503
bool isZero() const
Definition base_uint.h:540
bool isNonZero() const
Definition base_uint.h:545
static Output rippleCalculate(PaymentSandbox &view, STAmount const &saMaxAmountReq, STAmount const &saDstAmountReq, AccountID const &uDstAccountID, AccountID const &uSrcAccountID, STPathSet const &spsPaths, std::optional< uint256 > const &domainID, Logs &l, Input const *const pInputs=nullptr)
T emplace(T... args)
T is_same_v
T lock(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:25
@ arrayValue
array value (ordered list)
Definition json_value.h:44
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:45
static int constexpr max_src_cur
Maximum number of source currencies allowed in a path find request.
static int constexpr max_auto_src_cur
Maximum number of auto source currencies in a path find request.
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::string transHuman(TER code)
Definition TER.cpp:273
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
STAmount convertAmount(STAmount const &amt, bool all)
bool isXRP(AccountID const &c)
Definition AccountID.h:90
AccountID const & xrpAccount()
Compute AccountID from public key.
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
@ rpcACT_NOT_FOUND
Definition ErrorCodes.h:70
@ rpcDST_ACT_MISSING
Definition ErrorCodes.h:104
@ rpcSRC_ACT_NOT_FOUND
Definition ErrorCodes.h:122
@ rpcSENDMAX_MALFORMED
Definition ErrorCodes.h:119
@ rpcDOMAIN_MALFORMED
Definition ErrorCodes.h:158
@ rpcINTERNAL
Definition ErrorCodes.h:130
@ rpcSRC_ACT_MALFORMED
Definition ErrorCodes.h:120
@ rpcSRC_ISR_MALFORMED
Definition ErrorCodes.h:125
@ rpcDST_AMT_MALFORMED
Definition ErrorCodes.h:106
@ rpcDST_ACT_MALFORMED
Definition ErrorCodes.h:103
@ rpcSRC_CUR_MALFORMED
Definition ErrorCodes.h:124
@ rpcDST_AMT_MISSING
Definition ErrorCodes.h:107
@ rpcSRC_ACT_MISSING
Definition ErrorCodes.h:121
hash_set< Currency > accountDestCurrencies(AccountID const &account, std::shared_ptr< RippleLineCache > const &lrCache, bool includeXRP)
hash_set< Currency > accountSourceCurrencies(AccountID const &account, std::shared_ptr< RippleLineCache > const &lrCache, bool includeXRP)
@ lsfRequireDestTag
static std::string const & systemCurrencyCode()
Json::Value rpcError(int iError)
Definition RPCErr.cpp:31
@ tecPATH_PARTIAL
Definition TER.h:282
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
@ tesSUCCESS
Definition TER.h:244
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
@ tapNONE
Definition ApplyView.h:32
@ terNO_LINE
Definition TER.h:219
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:84
STL namespace.
T to_string(T... args)
T value_or(T... args)