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