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
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
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()) ||
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 auto const status = srcPathAsset.visit(
420 [&](Currency const& currency) {
421 // If neither is the source and they are not equal,
422 // then the source issuer is illegal. srcIssuerID
423 // comes from the optional IOU source_currencies
424 // issuer field, so this reconciliation is IOU-only.
425 if (srcIssuerID != *raSrcAccount_ &&
426 saSendMax_->getIssuer() != *raSrcAccount_ &&
427 srcIssuerID != saSendMax_->getIssuer())
428 {
430 return PFR_PJ_INVALID;
431 }
432
433 // If both are the source, use the source.
434 // Otherwise, use the one that's not the source.
435 if (srcIssuerID != *raSrcAccount_)
436 {
437 sciSourceAssets_.insert(Issue{currency, srcIssuerID});
438 }
439 else if (saSendMax_->getIssuer() != *raSrcAccount_)
440 {
441 sciSourceAssets_.insert(Issue{currency, saSendMax_->getIssuer()});
442 }
443 else
444 {
445 sciSourceAssets_.insert(Issue{currency, *raSrcAccount_});
446 }
447 return PFR_PJ_NOCHANGE;
448 },
449 [&](MPTID const& mpt) {
450 sciSourceAssets_.insert(mpt);
451 return PFR_PJ_NOCHANGE;
452 });
453 if (status == PFR_PJ_INVALID)
454 return status;
455 }
456 }
457 else
458 {
459 srcPathAsset.visit(
460 [&](Currency const& currency) {
461 sciSourceAssets_.insert(Issue{currency, srcIssuerID});
462 },
463 [&](MPTID const& mpt) { sciSourceAssets_.insert(MPTIssue{mpt}); });
464 }
465 }
466 }
467
468 if (jvParams.isMember(jss::id))
469 jvId_ = jvParams[jss::id];
470
471 if (jvParams.isMember(jss::domain))
472 {
473 UInt256 num;
474 if (!jvParams[jss::domain].isString() || !num.parseHex(jvParams[jss::domain].asString()))
475 {
477 return PFR_PJ_INVALID;
478 }
479
480 domain_ = num;
481 }
482
483 return PFR_PJ_NOCHANGE;
484}
485
488{
489 JLOG(journal_.debug()) << iIdentifier_ << " closed";
490 std::scoped_lock const sl(lock_);
491 jvStatus_[jss::closed] = true;
492 return jvStatus_;
493}
494
497{
498 std::scoped_lock const sl(lock_);
499 jvStatus_[jss::status] = jss::success;
500 return jvStatus_;
501}
502
503void
505{
506 JLOG(journal_.info()) << iIdentifier_ << " aborting early";
507}
508
511 std::shared_ptr<AssetCache> const& cache,
513 PathAsset const& currency,
514 STAmount const& dstAmount,
515 int const level,
516 std::function<bool(void)> const& continueCallback)
517{
518 auto i = currencyMap.find(currency);
519 if (i != currencyMap.end())
520 return i->second;
521 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
522 auto pathfinder = std::make_unique<Pathfinder>(
523 cache,
526 currency,
527 std::nullopt,
528 dstAmount,
530 domain_,
531 app_);
532 // NOLINTEND(bugprone-unchecked-optional-access)
533 if (pathfinder->findPaths(level, continueCallback))
534 {
535 pathfinder->computePathRanks(kMaxPaths, continueCallback);
536 }
537 else
538 {
539 pathfinder.reset(); // It's a bad request - clear it.
540 }
541 return currencyMap[currency] = std::move(pathfinder);
542}
543
544bool
546 std::shared_ptr<AssetCache> const& cache,
547 int const level,
548 json::Value& jvArray,
549 std::function<bool(void)> const& continueCallback)
550{
551 auto sourceAssets = sciSourceAssets_;
552 if (sourceAssets.empty() && saSendMax_)
553 {
554 sourceAssets.insert(saSendMax_->asset());
555 }
556 if (sourceAssets.empty())
557 {
558 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
559 auto assets = accountSourceAssets(*raSrcAccount_, cache, true);
560 bool const sameAccount = *raSrcAccount_ == *raDstAccount_;
561 // NOLINTEND(bugprone-unchecked-optional-access)
562 for (auto const& asset : assets)
563 {
564 if (!std::visit(
565 [&]<typename TAsset>(TAsset const& a) {
566 if (!sameAccount || a != saDstAmount_.asset())
567 {
568 if (sourceAssets.size() >= rpc::tuning::kMaxAutoSrcCur)
569 return false;
571 {
572 sourceAssets.insert(
573 Issue{a, a.isZero() ? xrpAccount() : *raSrcAccount_});
574 }
575 else
576 {
577 sourceAssets.insert(MPTIssue{a});
578 }
579 }
580 return true;
581 },
582 asset.value()))
583 {
584 return false;
585 }
586 }
587 }
588
589 auto const dstAmount = convertAmount(saDstAmount_, convertAll_);
591 for (auto const& asset : sourceAssets)
592 {
593 if (continueCallback && !continueCallback())
594 break;
595 JLOG(journal_.debug()) << iIdentifier_
596 << " Trying to find paths: " << STAmount(asset, 1).getFullText();
597
598 auto& pathfinder =
599 getPathFinder(cache, currencyMap, PathAsset(asset), dstAmount, level, continueCallback);
600 if (!pathfinder)
601 {
602 JLOG(journal_.debug()) << iIdentifier_ << " No paths found";
603 continue;
604 }
605
606 STPath fullLiquidityPath;
607 auto ps = pathfinder->getBestPaths(
608 kMaxPaths, fullLiquidityPath, context_[asset], asset.getIssuer(), continueCallback);
609 context_[asset] = ps;
610
611 auto const& sourceAccount = [&] {
612 if (!isXRP(asset.getIssuer()))
613 return asset.getIssuer();
614
615 if (isXRP(asset))
616 return xrpAccount();
617
618 return *raSrcAccount_;
619 }();
620
621 STAmount const saMaxAmount = [&]() {
622 if (saSendMax_)
623 return *saSendMax_;
624 return asset.visit(
625 [&](Issue const& issue) {
626 return STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true);
627 },
628 [](MPTIssue const& issue) { return STAmount(issue, 1u, 0, true); });
629 }();
630
631 JLOG(journal_.debug()) << iIdentifier_ << " Paths found, calling rippleCalc";
632
634 if (convertAll_)
635 rcInput.partialPaymentAllowed = true;
636 auto sandbox = std::make_unique<PaymentSandbox>(&*cache->getLedger(), TapNone);
638 *sandbox,
639 saMaxAmount, // --> Amount to send is unlimited
640 // to get an estimate.
641 dstAmount, // --> Amount to deliver.
642 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
643 *raDstAccount_, // --> Account to deliver to.
644 *raSrcAccount_, // --> Account sending from.
645 // NOLINTEND(bugprone-unchecked-optional-access)
646 ps, // --> Path set.
647 domain_, // --> Domain.
648 app_,
649 &rcInput);
650
651 if (!convertAll_ && !fullLiquidityPath.empty() &&
652 (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL))
653 {
654 JLOG(journal_.debug()) << iIdentifier_ << " Trying with an extra path element";
655
656 ps.pushBack(fullLiquidityPath);
657 sandbox = std::make_unique<PaymentSandbox>(&*cache->getLedger(), TapNone);
659 *sandbox,
660 saMaxAmount, // --> Amount to send is unlimited
661 // to get an estimate.
662 dstAmount, // --> Amount to deliver.
663 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
664 *raDstAccount_, // --> Account to deliver to.
665 *raSrcAccount_, // --> Account sending from.
666 // NOLINTEND(bugprone-unchecked-optional-access)
667 ps, // --> Path set.
668 domain_, // --> Domain.
669 app_);
670
671 if (!isTesSuccess(rc.result()))
672 {
673 JLOG(journal_.warn())
674 << iIdentifier_ << " Failed with covering path " << transHuman(rc.result());
675 }
676 else
677 {
678 JLOG(journal_.debug())
679 << iIdentifier_ << " Extra path element gives " << transHuman(rc.result());
680 }
681 }
682
683 if (rc.result() == tesSUCCESS)
684 {
686 if (rc.actualAmountIn.holds<Issue>())
687 rc.actualAmountIn.get<Issue>().account = sourceAccount;
688 jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(JsonOptions::Values::None);
689 jvEntry[jss::paths_computed] = ps.getJson(JsonOptions::Values::None);
690
691 if (convertAll_)
692 {
693 jvEntry[jss::destination_amount] =
694 rc.actualAmountOut.getJson(JsonOptions::Values::None);
695 }
696
697 if (hasCompletion())
698 {
699 // Old ripple_path_find API requires this
700 jvEntry[jss::paths_canonical] = json::ValueType::Array;
701 }
702
703 jvArray.append(jvEntry);
704 }
705 else
706 {
707 JLOG(journal_.debug())
708 << iIdentifier_ << " rippleCalc returns " << transHuman(rc.result());
709 }
710 }
711
712 /* The resource fee is based on the number of source currencies used.
713 The minimum cost is 50 and the maximum is 400. The cost increases
714 after four source currencies, 50 - (4 * 4) = 34.
715 */
716 int const size = sourceAssets.size();
717 consumer_.charge({std::clamp((size * size) + 34, 50, 400), "path update"});
718 return true;
719}
720
723 std::shared_ptr<AssetCache> const& cache,
724 bool fast,
725 std::function<bool(void)> const& continueCallback)
726{
727 using namespace std::chrono;
728 JLOG(journal_.debug()) << iIdentifier_ << " update " << (fast ? "fast" : "normal");
729
730 {
731 std::scoped_lock const sl(lock_);
732
733 if (!isValid(cache))
734 return jvStatus_;
735 }
736
738
739 if (hasCompletion())
740 {
741 // Old ripple_path_find API gives destination_currencies
742 auto& destAssets = (newStatus[jss::destination_currencies] = json::ValueType::Array);
743 // NOLINTNEXTLINE(bugprone-unchecked-optional-access) isValid() ensures both are set
744 auto const assets = accountDestAssets(*raDstAccount_, cache, true);
745 for (auto const& asset : assets)
746 destAssets.append(to_string(asset));
747 }
748
749 // NOLINTBEGIN(bugprone-unchecked-optional-access) isValid() ensures both are set
750 newStatus[jss::source_account] = toBase58(*raSrcAccount_);
751 newStatus[jss::destination_account] = toBase58(*raDstAccount_);
752 // NOLINTEND(bugprone-unchecked-optional-access)
753 newStatus[jss::destination_amount] = saDstAmount_.getJson(JsonOptions::Values::None);
754 newStatus[jss::full_reply] = !fast;
755
756 if (jvId_)
757 newStatus[jss::id] = jvId_;
758
759 bool const loaded = app_.getFeeTrack().isLoadedLocal();
760
761 if (iLevel_ == 0)
762 {
763 // first pass
764 if (loaded || fast)
765 {
766 iLevel_ = app_.config().pathSearchFast;
767 }
768 else
769 {
770 iLevel_ = app_.config().pathSearch;
771 }
772 }
773 else if ((iLevel_ == app_.config().pathSearchFast) && !fast)
774 {
775 // leaving fast pathfinding
776 iLevel_ = app_.config().pathSearch;
777 if (loaded && (iLevel_ > app_.config().pathSearchFast))
778 --iLevel_;
779 }
780 else if (bLastSuccess_)
781 {
782 // decrement, if possible
783 if (iLevel_ > app_.config().pathSearch ||
784 (loaded && (iLevel_ > app_.config().pathSearchFast)))
785 --iLevel_;
786 }
787 else
788 {
789 // adjust as needed
790 if (!loaded && (iLevel_ < app_.config().pathSearchMax))
791 ++iLevel_;
792 if (loaded && (iLevel_ > app_.config().pathSearchFast))
793 --iLevel_;
794 }
795
796 JLOG(journal_.debug()) << iIdentifier_ << " processing at level " << iLevel_;
797
799 if (findPaths(cache, iLevel_, jvArray, continueCallback))
800 {
801 bLastSuccess_ = jvArray.size() != 0;
802 newStatus[jss::alternatives] = std::move(jvArray);
803 }
804 else
805 {
806 bLastSuccess_ = false;
807 newStatus = rpcError(RpcInternal);
808 }
809
810 if (fast && quickReply_ == steady_clock::time_point{})
811 {
814 }
815 else if (!fast && fullReply_ == steady_clock::time_point{})
816 {
819 }
820
821 {
822 std::scoped_lock const sl(lock_);
823 jvStatus_ = newStatus;
824 }
825
826 JLOG(journal_.debug()) << iIdentifier_ << " update finished " << (fast ? "fast" : "normal");
827 return newStatus;
828}
829
832{
833 return wpSubscriber_.lock();
834}
835
836} // namespace xrpl
T clamp(T... args)
A generic endpoint for log messages.
Definition Journal.h:44
Represents a JSON value.
Definition json_value.h:117
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:562
bool isNonZero() const
Definition base_uint.h:567
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:525
std::shared_ptr< InfoSub > pointer
Definition InfoSub.h:92
A currency issued by an account.
Definition Issue.h:18
Currency currency
Definition Issue.h:20
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition PathAsset.h:52
constexpr bool holds() const
Definition PathAsset.h:78
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
std::chrono::steady_clock::time_point quickReply_
int parseJson(json::Value const &)
static unsigned int const kMaxPaths
int const iIdentifier_
std::unique_ptr< Pathfinder > const & getPathFinder(std::shared_ptr< AssetCache > const &, HashMap< PathAsset, std::unique_ptr< Pathfinder > > &, PathAsset const &, STAmount const &, int const, std::function< bool(void)> const &)
bool isValid(std::shared_ptr< AssetCache > const &crCache)
PathRequestManager & owner_
json::Value jvStatus_
std::optional< UInt256 > domain_
std::chrono::steady_clock::time_point const created_
bool needsUpdate(bool newOnly, LedgerIndex index)
STAmount saDstAmount_
resource::Consumer & consumer_
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::function< void(void)> fCompletion_
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_
std::string getFullText() const override
Definition STAmount.cpp:637
bool empty() const
Definition STPathSet.h:532
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)
An endpoint that consumes resources.
Definition Consumer.h:20
T count(T... args)
T duration_cast(T... args)
T is_same_v
T make_unique(T... args)
constexpr Zero kZero
Definition Zero.h:30
JSON (JavaScript Object Notation).
Definition json_errors.h:5
@ Array
array value (ordered list)
Definition json_value.h:28
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
STL namespace.
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:220
static constexpr int kMaxSrcCur
Maximum number of source currencies allowed in a path find request.
static constexpr int kMaxAutoSrcCur
Maximum number of auto source currencies in a path find request.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_LINE
Definition TER.h:220
@ RpcDstAmtMalformed
Definition ErrorCodes.h:89
@ RpcInternal
Definition ErrorCodes.h:113
@ RpcSrcActMalformed
Definition ErrorCodes.h:103
@ RpcActNotFound
Definition ErrorCodes.h:53
@ RpcDstActMalformed
Definition ErrorCodes.h:86
@ RpcDomainMalformed
Definition ErrorCodes.h:141
@ RpcSrcActMissing
Definition ErrorCodes.h:104
@ RpcDstActMissing
Definition ErrorCodes.h:87
@ RpcSrcCurMalformed
Definition ErrorCodes.h:107
@ RpcDstAmtMissing
Definition ErrorCodes.h:90
@ RpcSendmaxMalformed
Definition ErrorCodes.h:102
@ RpcSrcIsrMalformed
Definition ErrorCodes.h:108
@ RpcSrcActNotFound
Definition ErrorCodes.h:105
STAmount convertAmount(STAmount const &amt, bool all)
bool isXRP(AccountID const &c)
Definition AccountID.h:84
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:382
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:95
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:42
std::string transHuman(TER code)
Definition TER.cpp:266
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:65
bool validAsset(Asset const &asset)
Definition Asset.h:331
BaseUInt< 192 > UInt192
Definition base_uint.h:581
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
HashSet< PathAsset > accountDestAssets(AccountID const &account, std::shared_ptr< AssetCache > const &lrCache, bool includeXRP)
json::Value rpcError(ErrorCodeI iError)
Definition RPCErr.cpp:13
BaseUInt< 256 > UInt256
Definition base_uint.h:580
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:54
HashSet< PathAsset > accountSourceAssets(AccountID const &account, std::shared_ptr< AssetCache > const &lrCache, bool includeXRP)
@ TapNone
Definition ApplyView.h:28
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecPATH_PARTIAL
Definition TER.h:290
static std::string const & systemCurrencyCode()
std::unordered_map< Key, Value, Hash, Pred, Allocator > HashMap
bool toIssuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
@ tesSUCCESS
Definition TER.h:250
T size(T... args)
T to_string(T... args)
T visit(T... args)