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_ && saDstAmount < STAmount(lrLedger->fees().reserve))
210 {
211 // Payment must meet reserve.
213 return false;
214 }
215 }
216 else
217 {
218 bool const disallowXRP(sleDest->getFlags() & lsfDisallowXRP);
219
220 auto usDestCurrID =
221 accountDestCurrencies(*raDstAccount, crCache, !disallowXRP);
222
223 for (auto const& currency : usDestCurrID)
224 jvDestCur.append(to_string(currency));
225 jvStatus[jss::destination_tag] =
226 (sleDest->getFlags() & lsfRequireDestTag);
227 }
228
229 jvStatus[jss::ledger_hash] = to_string(lrLedger->info().hash);
230 jvStatus[jss::ledger_index] = lrLedger->seq();
231 return true;
232}
233
234/* If this is a normal path request, we want to run it once "fast" now
235 to give preliminary results.
236
237 If this is a legacy path request, we are only going to run it once,
238 and we can't run it in full now, so we don't want to run it at all.
239
240 If there's an error, we need to be sure to return it to the caller
241 in all cases.
242*/
246 Json::Value const& value)
247{
248 bool valid = false;
249
250 if (parseJson(value) != PFR_PJ_INVALID)
251 {
252 valid = isValid(cache);
253 if (!hasCompletion() && valid)
254 doUpdate(cache, true);
255 }
256
257 if (auto stream = m_journal.debug())
258 {
259 if (valid)
260 {
261 stream << iIdentifier << " valid: " << toBase58(*raSrcAccount);
262 stream << iIdentifier << " deliver: " << saDstAmount.getFullText();
263 }
264 else
265 {
266 stream << iIdentifier << " invalid";
267 }
268 }
269
270 return {valid, jvStatus};
271}
272
273int
275{
276 if (!jvParams.isMember(jss::source_account))
277 {
279 return PFR_PJ_INVALID;
280 }
281
282 if (!jvParams.isMember(jss::destination_account))
283 {
285 return PFR_PJ_INVALID;
286 }
287
288 if (!jvParams.isMember(jss::destination_amount))
289 {
291 return PFR_PJ_INVALID;
292 }
293
295 parseBase58<AccountID>(jvParams[jss::source_account].asString());
296 if (!raSrcAccount)
297 {
299 return PFR_PJ_INVALID;
300 }
301
303 parseBase58<AccountID>(jvParams[jss::destination_account].asString());
304 if (!raDstAccount)
305 {
307 return PFR_PJ_INVALID;
308 }
309
310 if (!amountFromJsonNoThrow(saDstAmount, jvParams[jss::destination_amount]))
311 {
313 return PFR_PJ_INVALID;
314 }
315
317
318 if ((saDstAmount.getCurrency().isZero() &&
321 (!convert_all_ && saDstAmount <= beast::zero))
322 {
324 return PFR_PJ_INVALID;
325 }
326
327 if (jvParams.isMember(jss::send_max))
328 {
329 // Send_max requires destination amount to be -1.
330 if (!convert_all_)
331 {
333 return PFR_PJ_INVALID;
334 }
335
337 if (!amountFromJsonNoThrow(*saSendMax, jvParams[jss::send_max]) ||
338 (saSendMax->getCurrency().isZero() &&
339 saSendMax->getIssuer().isNonZero()) ||
340 (saSendMax->getCurrency() == badCurrency()) ||
341 (*saSendMax <= beast::zero &&
342 *saSendMax != STAmount(saSendMax->issue(), 1u, 0, true)))
343 {
345 return PFR_PJ_INVALID;
346 }
347 }
348
349 if (jvParams.isMember(jss::source_currencies))
350 {
351 Json::Value const& jvSrcCurrencies = jvParams[jss::source_currencies];
352 if (!jvSrcCurrencies.isArray() || jvSrcCurrencies.size() == 0 ||
353 jvSrcCurrencies.size() > RPC::Tuning::max_src_cur)
354 {
356 return PFR_PJ_INVALID;
357 }
358
359 sciSourceCurrencies.clear();
360
361 for (auto const& c : jvSrcCurrencies)
362 {
363 // Mandatory currency
364 Currency srcCurrencyID;
365 if (!c.isObject() || !c.isMember(jss::currency) ||
366 !c[jss::currency].isString() ||
367 !to_currency(srcCurrencyID, c[jss::currency].asString()))
368 {
370 return PFR_PJ_INVALID;
371 }
372
373 // Optional issuer
374 AccountID srcIssuerID;
375 if (c.isMember(jss::issuer) &&
376 (!c[jss::issuer].isString() ||
377 !to_issuer(srcIssuerID, c[jss::issuer].asString())))
378 {
380 return PFR_PJ_INVALID;
381 }
382
383 if (srcCurrencyID.isZero())
384 {
385 if (srcIssuerID.isNonZero())
386 {
388 return PFR_PJ_INVALID;
389 }
390 }
391 else if (srcIssuerID.isZero())
392 {
393 srcIssuerID = *raSrcAccount;
394 }
395
396 if (saSendMax)
397 {
398 // If the currencies don't match, ignore the source currency.
399 if (srcCurrencyID == saSendMax->getCurrency())
400 {
401 // If neither is the source and they are not equal, then the
402 // source issuer is illegal.
403 if (srcIssuerID != *raSrcAccount &&
404 saSendMax->getIssuer() != *raSrcAccount &&
405 srcIssuerID != saSendMax->getIssuer())
406 {
408 return PFR_PJ_INVALID;
409 }
410
411 // If both are the source, use the source.
412 // Otherwise, use the one that's not the source.
413 if (srcIssuerID != *raSrcAccount)
414 {
415 sciSourceCurrencies.insert(
416 {srcCurrencyID, srcIssuerID});
417 }
418 else if (saSendMax->getIssuer() != *raSrcAccount)
419 {
420 sciSourceCurrencies.insert(
421 {srcCurrencyID, saSendMax->getIssuer()});
422 }
423 else
424 {
425 sciSourceCurrencies.insert(
426 {srcCurrencyID, *raSrcAccount});
427 }
428 }
429 }
430 else
431 {
432 sciSourceCurrencies.insert({srcCurrencyID, srcIssuerID});
433 }
434 }
435 }
436
437 if (jvParams.isMember(jss::id))
438 jvId = jvParams[jss::id];
439
440 if (jvParams.isMember(jss::domain))
441 {
442 uint256 num;
443 if (!jvParams[jss::domain].isString() ||
444 !num.parseHex(jvParams[jss::domain].asString()))
445 {
447 return PFR_PJ_INVALID;
448 }
449 else
450 {
451 domain = num;
452 }
453 }
454
455 return PFR_PJ_NOCHANGE;
456}
457
460{
461 JLOG(m_journal.debug()) << iIdentifier << " closed";
463 jvStatus[jss::closed] = true;
464 return jvStatus;
465}
466
469{
471 jvStatus[jss::status] = jss::success;
472 return jvStatus;
473}
474
475void
477{
478 JLOG(m_journal.info()) << iIdentifier << " aborting early";
479}
480
485 Currency const& currency,
486 STAmount const& dst_amount,
487 int const level,
488 std::function<bool(void)> const& continueCallback)
489{
490 auto i = currency_map.find(currency);
491 if (i != currency_map.end())
492 return i->second;
493 auto pathfinder = std::make_unique<Pathfinder>(
494 cache,
497 currency,
499 dst_amount,
500 saSendMax,
501 domain,
502 app_);
503 if (pathfinder->findPaths(level, continueCallback))
504 pathfinder->computePathRanks(max_paths_, continueCallback);
505 else
506 pathfinder.reset(); // It's a bad request - clear it.
507 return currency_map[currency] = std::move(pathfinder);
508}
509
510bool
513 int const level,
514 Json::Value& jvArray,
515 std::function<bool(void)> const& continueCallback)
516{
517 auto sourceCurrencies = sciSourceCurrencies;
518 if (sourceCurrencies.empty() && saSendMax)
519 {
520 sourceCurrencies.insert(saSendMax->issue());
521 }
522 if (sourceCurrencies.empty())
523 {
524 auto currencies = accountSourceCurrencies(*raSrcAccount, cache, true);
525 bool const sameAccount = *raSrcAccount == *raDstAccount;
526 for (auto const& c : currencies)
527 {
528 if (!sameAccount || c != saDstAmount.getCurrency())
529 {
530 if (sourceCurrencies.size() >= RPC::Tuning::max_auto_src_cur)
531 return false;
532 sourceCurrencies.insert(
533 {c, c.isZero() ? xrpAccount() : *raSrcAccount});
534 }
535 }
536 }
537
538 auto const dst_amount = convertAmount(saDstAmount, convert_all_);
540 for (auto const& issue : sourceCurrencies)
541 {
542 if (continueCallback && !continueCallback())
543 break;
544 JLOG(m_journal.debug())
545 << iIdentifier
546 << " Trying to find paths: " << STAmount(issue, 1).getFullText();
547
548 auto& pathfinder = getPathFinder(
549 cache,
550 currency_map,
551 issue.currency,
552 dst_amount,
553 level,
554 continueCallback);
555 if (!pathfinder)
556 {
557 JLOG(m_journal.debug()) << iIdentifier << " No paths found";
558 continue;
559 }
560
561 STPath fullLiquidityPath;
562 auto ps = pathfinder->getBestPaths(
564 fullLiquidityPath,
565 mContext[issue],
566 issue.account,
567 continueCallback);
568 mContext[issue] = ps;
569
570 auto const& sourceAccount = [&] {
571 if (!isXRP(issue.account))
572 return issue.account;
573
574 if (isXRP(issue.currency))
575 return xrpAccount();
576
577 return *raSrcAccount;
578 }();
579
580 STAmount saMaxAmount = saSendMax.value_or(
581 STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true));
582
583 JLOG(m_journal.debug())
584 << iIdentifier << " Paths found, calling rippleCalc";
585
587 if (convert_all_)
588 rcInput.partialPaymentAllowed = true;
589 auto sandbox =
590 std::make_unique<PaymentSandbox>(&*cache->getLedger(), tapNONE);
592 *sandbox,
593 saMaxAmount, // --> Amount to send is unlimited
594 // to get an estimate.
595 dst_amount, // --> Amount to deliver.
596 *raDstAccount, // --> Account to deliver to.
597 *raSrcAccount, // --> Account sending from.
598 ps, // --> Path set.
599 domain, // --> Domain.
600 app_.logs(),
601 &rcInput);
602
603 if (!convert_all_ && !fullLiquidityPath.empty() &&
604 (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL))
605 {
606 JLOG(m_journal.debug())
607 << iIdentifier << " Trying with an extra path element";
608
609 ps.push_back(fullLiquidityPath);
610 sandbox =
611 std::make_unique<PaymentSandbox>(&*cache->getLedger(), tapNONE);
613 *sandbox,
614 saMaxAmount, // --> Amount to send is unlimited
615 // to get an estimate.
616 dst_amount, // --> Amount to deliver.
617 *raDstAccount, // --> Account to deliver to.
618 *raSrcAccount, // --> Account sending from.
619 ps, // --> Path set.
620 domain, // --> Domain.
621 app_.logs());
622
623 if (rc.result() != tesSUCCESS)
624 {
625 JLOG(m_journal.warn())
626 << iIdentifier << " Failed with covering path "
627 << transHuman(rc.result());
628 }
629 else
630 {
631 JLOG(m_journal.debug())
632 << iIdentifier << " Extra path element gives "
633 << transHuman(rc.result());
634 }
635 }
636
637 if (rc.result() == tesSUCCESS)
638 {
640 rc.actualAmountIn.setIssuer(sourceAccount);
641 jvEntry[jss::source_amount] =
642 rc.actualAmountIn.getJson(JsonOptions::none);
643 jvEntry[jss::paths_computed] = ps.getJson(JsonOptions::none);
644
645 if (convert_all_)
646 jvEntry[jss::destination_amount] =
647 rc.actualAmountOut.getJson(JsonOptions::none);
648
649 if (hasCompletion())
650 {
651 // Old ripple_path_find API requires this
652 jvEntry[jss::paths_canonical] = Json::arrayValue;
653 }
654
655 jvArray.append(jvEntry);
656 }
657 else
658 {
659 JLOG(m_journal.debug()) << iIdentifier << " rippleCalc returns "
660 << transHuman(rc.result());
661 }
662 }
663
664 /* The resource fee is based on the number of source currencies used.
665 The minimum cost is 50 and the maximum is 400. The cost increases
666 after four source currencies, 50 - (4 * 4) = 34.
667 */
668 int const size = sourceCurrencies.size();
669 consumer_.charge({std::clamp(size * size + 34, 50, 400), "path update"});
670 return true;
671}
672
676 bool fast,
677 std::function<bool(void)> const& continueCallback)
678{
679 using namespace std::chrono;
680 JLOG(m_journal.debug())
681 << iIdentifier << " update " << (fast ? "fast" : "normal");
682
683 {
685
686 if (!isValid(cache))
687 return jvStatus;
688 }
689
690 Json::Value newStatus = Json::objectValue;
691
692 if (hasCompletion())
693 {
694 // Old ripple_path_find API gives destination_currencies
695 auto& destCurrencies =
696 (newStatus[jss::destination_currencies] = Json::arrayValue);
697 auto usCurrencies = accountDestCurrencies(*raDstAccount, cache, true);
698 for (auto const& c : usCurrencies)
699 destCurrencies.append(to_string(c));
700 }
701
702 newStatus[jss::source_account] = toBase58(*raSrcAccount);
703 newStatus[jss::destination_account] = toBase58(*raDstAccount);
704 newStatus[jss::destination_amount] = saDstAmount.getJson(JsonOptions::none);
705 newStatus[jss::full_reply] = !fast;
706
707 if (jvId)
708 newStatus[jss::id] = jvId;
709
710 bool loaded = app_.getFeeTrack().isLoadedLocal();
711
712 if (iLevel == 0)
713 {
714 // first pass
715 if (loaded || fast)
717 else
719 }
720 else if ((iLevel == app_.config().PATH_SEARCH_FAST) && !fast)
721 {
722 // leaving fast pathfinding
724 if (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))
725 --iLevel;
726 }
727 else if (bLastSuccess)
728 {
729 // decrement, if possible
730 if (iLevel > app_.config().PATH_SEARCH ||
731 (loaded && (iLevel > app_.config().PATH_SEARCH_FAST)))
732 --iLevel;
733 }
734 else
735 {
736 // adjust as needed
737 if (!loaded && (iLevel < app_.config().PATH_SEARCH_MAX))
738 ++iLevel;
739 if (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))
740 --iLevel;
741 }
742
743 JLOG(m_journal.debug()) << iIdentifier << " processing at level " << iLevel;
744
746 if (findPaths(cache, iLevel, jvArray, continueCallback))
747 {
748 bLastSuccess = jvArray.size() != 0;
749 newStatus[jss::alternatives] = std::move(jvArray);
750 }
751 else
752 {
753 bLastSuccess = false;
754 newStatus = rpcError(rpcINTERNAL);
755 }
756
757 if (fast && quick_reply_ == steady_clock::time_point{})
758 {
759 quick_reply_ = steady_clock::now();
760 mOwner.reportFast(duration_cast<milliseconds>(quick_reply_ - created_));
761 }
762 else if (!fast && full_reply_ == steady_clock::time_point{})
763 {
764 full_reply_ = steady_clock::now();
765 mOwner.reportFull(duration_cast<milliseconds>(full_reply_ - created_));
766 }
767
768 {
770 jvStatus = newStatus;
771 }
772
773 JLOG(m_journal.debug())
774 << iIdentifier << " update finished " << (fast ? "fast" : "normal");
775 return newStatus;
776}
777
780{
781 return wpSubscriber.lock();
782}
783
784} // 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:31
@ 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)