rippled
Loading...
Searching...
No Matches
Payment.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/misc/DelegateUtils.h>
21#include <xrpld/app/misc/PermissionedDEXHelpers.h>
22#include <xrpld/app/paths/RippleCalc.h>
23#include <xrpld/app/tx/detail/Payment.h>
24
25#include <xrpl/basics/Log.h>
26#include <xrpl/ledger/CredentialHelpers.h>
27#include <xrpl/ledger/View.h>
28#include <xrpl/protocol/Feature.h>
29#include <xrpl/protocol/Quality.h>
30#include <xrpl/protocol/TxFlags.h>
31#include <xrpl/protocol/jss.h>
32
33namespace ripple {
34
35TxConsequences
37{
38 auto calculateMaxXRPSpend = [](STTx const& tx) -> XRPAmount {
39 STAmount const maxAmount =
40 tx.isFieldPresent(sfSendMax) ? tx[sfSendMax] : tx[sfAmount];
41
42 // If there's no sfSendMax in XRP, and the sfAmount isn't
43 // in XRP, then the transaction does not spend XRP.
44 return maxAmount.native() ? maxAmount.xrp() : beast::zero;
45 };
46
47 return TxConsequences{ctx.tx, calculateMaxXRPSpend(ctx.tx)};
48}
49
52 AccountID const& account,
53 STAmount const& dstAmount,
54 std::optional<STAmount> const& sendMax)
55{
56 if (sendMax)
57 return *sendMax;
58 else if (dstAmount.native() || dstAmount.holds<MPTIssue>())
59 return dstAmount;
60 else
61 return STAmount(
62 Issue{dstAmount.get<Issue>().currency, account},
63 dstAmount.mantissa(),
64 dstAmount.exponent(),
65 dstAmount < beast::zero);
66}
67
68bool
70{
71 if (ctx.tx.isFieldPresent(sfCredentialIDs) &&
72 !ctx.rules.enabled(featureCredentials))
73 return false;
74 if (ctx.tx.isFieldPresent(sfDomainID) &&
75 !ctx.rules.enabled(featurePermissionedDEX))
76 return false;
77
78 return true;
79}
80
83{
84 auto& tx = ctx.tx;
85
86 STAmount const dstAmount(tx.getFieldAmount(sfAmount));
87 bool const mptDirect = dstAmount.holds<MPTIssue>();
88
89 return mptDirect ? tfMPTPaymentMask : tfPaymentMask;
90}
91
94{
95 auto& tx = ctx.tx;
96 auto& j = ctx.j;
97
98 STAmount const dstAmount(tx.getFieldAmount(sfAmount));
99 bool const mptDirect = dstAmount.holds<MPTIssue>();
100
101 if (mptDirect && !ctx.rules.enabled(featureMPTokensV1))
102 return temDISABLED;
103
104 std::uint32_t const txFlags = tx.getFlags();
105
106 if (mptDirect && ctx.tx.isFieldPresent(sfPaths))
107 return temMALFORMED;
108
109 bool const partialPaymentAllowed = txFlags & tfPartialPayment;
110 bool const limitQuality = txFlags & tfLimitQuality;
111 bool const defaultPathsAllowed = !(txFlags & tfNoRippleDirect);
112 bool const hasPaths = tx.isFieldPresent(sfPaths);
113 bool const hasMax = tx.isFieldPresent(sfSendMax);
114
115 auto const deliverMin = tx[~sfDeliverMin];
116
117 auto const account = tx.getAccountID(sfAccount);
118 STAmount const maxSourceAmount =
119 getMaxSourceAmount(account, dstAmount, tx[~sfSendMax]);
120
121 if ((mptDirect && dstAmount.asset() != maxSourceAmount.asset()) ||
122 (!mptDirect && maxSourceAmount.holds<MPTIssue>()))
123 {
124 JLOG(j.trace()) << "Malformed transaction: inconsistent issues: "
125 << dstAmount.getFullText() << " "
126 << maxSourceAmount.getFullText() << " "
127 << deliverMin.value_or(STAmount{}).getFullText();
128 return temMALFORMED;
129 }
130
131 auto const& srcAsset = maxSourceAmount.asset();
132 auto const& dstAsset = dstAmount.asset();
133
134 bool const xrpDirect = srcAsset.native() && dstAsset.native();
135
136 if (!isLegalNet(dstAmount) || !isLegalNet(maxSourceAmount))
137 return temBAD_AMOUNT;
138
139 auto const dstAccountID = tx.getAccountID(sfDestination);
140
141 if (!dstAccountID)
142 {
143 JLOG(j.trace()) << "Malformed transaction: "
144 << "Payment destination account not specified.";
145 return temDST_NEEDED;
146 }
147 if (hasMax && maxSourceAmount <= beast::zero)
148 {
149 JLOG(j.trace()) << "Malformed transaction: bad max amount: "
150 << maxSourceAmount.getFullText();
151 return temBAD_AMOUNT;
152 }
153 if (dstAmount <= beast::zero)
154 {
155 JLOG(j.trace()) << "Malformed transaction: bad dst amount: "
156 << dstAmount.getFullText();
157 return temBAD_AMOUNT;
158 }
159 if (badCurrency() == srcAsset || badCurrency() == dstAsset)
160 {
161 JLOG(j.trace()) << "Malformed transaction: Bad currency.";
162 return temBAD_CURRENCY;
163 }
164 if (account == dstAccountID && equalTokens(srcAsset, dstAsset) && !hasPaths)
165 {
166 // You're signing yourself a payment.
167 // If hasPaths is true, you might be trying some arbitrage.
168 JLOG(j.trace()) << "Malformed transaction: "
169 << "Redundant payment from " << to_string(account)
170 << " to self without path for " << to_string(dstAsset);
171 return temREDUNDANT;
172 }
173 if (xrpDirect && hasMax)
174 {
175 // Consistent but redundant transaction.
176 JLOG(j.trace()) << "Malformed transaction: "
177 << "SendMax specified for XRP to XRP.";
178 return temBAD_SEND_XRP_MAX;
179 }
180 if ((xrpDirect || mptDirect) && hasPaths)
181 {
182 // XRP is sent without paths.
183 JLOG(j.trace()) << "Malformed transaction: "
184 << "Paths specified for XRP to XRP or MPT to MPT.";
186 }
187 if (xrpDirect && partialPaymentAllowed)
188 {
189 // Consistent but redundant transaction.
190 JLOG(j.trace()) << "Malformed transaction: "
191 << "Partial payment specified for XRP to XRP.";
193 }
194 if ((xrpDirect || mptDirect) && limitQuality)
195 {
196 // Consistent but redundant transaction.
197 JLOG(j.trace())
198 << "Malformed transaction: "
199 << "Limit quality specified for XRP to XRP or MPT to MPT.";
201 }
202 if ((xrpDirect || mptDirect) && !defaultPathsAllowed)
203 {
204 // Consistent but redundant transaction.
205 JLOG(j.trace())
206 << "Malformed transaction: "
207 << "No ripple direct specified for XRP to XRP or MPT to MPT.";
209 }
210
211 if (deliverMin)
212 {
213 if (!partialPaymentAllowed)
214 {
215 JLOG(j.trace()) << "Malformed transaction: Partial payment not "
216 "specified for "
217 << jss::DeliverMin.c_str() << ".";
218 return temBAD_AMOUNT;
219 }
220
221 auto const dMin = *deliverMin;
222 if (!isLegalNet(dMin) || dMin <= beast::zero)
223 {
224 JLOG(j.trace())
225 << "Malformed transaction: Invalid " << jss::DeliverMin.c_str()
226 << " amount. " << dMin.getFullText();
227 return temBAD_AMOUNT;
228 }
229 if (dMin.asset() != dstAmount.asset())
230 {
231 JLOG(j.trace())
232 << "Malformed transaction: Dst issue differs "
233 "from "
234 << jss::DeliverMin.c_str() << ". " << dMin.getFullText();
235 return temBAD_AMOUNT;
236 }
237 if (dMin > dstAmount)
238 {
239 JLOG(j.trace())
240 << "Malformed transaction: Dst amount less than "
241 << jss::DeliverMin.c_str() << ". " << dMin.getFullText();
242 return temBAD_AMOUNT;
243 }
244 }
245
246 if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
247 !isTesSuccess(err))
248 return err;
249
250 return tesSUCCESS;
251}
252
253TER
255{
256 auto const delegate = tx[~sfDelegate];
257 if (!delegate)
258 return tesSUCCESS;
259
260 auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
261 auto const sle = view.read(delegateKey);
262
263 if (!sle)
265
266 if (checkTxPermission(sle, tx) == tesSUCCESS)
267 return tesSUCCESS;
268
270 loadGranularPermission(sle, ttPAYMENT, granularPermissions);
271
272 auto const& dstAmount = tx.getFieldAmount(sfAmount);
273 // post-amendment: disallow cross currency payments for PaymentMint and
274 // PaymentBurn
275 if (view.rules().enabled(fixDelegateV1_1))
276 {
277 auto const& amountAsset = dstAmount.asset();
278 if (tx.isFieldPresent(sfSendMax) &&
279 tx[sfSendMax].asset() != amountAsset)
281
282 if (granularPermissions.contains(PaymentMint) && !isXRP(amountAsset) &&
283 amountAsset.getIssuer() == tx[sfAccount])
284 return tesSUCCESS;
285
286 if (granularPermissions.contains(PaymentBurn) && !isXRP(amountAsset) &&
287 amountAsset.getIssuer() == tx[sfDestination])
288 return tesSUCCESS;
289
291 }
292
293 // Calling dstAmount.issue() in the next line would throw if it holds MPT.
294 // That exception would be caught in preclaim and returned as tefEXCEPTION.
295 // This check is just a cleaner, more explicit way to get the same result.
296 if (dstAmount.holds<MPTIssue>())
297 return tefEXCEPTION;
298
299 auto const& amountIssue = dstAmount.issue();
300 if (granularPermissions.contains(PaymentMint) && !isXRP(amountIssue) &&
301 amountIssue.account == tx[sfAccount])
302 return tesSUCCESS;
303
304 if (granularPermissions.contains(PaymentBurn) && !isXRP(amountIssue) &&
305 amountIssue.account == tx[sfDestination])
306 return tesSUCCESS;
307
309}
310
311TER
313{
314 // Ripple if source or destination is non-native or if there are paths.
315 std::uint32_t const txFlags = ctx.tx.getFlags();
316 bool const partialPaymentAllowed = txFlags & tfPartialPayment;
317 auto const hasPaths = ctx.tx.isFieldPresent(sfPaths);
318 auto const sendMax = ctx.tx[~sfSendMax];
319
320 AccountID const dstAccountID(ctx.tx[sfDestination]);
321 STAmount const dstAmount(ctx.tx[sfAmount]);
322
323 auto const k = keylet::account(dstAccountID);
324 auto const sleDst = ctx.view.read(k);
325
326 if (!sleDst)
327 {
328 // Destination account does not exist.
329 if (!dstAmount.native())
330 {
331 JLOG(ctx.j.trace())
332 << "Delay transaction: Destination account does not exist.";
333
334 // Another transaction could create the account and then this
335 // transaction would succeed.
336 return tecNO_DST;
337 }
338 else if (ctx.view.open() && partialPaymentAllowed)
339 {
340 // You cannot fund an account with a partial payment.
341 // Make retry work smaller, by rejecting this.
342 JLOG(ctx.j.trace()) << "Delay transaction: Partial payment not "
343 "allowed to create account.";
344
345 // Another transaction could create the account and then this
346 // transaction would succeed.
347 return telNO_DST_PARTIAL;
348 }
349 else if (dstAmount < STAmount(ctx.view.fees().reserve))
350 {
351 // accountReserve is the minimum amount that an account can have.
352 // Reserve is not scaled by load.
353 JLOG(ctx.j.trace())
354 << "Delay transaction: Destination account does not exist. "
355 << "Insufficent payment to create account.";
356
357 // TODO: dedupe
358 // Another transaction could create the account and then this
359 // transaction would succeed.
360 return tecNO_DST_INSUF_XRP;
361 }
362 }
363 else if (
364 (sleDst->getFlags() & lsfRequireDestTag) &&
365 !ctx.tx.isFieldPresent(sfDestinationTag))
366 {
367 // The tag is basically account-specific information we don't
368 // understand, but we can require someone to fill it in.
369
370 // We didn't make this test for a newly-formed account because there's
371 // no way for this field to be set.
372 JLOG(ctx.j.trace())
373 << "Malformed transaction: DestinationTag required.";
374
375 return tecDST_TAG_NEEDED;
376 }
377
378 // Payment with at least one intermediate step and uses transitive balances.
379 if ((hasPaths || sendMax || !dstAmount.native()) && ctx.view.open())
380 {
381 STPathSet const& paths = ctx.tx.getFieldPathSet(sfPaths);
382
383 if (paths.size() > MaxPathSize ||
384 std::any_of(paths.begin(), paths.end(), [](STPath const& path) {
385 return path.size() > MaxPathLength;
386 }))
387 {
388 return telBAD_PATH_COUNT;
389 }
390 }
391
392 if (auto const err =
393 credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
394 !isTesSuccess(err))
395 return err;
396
397 if (ctx.tx.isFieldPresent(sfDomainID))
398 {
400 ctx.view, ctx.tx[sfAccount], ctx.tx[sfDomainID]))
401 return tecNO_PERMISSION;
402
404 ctx.view, ctx.tx[sfDestination], ctx.tx[sfDomainID]))
405 return tecNO_PERMISSION;
406 }
407
408 return tesSUCCESS;
409}
410
411TER
413{
414 auto const deliverMin = ctx_.tx[~sfDeliverMin];
415
416 // Ripple if source or destination is non-native or if there are paths.
417 std::uint32_t const txFlags = ctx_.tx.getFlags();
418 bool const partialPaymentAllowed = txFlags & tfPartialPayment;
419 bool const limitQuality = txFlags & tfLimitQuality;
420 bool const defaultPathsAllowed = !(txFlags & tfNoRippleDirect);
421 auto const hasPaths = ctx_.tx.isFieldPresent(sfPaths);
422 auto const sendMax = ctx_.tx[~sfSendMax];
423
424 AccountID const dstAccountID(ctx_.tx.getAccountID(sfDestination));
425 STAmount const dstAmount(ctx_.tx.getFieldAmount(sfAmount));
426 bool const mptDirect = dstAmount.holds<MPTIssue>();
427 STAmount const maxSourceAmount =
428 getMaxSourceAmount(account_, dstAmount, sendMax);
429
430 JLOG(j_.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText()
431 << " dstAmount=" << dstAmount.getFullText();
432
433 // Open a ledger for editing.
434 auto const k = keylet::account(dstAccountID);
435 SLE::pointer sleDst = view().peek(k);
436
437 if (!sleDst)
438 {
439 std::uint32_t const seqno{
440 view().rules().enabled(featureDeletableAccounts) ? view().seq()
441 : 1};
442
443 // Create the account.
444 sleDst = std::make_shared<SLE>(k);
445 sleDst->setAccountID(sfAccount, dstAccountID);
446 sleDst->setFieldU32(sfSequence, seqno);
447
448 view().insert(sleDst);
449 }
450 else
451 {
452 // Tell the engine that we are intending to change the destination
453 // account. The source account gets always charged a fee so it's always
454 // marked as modified.
455 view().update(sleDst);
456 }
457
458 // Determine whether the destination requires deposit authorization.
459 bool const depositAuth = view().rules().enabled(featureDepositAuth);
460 bool const reqDepositAuth =
461 sleDst->getFlags() & lsfDepositAuth && depositAuth;
462
463 bool const depositPreauth = view().rules().enabled(featureDepositPreauth);
464
465 bool const ripple =
466 (hasPaths || sendMax || !dstAmount.native()) && !mptDirect;
467
468 // If the destination has lsfDepositAuth set, then only direct XRP
469 // payments (no intermediate steps) are allowed to the destination.
470 if (!depositPreauth && ripple && reqDepositAuth)
471 return tecNO_PERMISSION;
472
473 if (ripple)
474 {
475 // Ripple payment with at least one intermediate step and uses
476 // transitive balances.
477
478 if (depositPreauth && depositAuth)
479 {
480 // If depositPreauth is enabled, then an account that requires
481 // authorization has two ways to get an IOU Payment in:
482 // 1. If Account == Destination, or
483 // 2. If Account is deposit preauthorized by destination.
484
485 if (auto err = verifyDepositPreauth(
486 ctx_.tx,
487 ctx_.view(),
488 account_,
489 dstAccountID,
490 sleDst,
491 ctx_.journal);
492 !isTesSuccess(err))
493 return err;
494 }
495
497 rcInput.partialPaymentAllowed = partialPaymentAllowed;
498 rcInput.defaultPathsAllowed = defaultPathsAllowed;
499 rcInput.limitQuality = limitQuality;
500 rcInput.isLedgerOpen = view().open();
501
503 {
504 PaymentSandbox pv(&view());
505 JLOG(j_.debug()) << "Entering RippleCalc in payment: "
508 pv,
509 maxSourceAmount,
510 dstAmount,
511 dstAccountID,
512 account_,
513 ctx_.tx.getFieldPathSet(sfPaths),
514 ctx_.tx[~sfDomainID],
515 ctx_.app.logs(),
516 &rcInput);
517 // VFALCO NOTE We might not need to apply, depending
518 // on the TER. But always applying *should*
519 // be safe.
520 pv.apply(ctx_.rawView());
521 }
522
523 // TODO: is this right? If the amount is the correct amount, was
524 // the delivered amount previously set?
525 if (rc.result() == tesSUCCESS && rc.actualAmountOut != dstAmount)
526 {
527 if (deliverMin && rc.actualAmountOut < *deliverMin)
529 else
531 }
532
533 auto terResult = rc.result();
534
535 // Because of its overhead, if RippleCalc
536 // fails with a retry code, claim a fee
537 // instead. Maybe the user will be more
538 // careful with their path spec next time.
539 if (isTerRetry(terResult))
540 terResult = tecPATH_DRY;
541 return terResult;
542 }
543 else if (mptDirect)
544 {
545 JLOG(j_.trace()) << " dstAmount=" << dstAmount.getFullText();
546 auto const& mptIssue = dstAmount.get<MPTIssue>();
547
548 if (auto const ter = requireAuth(view(), mptIssue, account_);
549 ter != tesSUCCESS)
550 return ter;
551
552 if (auto const ter = requireAuth(view(), mptIssue, dstAccountID);
553 ter != tesSUCCESS)
554 return ter;
555
556 if (auto const ter =
557 canTransfer(view(), mptIssue, account_, dstAccountID);
558 ter != tesSUCCESS)
559 return ter;
560
561 if (auto err = verifyDepositPreauth(
562 ctx_.tx,
563 ctx_.view(),
564 account_,
565 dstAccountID,
566 sleDst,
567 ctx_.journal);
568 !isTesSuccess(err))
569 return err;
570
571 auto const& issuer = mptIssue.getIssuer();
572
573 // Transfer rate
574 Rate rate{QUALITY_ONE};
575 // Payment between the holders
576 if (account_ != issuer && dstAccountID != issuer)
577 {
578 // If globally/individually locked then
579 // - can't send between holders
580 // - holder can send back to issuer
581 // - issuer can send to holder
582 if (isAnyFrozen(view(), {account_, dstAccountID}, mptIssue))
583 return tecLOCKED;
584
585 // Get the rate for a payment between the holders.
586 rate = transferRate(view(), mptIssue.getMptID());
587 }
588
589 // Amount to deliver.
590 STAmount amountDeliver = dstAmount;
591 // Factor in the transfer rate.
592 // No rounding. It'll change once MPT integrated into DEX.
593 STAmount requiredMaxSourceAmount = multiply(dstAmount, rate);
594
595 // Send more than the account wants to pay or less than
596 // the account wants to deliver (if no SendMax).
597 // Adjust the amount to deliver.
598 if (partialPaymentAllowed && requiredMaxSourceAmount > maxSourceAmount)
599 {
600 requiredMaxSourceAmount = maxSourceAmount;
601 // No rounding. It'll change once MPT integrated into DEX.
602 amountDeliver = divide(maxSourceAmount, rate);
603 }
604
605 if (requiredMaxSourceAmount > maxSourceAmount ||
606 (deliverMin && amountDeliver < *deliverMin))
607 return tecPATH_PARTIAL;
608
609 PaymentSandbox pv(&view());
610 auto res = accountSend(
611 pv, account_, dstAccountID, amountDeliver, ctx_.journal);
612 if (res == tesSUCCESS)
613 {
614 pv.apply(ctx_.rawView());
615
616 // If the actual amount delivered is different from the original
617 // amount due to partial payment or transfer fee, we need to update
618 // DelieveredAmount using the actual delivered amount
619 if (view().rules().enabled(fixMPTDeliveredAmount) &&
620 amountDeliver != dstAmount)
621 ctx_.deliver(amountDeliver);
622 }
623 else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY)
624 res = tecPATH_PARTIAL;
625
626 return res;
627 }
628
629 XRPL_ASSERT(dstAmount.native(), "ripple::Payment::doApply : amount is XRP");
630
631 // Direct XRP payment.
632
633 auto const sleSrc = view().peek(keylet::account(account_));
634 if (!sleSrc)
635 return tefINTERNAL; // LCOV_EXCL_LINE
636
637 // ownerCount is the number of entries in this ledger for this
638 // account that require a reserve.
639 auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
640
641 // This is the total reserve in drops.
642 auto const reserve = view().fees().accountReserve(ownerCount);
643
644 // mPriorBalance is the balance on the sending account BEFORE the
645 // fees were charged. We want to make sure we have enough reserve
646 // to send. Allow final spend to use reserve for fee.
647 auto const mmm = std::max(reserve, ctx_.tx.getFieldAmount(sfFee).xrp());
648
649 if (mPriorBalance < dstAmount.xrp() + mmm)
650 {
651 // Vote no. However the transaction might succeed, if applied in
652 // a different order.
653 JLOG(j_.trace()) << "Delay transaction: Insufficient funds: "
654 << to_string(mPriorBalance) << " / "
655 << to_string(dstAmount.xrp() + mmm) << " ("
656 << to_string(reserve) << ")";
657
658 return tecUNFUNDED_PAYMENT;
659 }
660
661 // Pseudo-accounts cannot receive payments, other than these native to
662 // their underlying ledger object - implemented in their respective
663 // transaction types. Note, this is not amendment-gated because all writes
664 // to pseudo-account discriminator fields **are** amendment gated, hence the
665 // behaviour of this check will always match the active amendments.
666 if (isPseudoAccount(sleDst))
667 return tecNO_PERMISSION;
668
669 // The source account does have enough money. Make sure the
670 // source account has authority to deposit to the destination.
671 if (depositAuth)
672 {
673 // If depositPreauth is enabled, then an account that requires
674 // authorization has three ways to get an XRP Payment in:
675 // 1. If Account == Destination, or
676 // 2. If Account is deposit preauthorized by destination, or
677 // 3. If the destination's XRP balance is
678 // a. less than or equal to the base reserve and
679 // b. the deposit amount is less than or equal to the base reserve,
680 // then we allow the deposit.
681 //
682 // Rule 3 is designed to keep an account from getting wedged
683 // in an unusable state if it sets the lsfDepositAuth flag and
684 // then consumes all of its XRP. Without the rule if an
685 // account with lsfDepositAuth set spent all of its XRP, it
686 // would be unable to acquire more XRP required to pay fees.
687 //
688 // We choose the base reserve as our bound because it is
689 // a small number that seldom changes but is always sufficient
690 // to get the account un-wedged.
691
692 // Get the base reserve.
693 XRPAmount const dstReserve{view().fees().reserve};
694
695 if (dstAmount > dstReserve ||
696 sleDst->getFieldAmount(sfBalance) > dstReserve)
697 {
698 if (auto err = verifyDepositPreauth(
699 ctx_.tx,
700 ctx_.view(),
701 account_,
702 dstAccountID,
703 sleDst,
704 ctx_.journal);
705 !isTesSuccess(err))
706 return err;
707 }
708 }
709
710 // Do the arithmetic for the transfer and make the ledger change.
711 sleSrc->setFieldAmount(sfBalance, mSourceBalance - dstAmount);
712 sleDst->setFieldAmount(
713 sfBalance, sleDst->getFieldAmount(sfBalance) + dstAmount);
714
715 // Re-arm the password change fee if we can and need to.
716 if ((sleDst->getFlags() & lsfPasswordSpent))
717 sleDst->clearFlag(lsfPasswordSpent);
718
719 return tesSUCCESS;
720}
721
722} // namespace ripple
T any_of(T... args)
Stream debug() const
Definition Journal.h:328
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
virtual Logs & logs()=0
ApplyView & view()
Application & app
beast::Journal const journal
void deliver(STAmount const &amount)
Sets the DeliveredAmount field in the metadata.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
bool native() const
Definition Asset.h:101
A currency issued by an account.
Definition Issue.h:33
AccountID const & getIssuer() const
Definition MPTIssue.cpp:40
A wrapper which makes credits unavailable to balances.
void apply(RawView &to)
Apply changes to base view.
static std::size_t const MaxPathSize
Definition Payment.h:30
static TER checkPermission(ReadView const &view, STTx const &tx)
Definition Payment.cpp:254
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition Payment.cpp:69
TER doApply() override
Definition Payment.cpp:412
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition Payment.cpp:82
static TER preclaim(PreclaimContext const &ctx)
Definition Payment.cpp:312
static NotTEC preflight(PreflightContext const &ctx)
Definition Payment.cpp:93
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
Definition Payment.cpp:36
A view into a ledger.
Definition ReadView.h:51
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual bool open() const =0
Returns true if this reflects an open ledger.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:118
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:130
constexpr bool holds() const noexcept
Definition STAmount.h:465
int exponent() const noexcept
Definition STAmount.h:452
Asset const & asset() const
Definition STAmount.h:483
constexpr TIss const & get() const
XRPAmount xrp() const
Definition STAmount.cpp:306
std::uint64_t mantissa() const noexcept
Definition STAmount.h:477
std::string getFullText() const override
Definition STAmount.cpp:696
bool native() const noexcept
Definition STAmount.h:458
STPathSet const & getFieldPathSet(SField const &field) const
Definition STObject.cpp:678
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:657
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:671
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
std::uint32_t getFlags() const
Definition STObject.cpp:537
std::vector< STPath >::const_iterator end() const
Definition STPathSet.h:496
std::vector< STPath >::const_iterator begin() const
Definition STPathSet.h:490
std::vector< STPath >::size_type size() const
Definition STPathSet.h:502
uint256 getTransactionID() const
Definition STTx.h:219
AccountID const account_
Definition Transactor.h:147
ApplyView & view()
Definition Transactor.h:163
beast::Journal const j_
Definition Transactor.h:145
XRPAmount mPriorBalance
Definition Transactor.h:148
XRPAmount mSourceBalance
Definition Transactor.h:149
ApplyContext & ctx_
Definition Transactor.h:143
Class describing the consequences to the account of applying a transaction if the transaction consume...
Definition applySteps.h:58
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 contains(T... args)
T is_same_v
T max(T... args)
NotTEC checkFields(STTx const &tx, beast::Journal j)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:465
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
bool accountInDomain(ReadView const &view, AccountID const &account, Domain const &domainID)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:93
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
constexpr bool equalTokens(Asset const &lhs, Asset const &rhs)
Definition Asset.h:201
bool isXRP(AccountID const &c)
Definition AccountID.h:90
@ telBAD_PATH_COUNT
Definition TER.h:54
@ telNO_DST_PARTIAL
Definition TER.h:58
bool isLegalNet(STAmount const &value)
Definition STAmount.h:600
constexpr std::uint32_t tfMPTPaymentMask
Definition TxFlags.h:112
STAmount getMaxSourceAmount(AccountID const &account, STAmount const &dstAmount, std::optional< STAmount > const &sendMax)
Definition Payment.cpp:51
@ lsfRequireDestTag
@ lsfPasswordSpent
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2185
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2479
@ tefEXCEPTION
Definition TER.h:172
@ tefINTERNAL
Definition TER.h:173
constexpr std::uint32_t tfPartialPayment
Definition TxFlags.h:108
void loadGranularPermission(std::shared_ptr< SLE const > const &delegate, TxType const &type, std::unordered_set< GranularPermissionType > &granularPermissions)
Load the granular permissions granted to the delegate account for the specified transaction type.
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
Definition View.cpp:2698
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:762
TER checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
@ tecNO_DELEGATE_PERMISSION
Definition TER.h:364
@ tecNO_DST
Definition TER.h:290
@ tecINSUFFICIENT_FUNDS
Definition TER.h:325
@ tecNO_PERMISSION
Definition TER.h:305
@ tecDST_TAG_NEEDED
Definition TER.h:309
@ tecPATH_PARTIAL
Definition TER.h:282
@ tecUNFUNDED_PAYMENT
Definition TER.h:285
@ tecPATH_DRY
Definition TER.h:294
@ tecNO_DST_INSUF_XRP
Definition TER.h:291
@ tecLOCKED
Definition TER.h:358
bool isTerRetry(TER x) noexcept
Definition TER.h:668
constexpr std::uint32_t tfNoRippleDirect
Definition TxFlags.h:107
@ tesSUCCESS
Definition TER.h:244
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
constexpr std::uint32_t tfPaymentMask
Definition TxFlags.h:110
constexpr std::uint32_t tfLimitQuality
Definition TxFlags.h:109
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
Definition View.cpp:282
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1118
@ temBAD_SEND_XRP_PARTIAL
Definition TER.h:102
@ temBAD_AMOUNT
Definition TER.h:89
@ temREDUNDANT
Definition TER.h:112
@ temDST_NEEDED
Definition TER.h:109
@ temBAD_CURRENCY
Definition TER.h:90
@ temBAD_SEND_XRP_LIMIT
Definition TER.h:99
@ temMALFORMED
Definition TER.h:87
@ temBAD_SEND_XRP_PATHS
Definition TER.h:103
@ temBAD_SEND_XRP_NO_DIRECT
Definition TER.h:101
@ temDISABLED
Definition TER.h:114
@ temBAD_SEND_XRP_MAX
Definition TER.h:100
XRPAmount reserve
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
beast::Journal const j
Definition Transactor.h:88
State information when preflighting a tx.
Definition Transactor.h:35
beast::Journal const j
Definition Transactor.h:42
Represents a transfer rate.
Definition Rate.h:40
void setResult(TER const value)
Definition RippleCalc.h:82