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(duration_cast<milliseconds>(quick_reply_ - created_).count());
80 fast += "ms";
81 }
82 if (full_reply_ != steady_clock::time_point{})
83 {
84 full = " full:";
85 full += std::to_string(duration_cast<milliseconds>(full_reply_ - created_).count());
86 full += "ms";
87 }
88 stream << iIdentifier << " complete:" << fast << full
89 << " total:" << duration_cast<milliseconds>(steady_clock::now() - created_).count() << "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{
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{
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);
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
288 (saDstAmount.getCurrency() == badCurrency()) || (!convert_all_ && saDstAmount <= beast::zero))
289 {
291 return PFR_PJ_INVALID;
292 }
293
294 if (jvParams.isMember(jss::send_max))
295 {
296 // Send_max requires destination amount to be -1.
297 if (!convert_all_)
298 {
300 return PFR_PJ_INVALID;
301 }
302
304 if (!amountFromJsonNoThrow(*saSendMax, jvParams[jss::send_max]) ||
305 (saSendMax->getCurrency().isZero() && saSendMax->getIssuer().isNonZero()) ||
306 (saSendMax->getCurrency() == badCurrency()) ||
307 (*saSendMax <= beast::zero && *saSendMax != STAmount(saSendMax->issue(), 1u, 0, true)))
308 {
310 return PFR_PJ_INVALID;
311 }
312 }
313
314 if (jvParams.isMember(jss::source_currencies))
315 {
316 Json::Value const& jvSrcCurrencies = jvParams[jss::source_currencies];
317 if (!jvSrcCurrencies.isArray() || jvSrcCurrencies.size() == 0 ||
318 jvSrcCurrencies.size() > RPC::Tuning::max_src_cur)
319 {
321 return PFR_PJ_INVALID;
322 }
323
324 sciSourceCurrencies.clear();
325
326 for (auto const& c : jvSrcCurrencies)
327 {
328 // Mandatory currency
329 Currency srcCurrencyID;
330 if (!c.isObject() || !c.isMember(jss::currency) || !c[jss::currency].isString() ||
331 !to_currency(srcCurrencyID, c[jss::currency].asString()))
332 {
334 return PFR_PJ_INVALID;
335 }
336
337 // Optional issuer
338 AccountID srcIssuerID;
339 if (c.isMember(jss::issuer) &&
340 (!c[jss::issuer].isString() || !to_issuer(srcIssuerID, c[jss::issuer].asString())))
341 {
343 return PFR_PJ_INVALID;
344 }
345
346 if (srcCurrencyID.isZero())
347 {
348 if (srcIssuerID.isNonZero())
349 {
351 return PFR_PJ_INVALID;
352 }
353 }
354 else if (srcIssuerID.isZero())
355 {
356 srcIssuerID = *raSrcAccount;
357 }
358
359 if (saSendMax)
360 {
361 // If the currencies don't match, ignore the source currency.
362 if (srcCurrencyID == saSendMax->getCurrency())
363 {
364 // If neither is the source and they are not equal, then the
365 // source issuer is illegal.
366 if (srcIssuerID != *raSrcAccount && saSendMax->getIssuer() != *raSrcAccount &&
367 srcIssuerID != saSendMax->getIssuer())
368 {
370 return PFR_PJ_INVALID;
371 }
372
373 // If both are the source, use the source.
374 // Otherwise, use the one that's not the source.
375 if (srcIssuerID != *raSrcAccount)
376 {
377 sciSourceCurrencies.insert({srcCurrencyID, srcIssuerID});
378 }
379 else if (saSendMax->getIssuer() != *raSrcAccount)
380 {
381 sciSourceCurrencies.insert({srcCurrencyID, saSendMax->getIssuer()});
382 }
383 else
384 {
385 sciSourceCurrencies.insert({srcCurrencyID, *raSrcAccount});
386 }
387 }
388 }
389 else
390 {
391 sciSourceCurrencies.insert({srcCurrencyID, srcIssuerID});
392 }
393 }
394 }
395
396 if (jvParams.isMember(jss::id))
397 jvId = jvParams[jss::id];
398
399 if (jvParams.isMember(jss::domain))
400 {
401 uint256 num;
402 if (!jvParams[jss::domain].isString() || !num.parseHex(jvParams[jss::domain].asString()))
403 {
405 return PFR_PJ_INVALID;
406 }
407 else
408 {
409 domain = num;
410 }
411 }
412
413 return PFR_PJ_NOCHANGE;
414}
415
418{
419 JLOG(m_journal.debug()) << iIdentifier << " closed";
421 jvStatus[jss::closed] = true;
422 return jvStatus;
423}
424
427{
429 jvStatus[jss::status] = jss::success;
430 return jvStatus;
431}
432
433void
435{
436 JLOG(m_journal.info()) << iIdentifier << " aborting early";
437}
438
443 Currency const& currency,
444 STAmount const& dst_amount,
445 int const level,
446 std::function<bool(void)> const& continueCallback)
447{
448 auto i = currency_map.find(currency);
449 if (i != currency_map.end())
450 return i->second;
451 auto pathfinder = std::make_unique<Pathfinder>(
452 cache, *raSrcAccount, *raDstAccount, currency, std::nullopt, dst_amount, saSendMax, domain, app_);
453 if (pathfinder->findPaths(level, continueCallback))
454 pathfinder->computePathRanks(max_paths_, continueCallback);
455 else
456 pathfinder.reset(); // It's a bad request - clear it.
457 return currency_map[currency] = std::move(pathfinder);
458}
459
460bool
463 int const level,
464 Json::Value& jvArray,
465 std::function<bool(void)> const& continueCallback)
466{
467 auto sourceCurrencies = sciSourceCurrencies;
468 if (sourceCurrencies.empty() && saSendMax)
469 {
470 sourceCurrencies.insert(saSendMax->issue());
471 }
472 if (sourceCurrencies.empty())
473 {
474 auto currencies = accountSourceCurrencies(*raSrcAccount, cache, true);
475 bool const sameAccount = *raSrcAccount == *raDstAccount;
476 for (auto const& c : currencies)
477 {
478 if (!sameAccount || c != saDstAmount.getCurrency())
479 {
480 if (sourceCurrencies.size() >= RPC::Tuning::max_auto_src_cur)
481 return false;
482 sourceCurrencies.insert({c, c.isZero() ? xrpAccount() : *raSrcAccount});
483 }
484 }
485 }
486
487 auto const dst_amount = convertAmount(saDstAmount, convert_all_);
489 for (auto const& issue : sourceCurrencies)
490 {
491 if (continueCallback && !continueCallback())
492 break;
493 JLOG(m_journal.debug()) << iIdentifier << " Trying to find paths: " << STAmount(issue, 1).getFullText();
494
495 auto& pathfinder = getPathFinder(cache, currency_map, issue.currency, dst_amount, level, continueCallback);
496 if (!pathfinder)
497 {
498 JLOG(m_journal.debug()) << iIdentifier << " No paths found";
499 continue;
500 }
501
502 STPath fullLiquidityPath;
503 auto ps =
504 pathfinder->getBestPaths(max_paths_, fullLiquidityPath, mContext[issue], issue.account, continueCallback);
505 mContext[issue] = ps;
506
507 auto const& sourceAccount = [&] {
508 if (!isXRP(issue.account))
509 return issue.account;
510
511 if (isXRP(issue.currency))
512 return xrpAccount();
513
514 return *raSrcAccount;
515 }();
516
517 STAmount saMaxAmount = saSendMax.value_or(STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true));
518
519 JLOG(m_journal.debug()) << iIdentifier << " Paths found, calling rippleCalc";
520
522 if (convert_all_)
523 rcInput.partialPaymentAllowed = true;
524 auto sandbox = std::make_unique<PaymentSandbox>(&*cache->getLedger(), tapNONE);
526 *sandbox,
527 saMaxAmount, // --> Amount to send is unlimited
528 // to get an estimate.
529 dst_amount, // --> Amount to deliver.
530 *raDstAccount, // --> Account to deliver to.
531 *raSrcAccount, // --> Account sending from.
532 ps, // --> Path set.
533 domain, // --> Domain.
534 app_.logs(),
535 &rcInput);
536
537 if (!convert_all_ && !fullLiquidityPath.empty() &&
538 (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL))
539 {
540 JLOG(m_journal.debug()) << iIdentifier << " Trying with an extra path element";
541
542 ps.push_back(fullLiquidityPath);
543 sandbox = std::make_unique<PaymentSandbox>(&*cache->getLedger(), tapNONE);
545 *sandbox,
546 saMaxAmount, // --> Amount to send is unlimited
547 // to get an estimate.
548 dst_amount, // --> Amount to deliver.
549 *raDstAccount, // --> Account to deliver to.
550 *raSrcAccount, // --> Account sending from.
551 ps, // --> Path set.
552 domain, // --> Domain.
553 app_.logs());
554
555 if (rc.result() != tesSUCCESS)
556 {
557 JLOG(m_journal.warn()) << iIdentifier << " Failed with covering path " << transHuman(rc.result());
558 }
559 else
560 {
561 JLOG(m_journal.debug()) << iIdentifier << " Extra path element gives " << transHuman(rc.result());
562 }
563 }
564
565 if (rc.result() == tesSUCCESS)
566 {
568 rc.actualAmountIn.setIssuer(sourceAccount);
569 jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(JsonOptions::none);
570 jvEntry[jss::paths_computed] = ps.getJson(JsonOptions::none);
571
572 if (convert_all_)
573 jvEntry[jss::destination_amount] = rc.actualAmountOut.getJson(JsonOptions::none);
574
575 if (hasCompletion())
576 {
577 // Old ripple_path_find API requires this
578 jvEntry[jss::paths_canonical] = Json::arrayValue;
579 }
580
581 jvArray.append(jvEntry);
582 }
583 else
584 {
585 JLOG(m_journal.debug()) << iIdentifier << " rippleCalc returns " << transHuman(rc.result());
586 }
587 }
588
589 /* The resource fee is based on the number of source currencies used.
590 The minimum cost is 50 and the maximum is 400. The cost increases
591 after four source currencies, 50 - (4 * 4) = 34.
592 */
593 int const size = sourceCurrencies.size();
594 consumer_.charge({std::clamp(size * size + 34, 50, 400), "path update"});
595 return true;
596}
597
601 bool fast,
602 std::function<bool(void)> const& continueCallback)
603{
604 using namespace std::chrono;
605 JLOG(m_journal.debug()) << iIdentifier << " update " << (fast ? "fast" : "normal");
606
607 {
609
610 if (!isValid(cache))
611 return jvStatus;
612 }
613
614 Json::Value newStatus = Json::objectValue;
615
616 if (hasCompletion())
617 {
618 // Old ripple_path_find API gives destination_currencies
619 auto& destCurrencies = (newStatus[jss::destination_currencies] = Json::arrayValue);
620 auto usCurrencies = accountDestCurrencies(*raDstAccount, cache, true);
621 for (auto const& c : usCurrencies)
622 destCurrencies.append(to_string(c));
623 }
624
625 newStatus[jss::source_account] = toBase58(*raSrcAccount);
626 newStatus[jss::destination_account] = toBase58(*raDstAccount);
627 newStatus[jss::destination_amount] = saDstAmount.getJson(JsonOptions::none);
628 newStatus[jss::full_reply] = !fast;
629
630 if (jvId)
631 newStatus[jss::id] = jvId;
632
633 bool loaded = app_.getFeeTrack().isLoadedLocal();
634
635 if (iLevel == 0)
636 {
637 // first pass
638 if (loaded || fast)
640 else
642 }
643 else if ((iLevel == app_.config().PATH_SEARCH_FAST) && !fast)
644 {
645 // leaving fast pathfinding
647 if (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))
648 --iLevel;
649 }
650 else if (bLastSuccess)
651 {
652 // decrement, if possible
653 if (iLevel > app_.config().PATH_SEARCH || (loaded && (iLevel > app_.config().PATH_SEARCH_FAST)))
654 --iLevel;
655 }
656 else
657 {
658 // adjust as needed
659 if (!loaded && (iLevel < app_.config().PATH_SEARCH_MAX))
660 ++iLevel;
661 if (loaded && (iLevel > app_.config().PATH_SEARCH_FAST))
662 --iLevel;
663 }
664
665 JLOG(m_journal.debug()) << iIdentifier << " processing at level " << iLevel;
666
668 if (findPaths(cache, iLevel, jvArray, continueCallback))
669 {
670 bLastSuccess = jvArray.size() != 0;
671 newStatus[jss::alternatives] = std::move(jvArray);
672 }
673 else
674 {
675 bLastSuccess = false;
676 newStatus = rpcError(rpcINTERNAL);
677 }
678
679 if (fast && quick_reply_ == steady_clock::time_point{})
680 {
681 quick_reply_ = steady_clock::now();
682 mOwner.reportFast(duration_cast<milliseconds>(quick_reply_ - created_));
683 }
684 else if (!fast && full_reply_ == steady_clock::time_point{})
685 {
686 full_reply_ = steady_clock::now();
687 mOwner.reportFull(duration_cast<milliseconds>(full_reply_ - created_));
688 }
689
690 {
692 jvStatus = newStatus;
693 }
694
695 JLOG(m_journal.debug()) << iIdentifier << " update finished " << (fast ? "fast" : "normal");
696 return newStatus;
697}
698
701{
702 return wpSubscriber.lock();
703}
704
705} // 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:301
Stream info() const
Definition Journal.h:307
Stream warn() const
Definition Journal.h:313
virtual Config & config()=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:85
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:721
std::string getFullText() const override
Definition STAmount.cpp:629
Issue const & issue() const
Definition STAmount.h:455
Currency const & getCurrency() const
Definition STAmount.h:461
bool native() const noexcept
Definition STAmount.h:417
AccountID const & getIssuer() const
Definition STAmount.h:467
bool empty() const
Definition STPathSet.h:364
virtual LoadFeeTrack & getFeeTrack()=0
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:472
bool isZero() const
Definition base_uint.h:509
bool isNonZero() const
Definition base_uint.h:514
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:160
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:598
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: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:62
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)