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 ripple {
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:";
86 full += std::to_string(
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(
143 mInProgress, "ripple::PathRequest::updateComplete : in progress");
144 mInProgress = false;
145
146 if (fCompletion)
147 {
148 fCompletion();
150 }
151}
152
153bool
155{
156 if (!raSrcAccount || !raDstAccount)
157 return false;
158
159 if (!convert_all_ && (saSendMax || saDstAmount <= beast::zero))
160 {
161 // If send max specified, dst amt must be -1.
163 return false;
164 }
165
166 auto const& lrLedger = crCache->getLedger();
167
168 if (!lrLedger->exists(keylet::account(*raSrcAccount)))
169 {
170 // Source account does not exist.
172 return false;
173 }
174
175 auto const sleDest = lrLedger->read(keylet::account(*raDstAccount));
176
177 Json::Value& jvDestCur =
178 (jvStatus[jss::destination_currencies] = Json::arrayValue);
179
180 if (!sleDest)
181 {
183 if (!saDstAmount.native())
184 {
185 // Only XRP can be send to a non-existent account.
187 return false;
188 }
189
190 if (!convert_all_ && saDstAmount < STAmount(lrLedger->fees().reserve))
191 {
192 // Payment must meet reserve.
194 return false;
195 }
196 }
197 else
198 {
199 bool const disallowXRP(sleDest->getFlags() & lsfDisallowXRP);
200
201 auto usDestCurrID =
202 accountDestCurrencies(*raDstAccount, crCache, !disallowXRP);
203
204 for (auto const& currency : usDestCurrID)
205 jvDestCur.append(to_string(currency));
206 jvStatus[jss::destination_tag] =
207 (sleDest->getFlags() & lsfRequireDestTag);
208 }
209
210 jvStatus[jss::ledger_hash] = to_string(lrLedger->info().hash);
211 jvStatus[jss::ledger_index] = lrLedger->seq();
212 return true;
213}
214
215/* If this is a normal path request, we want to run it once "fast" now
216 to give preliminary results.
217
218 If this is a legacy path request, we are only going to run it once,
219 and we can't run it in full now, so we don't want to run it at all.
220
221 If there's an error, we need to be sure to return it to the caller
222 in all cases.
223*/
227 Json::Value const& value)
228{
229 bool valid = false;
230
231 if (parseJson(value) != PFR_PJ_INVALID)
232 {
233 valid = isValid(cache);
234 if (!hasCompletion() && valid)
235 doUpdate(cache, true);
236 }
237
238 if (auto stream = m_journal.debug())
239 {
240 if (valid)
241 {
242 stream << iIdentifier << " valid: " << toBase58(*raSrcAccount);
243 stream << iIdentifier << " deliver: " << saDstAmount.getFullText();
244 }
245 else
246 {
247 stream << iIdentifier << " invalid";
248 }
249 }
250
251 return {valid, jvStatus};
252}
253
254int
256{
257 if (!jvParams.isMember(jss::source_account))
258 {
260 return PFR_PJ_INVALID;
261 }
262
263 if (!jvParams.isMember(jss::destination_account))
264 {
266 return PFR_PJ_INVALID;
267 }
268
269 if (!jvParams.isMember(jss::destination_amount))
270 {
272 return PFR_PJ_INVALID;
273 }
274
276 parseBase58<AccountID>(jvParams[jss::source_account].asString());
277 if (!raSrcAccount)
278 {
280 return PFR_PJ_INVALID;
281 }
282
284 parseBase58<AccountID>(jvParams[jss::destination_account].asString());
285 if (!raDstAccount)
286 {
288 return PFR_PJ_INVALID;
289 }
290
291 if (!amountFromJsonNoThrow(saDstAmount, jvParams[jss::destination_amount]))
292 {
294 return PFR_PJ_INVALID;
295 }
296
298
299 if ((saDstAmount.getCurrency().isZero() &&
302 (!convert_all_ && saDstAmount <= beast::zero))
303 {
305 return PFR_PJ_INVALID;
306 }
307
308 if (jvParams.isMember(jss::send_max))
309 {
310 // Send_max requires destination amount to be -1.
311 if (!convert_all_)
312 {
314 return PFR_PJ_INVALID;
315 }
316
318 if (!amountFromJsonNoThrow(*saSendMax, jvParams[jss::send_max]) ||
319 (saSendMax->getCurrency().isZero() &&
320 saSendMax->getIssuer().isNonZero()) ||
321 (saSendMax->getCurrency() == badCurrency()) ||
322 (*saSendMax <= beast::zero &&
323 *saSendMax != STAmount(saSendMax->issue(), 1u, 0, true)))
324 {
326 return PFR_PJ_INVALID;
327 }
328 }
329
330 if (jvParams.isMember(jss::source_currencies))
331 {
332 Json::Value const& jvSrcCurrencies = jvParams[jss::source_currencies];
333 if (!jvSrcCurrencies.isArray() || jvSrcCurrencies.size() == 0 ||
334 jvSrcCurrencies.size() > RPC::Tuning::max_src_cur)
335 {
337 return PFR_PJ_INVALID;
338 }
339
340 sciSourceCurrencies.clear();
341
342 for (auto const& c : jvSrcCurrencies)
343 {
344 // Mandatory currency
345 Currency srcCurrencyID;
346 if (!c.isObject() || !c.isMember(jss::currency) ||
347 !c[jss::currency].isString() ||
348 !to_currency(srcCurrencyID, c[jss::currency].asString()))
349 {
351 return PFR_PJ_INVALID;
352 }
353
354 // Optional issuer
355 AccountID srcIssuerID;
356 if (c.isMember(jss::issuer) &&
357 (!c[jss::issuer].isString() ||
358 !to_issuer(srcIssuerID, c[jss::issuer].asString())))
359 {
361 return PFR_PJ_INVALID;
362 }
363
364 if (srcCurrencyID.isZero())
365 {
366 if (srcIssuerID.isNonZero())
367 {
369 return PFR_PJ_INVALID;
370 }
371 }
372 else if (srcIssuerID.isZero())
373 {
374 srcIssuerID = *raSrcAccount;
375 }
376
377 if (saSendMax)
378 {
379 // If the currencies don't match, ignore the source currency.
380 if (srcCurrencyID == saSendMax->getCurrency())
381 {
382 // If neither is the source and they are not equal, then the
383 // source issuer is illegal.
384 if (srcIssuerID != *raSrcAccount &&
385 saSendMax->getIssuer() != *raSrcAccount &&
386 srcIssuerID != saSendMax->getIssuer())
387 {
389 return PFR_PJ_INVALID;
390 }
391
392 // If both are the source, use the source.
393 // Otherwise, use the one that's not the source.
394 if (srcIssuerID != *raSrcAccount)
395 {
396 sciSourceCurrencies.insert(
397 {srcCurrencyID, srcIssuerID});
398 }
399 else if (saSendMax->getIssuer() != *raSrcAccount)
400 {
401 sciSourceCurrencies.insert(
402 {srcCurrencyID, saSendMax->getIssuer()});
403 }
404 else
405 {
406 sciSourceCurrencies.insert(
407 {srcCurrencyID, *raSrcAccount});
408 }
409 }
410 }
411 else
412 {
413 sciSourceCurrencies.insert({srcCurrencyID, srcIssuerID});
414 }
415 }
416 }
417
418 if (jvParams.isMember(jss::id))
419 jvId = jvParams[jss::id];
420
421 if (jvParams.isMember(jss::domain))
422 {
423 uint256 num;
424 if (!jvParams[jss::domain].isString() ||
425 !num.parseHex(jvParams[jss::domain].asString()))
426 {
428 return PFR_PJ_INVALID;
429 }
430 else
431 {
432 domain = num;
433 }
434 }
435
436 return PFR_PJ_NOCHANGE;
437}
438
441{
442 JLOG(m_journal.debug()) << iIdentifier << " closed";
444 jvStatus[jss::closed] = true;
445 return jvStatus;
446}
447
450{
452 jvStatus[jss::status] = jss::success;
453 return jvStatus;
454}
455
456void
458{
459 JLOG(m_journal.info()) << iIdentifier << " aborting early";
460}
461
466 Currency const& currency,
467 STAmount const& dst_amount,
468 int const level,
469 std::function<bool(void)> const& continueCallback)
470{
471 auto i = currency_map.find(currency);
472 if (i != currency_map.end())
473 return i->second;
474 auto pathfinder = std::make_unique<Pathfinder>(
475 cache,
478 currency,
480 dst_amount,
481 saSendMax,
482 domain,
483 app_);
484 if (pathfinder->findPaths(level, continueCallback))
485 pathfinder->computePathRanks(max_paths_, continueCallback);
486 else
487 pathfinder.reset(); // It's a bad request - clear it.
488 return currency_map[currency] = std::move(pathfinder);
489}
490
491bool
494 int const level,
495 Json::Value& jvArray,
496 std::function<bool(void)> const& continueCallback)
497{
498 auto sourceCurrencies = sciSourceCurrencies;
499 if (sourceCurrencies.empty() && saSendMax)
500 {
501 sourceCurrencies.insert(saSendMax->issue());
502 }
503 if (sourceCurrencies.empty())
504 {
505 auto currencies = accountSourceCurrencies(*raSrcAccount, cache, true);
506 bool const sameAccount = *raSrcAccount == *raDstAccount;
507 for (auto const& c : currencies)
508 {
509 if (!sameAccount || c != saDstAmount.getCurrency())
510 {
511 if (sourceCurrencies.size() >= RPC::Tuning::max_auto_src_cur)
512 return false;
513 sourceCurrencies.insert(
514 {c, c.isZero() ? xrpAccount() : *raSrcAccount});
515 }
516 }
517 }
518
519 auto const dst_amount = convertAmount(saDstAmount, convert_all_);
521 for (auto const& issue : sourceCurrencies)
522 {
523 if (continueCallback && !continueCallback())
524 break;
525 JLOG(m_journal.debug())
526 << iIdentifier
527 << " Trying to find paths: " << STAmount(issue, 1).getFullText();
528
529 auto& pathfinder = getPathFinder(
530 cache,
531 currency_map,
532 issue.currency,
533 dst_amount,
534 level,
535 continueCallback);
536 if (!pathfinder)
537 {
538 JLOG(m_journal.debug()) << iIdentifier << " No paths found";
539 continue;
540 }
541
542 STPath fullLiquidityPath;
543 auto ps = pathfinder->getBestPaths(
545 fullLiquidityPath,
546 mContext[issue],
547 issue.account,
548 continueCallback);
549 mContext[issue] = ps;
550
551 auto const& sourceAccount = [&] {
552 if (!isXRP(issue.account))
553 return issue.account;
554
555 if (isXRP(issue.currency))
556 return xrpAccount();
557
558 return *raSrcAccount;
559 }();
560
561 STAmount saMaxAmount = saSendMax.value_or(
562 STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true));
563
564 JLOG(m_journal.debug())
565 << iIdentifier << " Paths found, calling rippleCalc";
566
568 if (convert_all_)
569 rcInput.partialPaymentAllowed = true;
570 auto sandbox =
571 std::make_unique<PaymentSandbox>(&*cache->getLedger(), tapNONE);
573 *sandbox,
574 saMaxAmount, // --> Amount to send is unlimited
575 // to get an estimate.
576 dst_amount, // --> Amount to deliver.
577 *raDstAccount, // --> Account to deliver to.
578 *raSrcAccount, // --> Account sending from.
579 ps, // --> Path set.
580 domain, // --> Domain.
581 app_.logs(),
582 &rcInput);
583
584 if (!convert_all_ && !fullLiquidityPath.empty() &&
585 (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL))
586 {
587 JLOG(m_journal.debug())
588 << iIdentifier << " Trying with an extra path element";
589
590 ps.push_back(fullLiquidityPath);
591 sandbox =
592 std::make_unique<PaymentSandbox>(&*cache->getLedger(), tapNONE);
594 *sandbox,
595 saMaxAmount, // --> Amount to send is unlimited
596 // to get an estimate.
597 dst_amount, // --> Amount to deliver.
598 *raDstAccount, // --> Account to deliver to.
599 *raSrcAccount, // --> Account sending from.
600 ps, // --> Path set.
601 domain, // --> Domain.
602 app_.logs());
603
604 if (rc.result() != tesSUCCESS)
605 {
606 JLOG(m_journal.warn())
607 << iIdentifier << " Failed with covering path "
608 << transHuman(rc.result());
609 }
610 else
611 {
612 JLOG(m_journal.debug())
613 << iIdentifier << " Extra path element gives "
614 << transHuman(rc.result());
615 }
616 }
617
618 if (rc.result() == tesSUCCESS)
619 {
621 rc.actualAmountIn.setIssuer(sourceAccount);
622 jvEntry[jss::source_amount] =
623 rc.actualAmountIn.getJson(JsonOptions::none);
624 jvEntry[jss::paths_computed] = ps.getJson(JsonOptions::none);
625
626 if (convert_all_)
627 jvEntry[jss::destination_amount] =
628 rc.actualAmountOut.getJson(JsonOptions::none);
629
630 if (hasCompletion())
631 {
632 // Old ripple_path_find API requires this
633 jvEntry[jss::paths_canonical] = Json::arrayValue;
634 }
635
636 jvArray.append(jvEntry);
637 }
638 else
639 {
640 JLOG(m_journal.debug()) << iIdentifier << " rippleCalc returns "
641 << transHuman(rc.result());
642 }
643 }
644
645 /* The resource fee is based on the number of source currencies used.
646 The minimum cost is 50 and the maximum is 400. The cost increases
647 after four source currencies, 50 - (4 * 4) = 34.
648 */
649 int const size = sourceCurrencies.size();
650 consumer_.charge({std::clamp(size * size + 34, 50, 400), "path update"});
651 return true;
652}
653
657 bool fast,
658 std::function<bool(void)> const& continueCallback)
659{
660 using namespace std::chrono;
661 JLOG(m_journal.debug())
662 << iIdentifier << " update " << (fast ? "fast" : "normal");
663
664 {
666
667 if (!isValid(cache))
668 return jvStatus;
669 }
670
671 Json::Value newStatus = Json::objectValue;
672
673 if (hasCompletion())
674 {
675 // Old ripple_path_find API gives destination_currencies
676 auto& destCurrencies =
677 (newStatus[jss::destination_currencies] = Json::arrayValue);
678 auto usCurrencies = accountDestCurrencies(*raDstAccount, cache, true);
679 for (auto const& c : usCurrencies)
680 destCurrencies.append(to_string(c));
681 }
682
683 newStatus[jss::source_account] = toBase58(*raSrcAccount);
684 newStatus[jss::destination_account] = toBase58(*raDstAccount);
685 newStatus[jss::destination_amount] = saDstAmount.getJson(JsonOptions::none);
686 newStatus[jss::full_reply] = !fast;
687
688 if (jvId)
689 newStatus[jss::id] = jvId;
690
691 bool loaded = app_.getFeeTrack().isLoadedLocal();
692
693 if (iLevel == 0)
694 {
695 // first pass
696 if (loaded || fast)
698 else
700 }
701 else if ((iLevel == app_.config().PATH_SEARCH_FAST) && !fast)
702 {
703 // leaving fast pathfinding
705 if (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))
706 --iLevel;
707 }
708 else if (bLastSuccess)
709 {
710 // decrement, if possible
711 if (iLevel > app_.config().PATH_SEARCH ||
712 (loaded && (iLevel > app_.config().PATH_SEARCH_FAST)))
713 --iLevel;
714 }
715 else
716 {
717 // adjust as needed
718 if (!loaded && (iLevel < app_.config().PATH_SEARCH_MAX))
719 ++iLevel;
720 if (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))
721 --iLevel;
722 }
723
724 JLOG(m_journal.debug()) << iIdentifier << " processing at level " << iLevel;
725
727 if (findPaths(cache, iLevel, jvArray, continueCallback))
728 {
729 bLastSuccess = jvArray.size() != 0;
730 newStatus[jss::alternatives] = std::move(jvArray);
731 }
732 else
733 {
734 bLastSuccess = false;
735 newStatus = rpcError(rpcINTERNAL);
736 }
737
738 if (fast && quick_reply_ == steady_clock::time_point{})
739 {
740 quick_reply_ = steady_clock::now();
741 mOwner.reportFast(duration_cast<milliseconds>(quick_reply_ - created_));
742 }
743 else if (!fast && full_reply_ == steady_clock::time_point{})
744 {
745 full_reply_ = steady_clock::now();
746 mOwner.reportFull(duration_cast<milliseconds>(full_reply_ - created_));
747 }
748
749 {
751 jvStatus = newStatus;
752 }
753
754 JLOG(m_journal.debug())
755 << iIdentifier << " update finished " << (fast ? "fast" : "normal");
756 return newStatus;
757}
758
761{
762 return wpSubscriber.lock();
763}
764
765} // namespace ripple
T clamp(T... args)
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.
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
Definition Config.h:177
int PATH_SEARCH_MAX
Definition Config.h:179
int PATH_SEARCH_FAST
Definition Config.h:178
A currency issued by an account.
Definition Issue.h:14
Currency currency
Definition Issue.h:16
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: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:753
Currency const & getCurrency() const
Definition STAmount.h:483
AccountID const & getIssuer() const
Definition STAmount.h:489
Issue const & issue() const
Definition STAmount.h:477
std::string getFullText() const override
Definition STAmount.cpp:654
bool native() const noexcept
Definition STAmount.h:439
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:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
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:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::string transHuman(TER code)
Definition TER.cpp:254
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
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:71
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:51
@ rpcDST_ACT_MISSING
Definition ErrorCodes.h:85
@ rpcSRC_ACT_NOT_FOUND
Definition ErrorCodes.h:103
@ rpcSENDMAX_MALFORMED
Definition ErrorCodes.h:100
@ rpcDOMAIN_MALFORMED
Definition ErrorCodes.h:139
@ rpcINTERNAL
Definition ErrorCodes.h:111
@ rpcSRC_ACT_MALFORMED
Definition ErrorCodes.h:101
@ rpcSRC_ISR_MALFORMED
Definition ErrorCodes.h:106
@ rpcDST_AMT_MALFORMED
Definition ErrorCodes.h:87
@ rpcDST_ACT_MALFORMED
Definition ErrorCodes.h:84
@ rpcSRC_CUR_MALFORMED
Definition ErrorCodes.h:105
@ rpcDST_AMT_MISSING
Definition ErrorCodes.h:88
@ rpcSRC_ACT_MISSING
Definition ErrorCodes.h:102
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:12
@ tecPATH_PARTIAL
Definition TER.h:264
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
@ tesSUCCESS
Definition TER.h:226
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
@ tapNONE
Definition ApplyView.h:12
@ terNO_LINE
Definition TER.h:200
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:65
STL namespace.
T to_string(T... args)
T value_or(T... args)