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