xrpld
Loading...
Searching...
No Matches
PathRequest.cpp
1#include <xrpld/rpc/detail/PathRequest.h>
2
3#include <xrpld/app/main/Application.h>
4#include <xrpld/core/Config.h>
5#include <xrpld/rpc/detail/AccountAssets.h>
6#include <xrpld/rpc/detail/PathRequestManager.h>
7#include <xrpld/rpc/detail/Pathfinder.h>
8#include <xrpld/rpc/detail/PathfinderUtils.h>
9#include <xrpld/rpc/detail/Tuning.h>
10
11#include <xrpl/basics/Log.h>
12#include <xrpl/basics/UnorderedContainers.h>
13#include <xrpl/basics/base_uint.h>
14#include <xrpl/beast/utility/Journal.h>
15#include <xrpl/beast/utility/Zero.h>
16#include <xrpl/beast/utility/instrumentation.h>
17#include <xrpl/json/json_value.h>
18#include <xrpl/ledger/ApplyView.h>
19#include <xrpl/ledger/PaymentSandbox.h>
20#include <xrpl/protocol/AccountID.h>
21#include <xrpl/protocol/Asset.h>
22#include <xrpl/protocol/ErrorCodes.h>
23#include <xrpl/protocol/Indexes.h>
24#include <xrpl/protocol/LedgerFormats.h>
25#include <xrpl/protocol/PathAsset.h>
26#include <xrpl/protocol/Protocol.h>
27#include <xrpl/protocol/RPCErr.h>
28#include <xrpl/protocol/STAmount.h>
29#include <xrpl/protocol/STPathSet.h>
30#include <xrpl/protocol/SystemParameters.h>
31#include <xrpl/protocol/TER.h>
32#include <xrpl/protocol/UintTypes.h>
33#include <xrpl/protocol/jss.h>
34#include <xrpl/resource/Consumer.h>
35#include <xrpl/server/InfoSub.h>
36#include <xrpl/server/LoadFeeTrack.h>
37#include <xrpl/tx/paths/RippleCalc.h>
38
39#include <algorithm>
40#include <chrono>
41#include <functional>
42#include <memory>
43#include <mutex>
44#include <optional>
45#include <string>
46#include <utility>
47#include <variant>
48
49namespace xrpl {
50
52 Application& app,
53 std::shared_ptr<InfoSub> const& subscriber,
54 int id,
55 PathRequestManager& owner,
56 beast::Journal journal)
57 : app_(app)
58 , journal_(journal)
59 , owner_(owner)
60 , wpSubscriber_(subscriber)
61 , consumer_(subscriber->getConsumer())
62 , jvStatus_(json::ValueType::Object)
63 , lastIndex_(0)
64 , inProgress_(false)
65 , iLevel_(0)
66 , bLastSuccess_(false)
67 , iIdentifier_(id)
68 , created_(std::chrono::steady_clock::now())
69{
70 JLOG(journal_.debug()) << iIdentifier_ << " created";
71}
72
74 Application& app,
75 std::function<void(void)> completion,
76 Resource::Consumer& consumer,
77 int id,
78 PathRequestManager& owner,
79 beast::Journal journal)
80 : app_(app)
81 , journal_(journal)
82 , owner_(owner)
83 , fCompletion_(std::move(completion))
84 , consumer_(consumer)
85 , jvStatus_(json::ValueType::Object)
86 , lastIndex_(0)
87 , inProgress_(false)
88 , iLevel_(0)
89 , bLastSuccess_(false)
90 , iIdentifier_(id)
91 , created_(std::chrono::steady_clock::now())
92{
93 JLOG(journal_.debug()) << iIdentifier_ << " created";
94}
95
97{
98 using namespace std::chrono;
99 auto stream = journal_.info();
100 if (!stream)
101 return;
102
103 std::string fast, full;
104 if (quickReply_ != steady_clock::time_point{})
105 {
106 fast = " fast:";
108 fast += "ms";
109 }
110 if (fullReply_ != steady_clock::time_point{})
111 {
112 full = " full:";
114 full += "ms";
115 }
116 stream << iIdentifier_ << " complete:" << fast << full
117 << " total:" << duration_cast<milliseconds>(steady_clock::now() - created_).count()
118 << "ms";
119}
120
121bool
123{
125
126 // does this path request still need its first full path
127 return lastIndex_ == 0;
128}
129
130bool
132{
134
135 if (inProgress_)
136 {
137 // Another thread is handling this
138 return false;
139 }
140
141 if (newOnly && (lastIndex_ != 0))
142 {
143 // Only handling new requests, this isn't new
144 return false;
145 }
146
147 if (lastIndex_ >= index)
148 {
149 return false;
150 }
151
152 inProgress_ = true;
153 return true;
154}
155
156bool
158{
159 return bool(fCompletion_);
160}
161
162void
164{
166
167 XRPL_ASSERT(inProgress_, "xrpl::PathRequest::updateComplete : in progress");
168 inProgress_ = false;
169
170 if (fCompletion_)
171 {
172 fCompletion_();
174 }
175}
176
177bool
179{
181 return false;
182
183 if (!convertAll_ && (saSendMax_ || saDstAmount_ <= beast::kZero))
184 {
185 // If send max specified, dst amt must be -1.
187 return false;
188 }
189
190 auto const& lrLedger = crCache->getLedger();
191
192 if (!lrLedger->exists(keylet::account(*raSrcAccount_)))
193 {
194 // Source account does not exist.
196 return false;
197 }
198
199 auto const sleDest = lrLedger->read(keylet::account(*raDstAccount_));
200
201 json::Value& jvDestCur = (jvStatus_[jss::destination_currencies] = json::ValueType::Array);
202
203 if (!sleDest)
204 {
206 if (!saDstAmount_.native())
207 {
208 // Only XRP can be send to a non-existent account.
210 return false;
211 }
212
213 if (!convertAll_ && saDstAmount_ < STAmount(lrLedger->fees().reserve))
214 {
215 // Payment must meet reserve.
217 return false;
218 }
219 }
220 else
221 {
222 bool const disallowXRP(sleDest->isFlag(lsfDisallowXRP));
223
224 auto const destAssets = accountDestAssets(*raDstAccount_, crCache, !disallowXRP);
225
226 for (auto const& asset : destAssets)
227 jvDestCur.append(to_string(asset));
228
229 jvStatus_[jss::destination_tag] = (sleDest->getFlags() & lsfRequireDestTag);
230 }
231
232 jvStatus_[jss::ledger_hash] = to_string(lrLedger->header().hash);
233 jvStatus_[jss::ledger_index] = lrLedger->seq();
234 return true;
235}
236
237/* If this is a normal path request, we want to run it once "fast" now
238 to give preliminary results.
239
240 If this is a legacy path request, we are only going to run it once,
241 and we can't run it in full now, so we don't want to run it at all.
242
243 If there's an error, we need to be sure to return it to the caller
244 in all cases.
245*/
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 = 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
295 raSrcAccount_ = parseBase58<AccountID>(jvParams[jss::source_account].asString());
296 if (!raSrcAccount_)
297 {
299 return PFR_PJ_INVALID;
300 }
301
302 raDstAccount_ = parseBase58<AccountID>(jvParams[jss::destination_account].asString());
303 if (!raDstAccount_)
304 {
306 return PFR_PJ_INVALID;
307 }
308
309 if (!amountFromJsonNoThrow(saDstAmount_, jvParams[jss::destination_amount]))
310 {
312 return PFR_PJ_INVALID;
313 }
314
315 convertAll_ = saDstAmount_ == STAmount(saDstAmount_.asset(), 1u, 0, true);
316
317 if (!validAsset(saDstAmount_.asset()) || (!convertAll_ && saDstAmount_ <= beast::kZero))
318 {
320 return PFR_PJ_INVALID;
321 }
322
323 if (jvParams.isMember(jss::send_max))
324 {
325 // Send_max requires destination amount to be -1.
326 if (!convertAll_)
327 {
329 return PFR_PJ_INVALID;
330 }
331
332 saSendMax_.emplace();
333 if (!amountFromJsonNoThrow(*saSendMax_, jvParams[jss::send_max]) ||
334 !validAsset(saSendMax_->asset()) ||
335 (*saSendMax_ <= beast::kZero &&
336 *saSendMax_ != STAmount(saSendMax_->asset(), 1u, 0, true)))
337 {
339 return PFR_PJ_INVALID;
340 }
341 }
342
343 if (jvParams.isMember(jss::source_currencies))
344 {
345 json::Value const& jvSrcCurrencies = jvParams[jss::source_currencies];
346 if (!jvSrcCurrencies.isArray() || jvSrcCurrencies.size() == 0 ||
347 jvSrcCurrencies.size() > RPC::Tuning::kMaxSrcCur)
348 {
350 return PFR_PJ_INVALID;
351 }
352
353 sciSourceAssets_.clear();
354
355 for (auto const& c : jvSrcCurrencies)
356 {
357 // Mandatory currency or MPT
358 if (!validJSONAsset(c) || !c.isObject())
359 {
361 return PFR_PJ_INVALID;
362 }
363
364 PathAsset srcPathAsset;
365 if (c.isMember(jss::currency))
366 {
367 Currency currency;
368 if (!c[jss::currency].isString() ||
369 !toCurrency(currency, c[jss::currency].asString()))
370 {
372 return PFR_PJ_INVALID;
373 }
374 srcPathAsset = currency;
375 }
376 else
377 {
378 uint192 u;
379 if (!c[jss::mpt_issuance_id].isString() ||
380 !u.parseHex(c[jss::mpt_issuance_id].asString()))
381 {
383 return PFR_PJ_INVALID;
384 }
385 srcPathAsset = u;
386 }
387
388 // Optional issuer
389 AccountID srcIssuerID;
390 if (c.isMember(jss::issuer) &&
391 (c.isMember(jss::mpt_issuance_id) || !c[jss::issuer].isString() ||
392 !toIssuer(srcIssuerID, c[jss::issuer].asString())))
393 {
395 return PFR_PJ_INVALID;
396 }
397
398 if (srcPathAsset.holds<Currency>())
399 {
400 if (srcPathAsset.get<Currency>().isZero())
401 {
402 if (srcIssuerID.isNonZero())
403 {
405 return PFR_PJ_INVALID;
406 }
407 }
408 else if (srcIssuerID.isZero())
409 {
410 srcIssuerID = *raSrcAccount_;
411 }
412 }
413
414 if (saSendMax_)
415 {
416 // If the assets don't match, ignore the source asset.
417 if (srcPathAsset == saSendMax_->asset())
418 {
419 // If neither is the source and they are not equal, then the
420 // source issuer is illegal.
421 if (srcIssuerID != *raSrcAccount_ &&
422 saSendMax_->getIssuer() != *raSrcAccount_ &&
423 srcIssuerID != saSendMax_->getIssuer())
424 {
426 return PFR_PJ_INVALID;
427 }
428
429 // If both are the source, use the source.
430 // Otherwise, use the one that's not the source.
431 srcPathAsset.visit(
432 [&](Currency const& currency) {
433 if (srcIssuerID != *raSrcAccount_)
434 {
435 sciSourceAssets_.insert(Issue{currency, srcIssuerID});
436 }
437 else if (saSendMax_->getIssuer() != *raSrcAccount_)
438 {
439 sciSourceAssets_.insert(Issue{currency, saSendMax_->getIssuer()});
440 }
441 {
442 sciSourceAssets_.insert(Issue{currency, *raSrcAccount_});
443 }
444 },
445 [&](MPTID const& mpt) { sciSourceAssets_.insert(mpt); });
446 }
447 }
448 else
449 {
450 srcPathAsset.visit(
451 [&](Currency const& currency) {
452 sciSourceAssets_.insert(Issue{currency, srcIssuerID});
453 },
454 [&](MPTID const& mpt) { sciSourceAssets_.insert(MPTIssue{mpt}); });
455 }
456 }
457 }
458
459 if (jvParams.isMember(jss::id))
460 jvId_ = jvParams[jss::id];
461
462 if (jvParams.isMember(jss::domain))
463 {
464 uint256 num;
465 if (!jvParams[jss::domain].isString() || !num.parseHex(jvParams[jss::domain].asString()))
466 {
468 return PFR_PJ_INVALID;
469 }
470
471 domain_ = num;
472 }
473
474 return PFR_PJ_NOCHANGE;
475}
476
479{
480 JLOG(journal_.debug()) << iIdentifier_ << " closed";
481 std::scoped_lock const sl(lock_);
482 jvStatus_[jss::closed] = true;
483 return jvStatus_;
484}
485
488{
489 std::scoped_lock const sl(lock_);
490 jvStatus_[jss::status] = jss::success;
491 return jvStatus_;
492}
493
494void
496{
497 JLOG(journal_.info()) << iIdentifier_ << " aborting early";
498}
499
502 std::shared_ptr<AssetCache> const& cache,
504 PathAsset const& currency,
505 STAmount const& dstAmount,
506 int const level,
507 std::function<bool(void)> const& continueCallback)
508{
509 auto i = currencyMap.find(currency);
510 if (i != currencyMap.end())
511 return i->second;
512 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
513 auto pathfinder = std::make_unique<Pathfinder>(
514 cache,
517 currency,
518 std::nullopt,
519 dstAmount,
521 domain_,
522 app_);
523 // NOLINTEND(bugprone-unchecked-optional-access)
524 if (pathfinder->findPaths(level, continueCallback))
525 {
526 pathfinder->computePathRanks(kMaxPaths, continueCallback);
527 }
528 else
529 {
530 pathfinder.reset(); // It's a bad request - clear it.
531 }
532 return currencyMap[currency] = std::move(pathfinder);
533}
534
535bool
537 std::shared_ptr<AssetCache> const& cache,
538 int const level,
539 json::Value& jvArray,
540 std::function<bool(void)> const& continueCallback)
541{
542 auto sourceAssets = sciSourceAssets_;
543 if (sourceAssets.empty() && saSendMax_)
544 {
545 sourceAssets.insert(saSendMax_->asset());
546 }
547 if (sourceAssets.empty())
548 {
549 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
550 auto assets = accountSourceAssets(*raSrcAccount_, cache, true);
551 bool const sameAccount = *raSrcAccount_ == *raDstAccount_;
552 // NOLINTEND(bugprone-unchecked-optional-access)
553 for (auto const& asset : assets)
554 {
555 if (!std::visit(
556 [&]<typename TAsset>(TAsset const& a) {
557 if (!sameAccount || a != saDstAmount_.asset())
558 {
559 if (sourceAssets.size() >= RPC::Tuning::kMaxAutoSrcCur)
560 return false;
562 {
563 sourceAssets.insert(
564 Issue{a, a.isZero() ? xrpAccount() : *raSrcAccount_});
565 }
566 else
567 {
568 sourceAssets.insert(MPTIssue{a});
569 }
570 }
571 return true;
572 },
573 asset.value()))
574 {
575 return false;
576 }
577 }
578 }
579
580 auto const dstAmount = convertAmount(saDstAmount_, convertAll_);
582 for (auto const& asset : sourceAssets)
583 {
584 if (continueCallback && !continueCallback())
585 break;
586 JLOG(journal_.debug()) << iIdentifier_
587 << " Trying to find paths: " << STAmount(asset, 1).getFullText();
588
589 auto& pathfinder =
590 getPathFinder(cache, currencyMap, PathAsset(asset), dstAmount, level, continueCallback);
591 if (!pathfinder)
592 {
593 JLOG(journal_.debug()) << iIdentifier_ << " No paths found";
594 continue;
595 }
596
597 STPath fullLiquidityPath;
598 auto ps = pathfinder->getBestPaths(
599 kMaxPaths, fullLiquidityPath, context_[asset], asset.getIssuer(), continueCallback);
600 context_[asset] = ps;
601
602 auto const& sourceAccount = [&] {
603 if (!isXRP(asset.getIssuer()))
604 return asset.getIssuer();
605
606 if (isXRP(asset))
607 return xrpAccount();
608
609 return *raSrcAccount_;
610 }();
611
612 STAmount const saMaxAmount = [&]() {
613 if (saSendMax_)
614 return *saSendMax_;
615 return asset.visit(
616 [&](Issue const& issue) {
617 return STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true);
618 },
619 [](MPTIssue const& issue) { return STAmount(issue, 1u, 0, true); });
620 }();
621
622 JLOG(journal_.debug()) << iIdentifier_ << " Paths found, calling rippleCalc";
623
625 if (convertAll_)
626 rcInput.partialPaymentAllowed = true;
627 auto sandbox = std::make_unique<PaymentSandbox>(&*cache->getLedger(), TapNone);
629 *sandbox,
630 saMaxAmount, // --> Amount to send is unlimited
631 // to get an estimate.
632 dstAmount, // --> Amount to deliver.
633 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
634 *raDstAccount_, // --> Account to deliver to.
635 *raSrcAccount_, // --> Account sending from.
636 // NOLINTEND(bugprone-unchecked-optional-access)
637 ps, // --> Path set.
638 domain_, // --> Domain.
639 app_,
640 &rcInput);
641
642 if (!convertAll_ && !fullLiquidityPath.empty() &&
643 (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL))
644 {
645 JLOG(journal_.debug()) << iIdentifier_ << " Trying with an extra path element";
646
647 ps.pushBack(fullLiquidityPath);
648 sandbox = std::make_unique<PaymentSandbox>(&*cache->getLedger(), TapNone);
650 *sandbox,
651 saMaxAmount, // --> Amount to send is unlimited
652 // to get an estimate.
653 dstAmount, // --> Amount to deliver.
654 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
655 *raDstAccount_, // --> Account to deliver to.
656 *raSrcAccount_, // --> Account sending from.
657 // NOLINTEND(bugprone-unchecked-optional-access)
658 ps, // --> Path set.
659 domain_, // --> Domain.
660 app_);
661
662 if (!isTesSuccess(rc.result()))
663 {
664 JLOG(journal_.warn())
665 << iIdentifier_ << " Failed with covering path " << transHuman(rc.result());
666 }
667 else
668 {
669 JLOG(journal_.debug())
670 << iIdentifier_ << " Extra path element gives " << transHuman(rc.result());
671 }
672 }
673
674 if (rc.result() == tesSUCCESS)
675 {
677 if (rc.actualAmountIn.holds<Issue>())
678 rc.actualAmountIn.get<Issue>().account = sourceAccount;
679 jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(JsonOptions::Values::None);
680 jvEntry[jss::paths_computed] = ps.getJson(JsonOptions::Values::None);
681
682 if (convertAll_)
683 {
684 jvEntry[jss::destination_amount] =
685 rc.actualAmountOut.getJson(JsonOptions::Values::None);
686 }
687
688 if (hasCompletion())
689 {
690 // Old ripple_path_find API requires this
691 jvEntry[jss::paths_canonical] = json::ValueType::Array;
692 }
693
694 jvArray.append(jvEntry);
695 }
696 else
697 {
698 JLOG(journal_.debug())
699 << iIdentifier_ << " rippleCalc returns " << transHuman(rc.result());
700 }
701 }
702
703 /* The resource fee is based on the number of source currencies used.
704 The minimum cost is 50 and the maximum is 400. The cost increases
705 after four source currencies, 50 - (4 * 4) = 34.
706 */
707 int const size = sourceAssets.size();
708 consumer_.charge({std::clamp((size * size) + 34, 50, 400), "path update"});
709 return true;
710}
711
714 std::shared_ptr<AssetCache> const& cache,
715 bool fast,
716 std::function<bool(void)> const& continueCallback)
717{
718 using namespace std::chrono;
719 JLOG(journal_.debug()) << iIdentifier_ << " update " << (fast ? "fast" : "normal");
720
721 {
722 std::scoped_lock const sl(lock_);
723
724 if (!isValid(cache))
725 return jvStatus_;
726 }
727
729
730 if (hasCompletion())
731 {
732 // Old ripple_path_find API gives destination_currencies
733 auto& destAssets = (newStatus[jss::destination_currencies] = json::ValueType::Array);
734 // NOLINTNEXTLINE(bugprone-unchecked-optional-access) isValid() ensures both are set
735 auto const assets = accountDestAssets(*raDstAccount_, cache, true);
736 for (auto const& asset : assets)
737 destAssets.append(to_string(asset));
738 }
739
740 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
741 newStatus[jss::source_account] = toBase58(*raSrcAccount_);
742 newStatus[jss::destination_account] = toBase58(*raDstAccount_);
743 // NOLINTEND(bugprone-unchecked-optional-access)
744 newStatus[jss::destination_amount] = saDstAmount_.getJson(JsonOptions::Values::None);
745 newStatus[jss::full_reply] = !fast;
746
747 if (jvId_)
748 newStatus[jss::id] = jvId_;
749
750 bool const loaded = app_.getFeeTrack().isLoadedLocal();
751
752 if (iLevel_ == 0)
753 {
754 // first pass
755 if (loaded || fast)
756 {
757 iLevel_ = app_.config().pathSearchFast;
758 }
759 else
760 {
761 iLevel_ = app_.config().pathSearch;
762 }
763 }
764 else if ((iLevel_ == app_.config().pathSearchFast) && !fast)
765 {
766 // leaving fast pathfinding
767 iLevel_ = app_.config().pathSearch;
768 if (loaded && (iLevel_ > app_.config().pathSearchFast))
769 --iLevel_;
770 }
771 else if (bLastSuccess_)
772 {
773 // decrement, if possible
774 if (iLevel_ > app_.config().pathSearch ||
775 (loaded && (iLevel_ > app_.config().pathSearchFast)))
776 --iLevel_;
777 }
778 else
779 {
780 // adjust as needed
781 if (!loaded && (iLevel_ < app_.config().pathSearchMax))
782 ++iLevel_;
783 if (loaded && (iLevel_ > app_.config().pathSearchFast))
784 --iLevel_;
785 }
786
787 JLOG(journal_.debug()) << iIdentifier_ << " processing at level " << iLevel_;
788
790 if (findPaths(cache, iLevel_, jvArray, continueCallback))
791 {
792 bLastSuccess_ = jvArray.size() != 0;
793 newStatus[jss::alternatives] = std::move(jvArray);
794 }
795 else
796 {
797 bLastSuccess_ = false;
798 newStatus = rpcError(RpcInternal);
799 }
800
801 if (fast && quickReply_ == steady_clock::time_point{})
802 {
805 }
806 else if (!fast && fullReply_ == steady_clock::time_point{})
807 {
810 }
811
812 {
813 std::scoped_lock const sl(lock_);
814 jvStatus_ = newStatus;
815 }
816
817 JLOG(journal_.debug()) << iIdentifier_ << " update finished " << (fast ? "fast" : "normal");
818 return newStatus;
819}
820
823{
824 return wpSubscriber_.lock();
825}
826
827} // namespace xrpl
T clamp(T... args)
A generic endpoint for log messages.
Definition Journal.h:38
Represents a JSON value.
Definition json_value.h:130
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.
bool isZero() const
Definition base_uint.h:544
bool isNonZero() const
Definition base_uint.h:549
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:507
std::shared_ptr< InfoSub > pointer
Definition InfoSub.h:47
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition PathAsset.h:43
constexpr bool holds() const
Definition PathAsset.h:69
T const & get() const
json::Value jvId_
PathRequest(Application &app, std::shared_ptr< InfoSub > const &subscriber, int id, PathRequestManager &, beast::Journal journal)
LedgerIndex lastIndex_
std::recursive_mutex lock_
void doAborting() const
Resource::Consumer & consumer_
std::chrono::steady_clock::time_point quickReply_
int parseJson(json::Value const &)
static unsigned int const kMaxPaths
int const iIdentifier_
bool isValid(std::shared_ptr< AssetCache > const &crCache)
PathRequestManager & owner_
json::Value jvStatus_
std::chrono::steady_clock::time_point const created_
bool needsUpdate(bool newOnly, LedgerIndex index)
STAmount saDstAmount_
json::Value doClose() override
std::recursive_mutex indexLock_
std::weak_ptr< InfoSub > wpSubscriber_
InfoSub::pointer getSubscriber() const
json::Value doStatus(json::Value const &) override
json::Value doUpdate(std::shared_ptr< AssetCache > const &, bool fast, std::function< bool(void)> const &continueCallback={})
std::optional< STAmount > saSendMax_
beast::Journal journal_
Application & app_
~PathRequest() override
std::chrono::steady_clock::time_point fullReply_
std::set< Asset > sciSourceAssets_
std::optional< AccountID > raDstAccount_
std::optional< uint256 > domain_
std::function< void(void)> fCompletion_
std::unique_ptr< Pathfinder > const & getPathFinder(std::shared_ptr< AssetCache > const &, hash_map< PathAsset, std::unique_ptr< Pathfinder > > &, PathAsset const &, STAmount const &, int const, std::function< bool(void)> const &)
std::pair< bool, json::Value > doCreate(std::shared_ptr< AssetCache > const &, json::Value const &)
bool findPaths(std::shared_ptr< AssetCache > const &, int const, json::Value &, std::function< bool(void)> const &)
Finds and sets a PathSet in the JSON argument.
std::optional< AccountID > raSrcAccount_
std::map< Asset, STPathSet > context_
An endpoint that consumes resources.
Definition Consumer.h:15
std::string getFullText() const override
Definition STAmount.cpp:636
bool empty() const
Definition STPathSet.h:430
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, ServiceRegistry &registry, Input const *const pInputs=nullptr)
T count(T... args)
T duration_cast(T... args)
T is_same_v
T make_unique(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:5
@ Array
array value (ordered list)
Definition json_value.h:25
@ Object
object value (collection of name/value pairs).
Definition json_value.h:26
STL namespace.
static constexpr int kMaxAutoSrcCur
Maximum number of auto source currencies in a path find request.
static constexpr int kMaxSrcCur
Maximum number of source currencies allowed 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:186
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_LINE
Definition TER.h:211
BaseUInt< 192 > uint192
Definition base_uint.h:563
@ RpcDstAmtMalformed
Definition ErrorCodes.h:88
@ RpcInternal
Definition ErrorCodes.h:112
@ RpcSrcActMalformed
Definition ErrorCodes.h:102
@ RpcActNotFound
Definition ErrorCodes.h:52
@ RpcDstActMalformed
Definition ErrorCodes.h:85
@ RpcDomainMalformed
Definition ErrorCodes.h:140
@ RpcSrcActMissing
Definition ErrorCodes.h:103
@ RpcDstActMissing
Definition ErrorCodes.h:86
@ RpcSrcCurMalformed
Definition ErrorCodes.h:106
@ RpcDstAmtMissing
Definition ErrorCodes.h:89
@ RpcSendmaxMalformed
Definition ErrorCodes.h:101
@ RpcSrcIsrMalformed
Definition ErrorCodes.h:107
@ RpcSrcActNotFound
Definition ErrorCodes.h:104
STAmount convertAmount(STAmount const &amt, bool all)
bool isXRP(AccountID const &c)
Definition AccountID.h:70
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:259
bool validJSONAsset(json::Value const &jv)
Definition Asset.cpp:51
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:93
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:36
std::string transHuman(TER code)
Definition TER.cpp:256
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:65
hash_set< PathAsset > accountDestAssets(AccountID const &account, std::shared_ptr< AssetCache > const &lrCache, bool includeXRP)
bool validAsset(Asset const &asset)
Definition Asset.h:320
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
hash_set< PathAsset > accountSourceAssets(AccountID const &account, std::shared_ptr< AssetCache > const &lrCache, bool includeXRP)
bool amountFromJsonNoThrow(STAmount &result, json::Value const &jvSource)
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:44
std::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
@ TapNone
Definition ApplyView.h:13
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecPATH_PARTIAL
Definition TER.h:280
static std::string const & systemCurrencyCode()
bool toIssuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
BaseUInt< 256 > uint256
Definition base_uint.h:562
@ tesSUCCESS
Definition TER.h:240
T size(T... args)
T to_string(T... args)
T visit(T... args)