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
70{
71 if (ctx.tx.isFieldPresent(sfCredentialIDs) &&
72 !ctx.rules.enabled(featureCredentials))
73 return temDISABLED;
74
75 if (ctx.tx.isFieldPresent(sfDomainID) &&
76 !ctx.rules.enabled(featurePermissionedDEX))
77 return temDISABLED;
78
79 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
80 return ret;
81
82 auto& tx = ctx.tx;
83 auto& j = ctx.j;
84
85 STAmount const dstAmount(tx.getFieldAmount(sfAmount));
86 bool const mptDirect = dstAmount.holds<MPTIssue>();
87
88 if (mptDirect && !ctx.rules.enabled(featureMPTokensV1))
89 return temDISABLED;
90
91 std::uint32_t const txFlags = tx.getFlags();
92
93 std::uint32_t paymentMask = mptDirect ? tfMPTPaymentMask : tfPaymentMask;
94
95 if (txFlags & paymentMask)
96 {
97 JLOG(j.trace()) << "Malformed transaction: Invalid flags set.";
98 return temINVALID_FLAG;
99 }
100
101 if (mptDirect && ctx.tx.isFieldPresent(sfPaths))
102 return temMALFORMED;
103
104 bool const partialPaymentAllowed = txFlags & tfPartialPayment;
105 bool const limitQuality = txFlags & tfLimitQuality;
106 bool const defaultPathsAllowed = !(txFlags & tfNoRippleDirect);
107 bool const hasPaths = tx.isFieldPresent(sfPaths);
108 bool const hasMax = tx.isFieldPresent(sfSendMax);
109
110 auto const deliverMin = tx[~sfDeliverMin];
111
112 auto const account = tx.getAccountID(sfAccount);
113 STAmount const maxSourceAmount =
114 getMaxSourceAmount(account, dstAmount, tx[~sfSendMax]);
115
116 if ((mptDirect && dstAmount.asset() != maxSourceAmount.asset()) ||
117 (!mptDirect && maxSourceAmount.holds<MPTIssue>()))
118 {
119 JLOG(j.trace()) << "Malformed transaction: inconsistent issues: "
120 << dstAmount.getFullText() << " "
121 << maxSourceAmount.getFullText() << " "
122 << deliverMin.value_or(STAmount{}).getFullText();
123 return temMALFORMED;
124 }
125
126 auto const& srcAsset = maxSourceAmount.asset();
127 auto const& dstAsset = dstAmount.asset();
128
129 bool const xrpDirect = srcAsset.native() && dstAsset.native();
130
131 if (!isLegalNet(dstAmount) || !isLegalNet(maxSourceAmount))
132 return temBAD_AMOUNT;
133
134 auto const dstAccountID = tx.getAccountID(sfDestination);
135
136 if (!dstAccountID)
137 {
138 JLOG(j.trace()) << "Malformed transaction: "
139 << "Payment destination account not specified.";
140 return temDST_NEEDED;
141 }
142 if (hasMax && maxSourceAmount <= beast::zero)
143 {
144 JLOG(j.trace()) << "Malformed transaction: bad max amount: "
145 << maxSourceAmount.getFullText();
146 return temBAD_AMOUNT;
147 }
148 if (dstAmount <= beast::zero)
149 {
150 JLOG(j.trace()) << "Malformed transaction: bad dst amount: "
151 << dstAmount.getFullText();
152 return temBAD_AMOUNT;
153 }
154 if (badCurrency() == srcAsset || badCurrency() == dstAsset)
155 {
156 JLOG(j.trace()) << "Malformed transaction: Bad currency.";
157 return temBAD_CURRENCY;
158 }
159 if (account == dstAccountID && equalTokens(srcAsset, dstAsset) && !hasPaths)
160 {
161 // You're signing yourself a payment.
162 // If hasPaths is true, you might be trying some arbitrage.
163 JLOG(j.trace()) << "Malformed transaction: "
164 << "Redundant payment from " << to_string(account)
165 << " to self without path for " << to_string(dstAsset);
166 return temREDUNDANT;
167 }
168 if (xrpDirect && hasMax)
169 {
170 // Consistent but redundant transaction.
171 JLOG(j.trace()) << "Malformed transaction: "
172 << "SendMax specified for XRP to XRP.";
173 return temBAD_SEND_XRP_MAX;
174 }
175 if ((xrpDirect || mptDirect) && hasPaths)
176 {
177 // XRP is sent without paths.
178 JLOG(j.trace()) << "Malformed transaction: "
179 << "Paths specified for XRP to XRP or MPT to MPT.";
181 }
182 if (xrpDirect && partialPaymentAllowed)
183 {
184 // Consistent but redundant transaction.
185 JLOG(j.trace()) << "Malformed transaction: "
186 << "Partial payment specified for XRP to XRP.";
188 }
189 if ((xrpDirect || mptDirect) && limitQuality)
190 {
191 // Consistent but redundant transaction.
192 JLOG(j.trace())
193 << "Malformed transaction: "
194 << "Limit quality specified for XRP to XRP or MPT to MPT.";
196 }
197 if ((xrpDirect || mptDirect) && !defaultPathsAllowed)
198 {
199 // Consistent but redundant transaction.
200 JLOG(j.trace())
201 << "Malformed transaction: "
202 << "No ripple direct specified for XRP to XRP or MPT to MPT.";
204 }
205
206 if (deliverMin)
207 {
208 if (!partialPaymentAllowed)
209 {
210 JLOG(j.trace()) << "Malformed transaction: Partial payment not "
211 "specified for "
212 << jss::DeliverMin.c_str() << ".";
213 return temBAD_AMOUNT;
214 }
215
216 auto const dMin = *deliverMin;
217 if (!isLegalNet(dMin) || dMin <= beast::zero)
218 {
219 JLOG(j.trace())
220 << "Malformed transaction: Invalid " << jss::DeliverMin.c_str()
221 << " amount. " << dMin.getFullText();
222 return temBAD_AMOUNT;
223 }
224 if (dMin.asset() != dstAmount.asset())
225 {
226 JLOG(j.trace())
227 << "Malformed transaction: Dst issue differs "
228 "from "
229 << jss::DeliverMin.c_str() << ". " << dMin.getFullText();
230 return temBAD_AMOUNT;
231 }
232 if (dMin > dstAmount)
233 {
234 JLOG(j.trace())
235 << "Malformed transaction: Dst amount less than "
236 << jss::DeliverMin.c_str() << ". " << dMin.getFullText();
237 return temBAD_AMOUNT;
238 }
239 }
240
241 if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
242 !isTesSuccess(err))
243 return err;
244
245 return preflight2(ctx);
246}
247
248TER
250{
251 auto const delegate = tx[~sfDelegate];
252 if (!delegate)
253 return tesSUCCESS;
254
255 auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
256 auto const sle = view.read(delegateKey);
257
258 if (!sle)
260
261 if (checkTxPermission(sle, tx) == tesSUCCESS)
262 return tesSUCCESS;
263
265 loadGranularPermission(sle, ttPAYMENT, granularPermissions);
266
267 auto const& dstAmount = tx.getFieldAmount(sfAmount);
268 // post-amendment: disallow cross currency payments for PaymentMint and
269 // PaymentBurn
270 if (view.rules().enabled(fixDelegateV1_1))
271 {
272 auto const& amountAsset = dstAmount.asset();
273 if (tx.isFieldPresent(sfSendMax) &&
274 tx[sfSendMax].asset() != amountAsset)
276
277 if (granularPermissions.contains(PaymentMint) && !isXRP(amountAsset) &&
278 amountAsset.getIssuer() == tx[sfAccount])
279 return tesSUCCESS;
280
281 if (granularPermissions.contains(PaymentBurn) && !isXRP(amountAsset) &&
282 amountAsset.getIssuer() == tx[sfDestination])
283 return tesSUCCESS;
284
286 }
287
288 // Calling dstAmount.issue() in the next line would throw if it holds MPT.
289 // That exception would be caught in preclaim and returned as tefEXCEPTION.
290 // This check is just a cleaner, more explicit way to get the same result.
291 if (dstAmount.holds<MPTIssue>())
292 return tefEXCEPTION;
293
294 auto const& amountIssue = dstAmount.issue();
295 if (granularPermissions.contains(PaymentMint) && !isXRP(amountIssue) &&
296 amountIssue.account == tx[sfAccount])
297 return tesSUCCESS;
298
299 if (granularPermissions.contains(PaymentBurn) && !isXRP(amountIssue) &&
300 amountIssue.account == tx[sfDestination])
301 return tesSUCCESS;
302
304}
305
306TER
308{
309 // Ripple if source or destination is non-native or if there are paths.
310 std::uint32_t const txFlags = ctx.tx.getFlags();
311 bool const partialPaymentAllowed = txFlags & tfPartialPayment;
312 auto const hasPaths = ctx.tx.isFieldPresent(sfPaths);
313 auto const sendMax = ctx.tx[~sfSendMax];
314
315 AccountID const dstAccountID(ctx.tx[sfDestination]);
316 STAmount const dstAmount(ctx.tx[sfAmount]);
317
318 auto const k = keylet::account(dstAccountID);
319 auto const sleDst = ctx.view.read(k);
320
321 if (!sleDst)
322 {
323 // Destination account does not exist.
324 if (!dstAmount.native())
325 {
326 JLOG(ctx.j.trace())
327 << "Delay transaction: Destination account does not exist.";
328
329 // Another transaction could create the account and then this
330 // transaction would succeed.
331 return tecNO_DST;
332 }
333 else if (ctx.view.open() && partialPaymentAllowed)
334 {
335 // You cannot fund an account with a partial payment.
336 // Make retry work smaller, by rejecting this.
337 JLOG(ctx.j.trace()) << "Delay transaction: Partial payment not "
338 "allowed to create account.";
339
340 // Another transaction could create the account and then this
341 // transaction would succeed.
342 return telNO_DST_PARTIAL;
343 }
344 else if (dstAmount < STAmount(ctx.view.fees().accountReserve(0)))
345 {
346 // accountReserve is the minimum amount that an account can have.
347 // Reserve is not scaled by load.
348 JLOG(ctx.j.trace())
349 << "Delay transaction: Destination account does not exist. "
350 << "Insufficent payment to create account.";
351
352 // TODO: dedupe
353 // Another transaction could create the account and then this
354 // transaction would succeed.
355 return tecNO_DST_INSUF_XRP;
356 }
357 }
358 else if (
359 (sleDst->getFlags() & lsfRequireDestTag) &&
360 !ctx.tx.isFieldPresent(sfDestinationTag))
361 {
362 // The tag is basically account-specific information we don't
363 // understand, but we can require someone to fill it in.
364
365 // We didn't make this test for a newly-formed account because there's
366 // no way for this field to be set.
367 JLOG(ctx.j.trace())
368 << "Malformed transaction: DestinationTag required.";
369
370 return tecDST_TAG_NEEDED;
371 }
372
373 // Payment with at least one intermediate step and uses transitive balances.
374 if ((hasPaths || sendMax || !dstAmount.native()) && ctx.view.open())
375 {
376 STPathSet const& paths = ctx.tx.getFieldPathSet(sfPaths);
377
378 if (paths.size() > MaxPathSize ||
379 std::any_of(paths.begin(), paths.end(), [](STPath const& path) {
380 return path.size() > MaxPathLength;
381 }))
382 {
383 return telBAD_PATH_COUNT;
384 }
385 }
386
387 if (auto const err =
388 credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
389 !isTesSuccess(err))
390 return err;
391
392 if (ctx.tx.isFieldPresent(sfDomainID))
393 {
395 ctx.view, ctx.tx[sfAccount], ctx.tx[sfDomainID]))
396 return tecNO_PERMISSION;
397
399 ctx.view, ctx.tx[sfDestination], ctx.tx[sfDomainID]))
400 return tecNO_PERMISSION;
401 }
402
403 return tesSUCCESS;
404}
405
406TER
408{
409 auto const deliverMin = ctx_.tx[~sfDeliverMin];
410
411 // Ripple if source or destination is non-native or if there are paths.
412 std::uint32_t const txFlags = ctx_.tx.getFlags();
413 bool const partialPaymentAllowed = txFlags & tfPartialPayment;
414 bool const limitQuality = txFlags & tfLimitQuality;
415 bool const defaultPathsAllowed = !(txFlags & tfNoRippleDirect);
416 auto const hasPaths = ctx_.tx.isFieldPresent(sfPaths);
417 auto const sendMax = ctx_.tx[~sfSendMax];
418
419 AccountID const dstAccountID(ctx_.tx.getAccountID(sfDestination));
420 STAmount const dstAmount(ctx_.tx.getFieldAmount(sfAmount));
421 bool const mptDirect = dstAmount.holds<MPTIssue>();
422 STAmount const maxSourceAmount =
423 getMaxSourceAmount(account_, dstAmount, sendMax);
424
425 JLOG(j_.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText()
426 << " dstAmount=" << dstAmount.getFullText();
427
428 // Open a ledger for editing.
429 auto const k = keylet::account(dstAccountID);
430 SLE::pointer sleDst = view().peek(k);
431
432 if (!sleDst)
433 {
434 std::uint32_t const seqno{
435 view().rules().enabled(featureDeletableAccounts) ? view().seq()
436 : 1};
437
438 // Create the account.
439 sleDst = std::make_shared<SLE>(k);
440 sleDst->setAccountID(sfAccount, dstAccountID);
441 sleDst->setFieldU32(sfSequence, seqno);
442
443 view().insert(sleDst);
444 }
445 else
446 {
447 // Tell the engine that we are intending to change the destination
448 // account. The source account gets always charged a fee so it's always
449 // marked as modified.
450 view().update(sleDst);
451 }
452
453 // Determine whether the destination requires deposit authorization.
454 bool const depositAuth = view().rules().enabled(featureDepositAuth);
455 bool const reqDepositAuth =
456 sleDst->getFlags() & lsfDepositAuth && depositAuth;
457
458 bool const depositPreauth = view().rules().enabled(featureDepositPreauth);
459
460 bool const ripple =
461 (hasPaths || sendMax || !dstAmount.native()) && !mptDirect;
462
463 // If the destination has lsfDepositAuth set, then only direct XRP
464 // payments (no intermediate steps) are allowed to the destination.
465 if (!depositPreauth && ripple && reqDepositAuth)
466 return tecNO_PERMISSION;
467
468 if (ripple)
469 {
470 // Ripple payment with at least one intermediate step and uses
471 // transitive balances.
472
473 if (depositPreauth && depositAuth)
474 {
475 // If depositPreauth is enabled, then an account that requires
476 // authorization has two ways to get an IOU Payment in:
477 // 1. If Account == Destination, or
478 // 2. If Account is deposit preauthorized by destination.
479
480 if (auto err = verifyDepositPreauth(
481 ctx_.tx,
482 ctx_.view(),
483 account_,
484 dstAccountID,
485 sleDst,
486 ctx_.journal);
487 !isTesSuccess(err))
488 return err;
489 }
490
492 rcInput.partialPaymentAllowed = partialPaymentAllowed;
493 rcInput.defaultPathsAllowed = defaultPathsAllowed;
494 rcInput.limitQuality = limitQuality;
495 rcInput.isLedgerOpen = view().open();
496
498 {
499 PaymentSandbox pv(&view());
500 JLOG(j_.debug()) << "Entering RippleCalc in payment: "
503 pv,
504 maxSourceAmount,
505 dstAmount,
506 dstAccountID,
507 account_,
508 ctx_.tx.getFieldPathSet(sfPaths),
509 ctx_.tx[~sfDomainID],
510 ctx_.app.logs(),
511 &rcInput);
512 // VFALCO NOTE We might not need to apply, depending
513 // on the TER. But always applying *should*
514 // be safe.
515 pv.apply(ctx_.rawView());
516 }
517
518 // TODO: is this right? If the amount is the correct amount, was
519 // the delivered amount previously set?
520 if (rc.result() == tesSUCCESS && rc.actualAmountOut != dstAmount)
521 {
522 if (deliverMin && rc.actualAmountOut < *deliverMin)
524 else
526 }
527
528 auto terResult = rc.result();
529
530 // Because of its overhead, if RippleCalc
531 // fails with a retry code, claim a fee
532 // instead. Maybe the user will be more
533 // careful with their path spec next time.
534 if (isTerRetry(terResult))
535 terResult = tecPATH_DRY;
536 return terResult;
537 }
538 else if (mptDirect)
539 {
540 JLOG(j_.trace()) << " dstAmount=" << dstAmount.getFullText();
541 auto const& mptIssue = dstAmount.get<MPTIssue>();
542
543 if (auto const ter = requireAuth(view(), mptIssue, account_);
544 ter != tesSUCCESS)
545 return ter;
546
547 if (auto const ter = requireAuth(view(), mptIssue, dstAccountID);
548 ter != tesSUCCESS)
549 return ter;
550
551 if (auto const ter =
552 canTransfer(view(), mptIssue, account_, dstAccountID);
553 ter != tesSUCCESS)
554 return ter;
555
556 if (auto err = verifyDepositPreauth(
557 ctx_.tx,
558 ctx_.view(),
559 account_,
560 dstAccountID,
561 sleDst,
562 ctx_.journal);
563 !isTesSuccess(err))
564 return err;
565
566 auto const& issuer = mptIssue.getIssuer();
567
568 // Transfer rate
569 Rate rate{QUALITY_ONE};
570 // Payment between the holders
571 if (account_ != issuer && dstAccountID != issuer)
572 {
573 // If globally/individually locked then
574 // - can't send between holders
575 // - holder can send back to issuer
576 // - issuer can send to holder
577 if (isAnyFrozen(view(), {account_, dstAccountID}, mptIssue))
578 return tecLOCKED;
579
580 // Get the rate for a payment between the holders.
581 rate = transferRate(view(), mptIssue.getMptID());
582 }
583
584 // Amount to deliver.
585 STAmount amountDeliver = dstAmount;
586 // Factor in the transfer rate.
587 // No rounding. It'll change once MPT integrated into DEX.
588 STAmount requiredMaxSourceAmount = multiply(dstAmount, rate);
589
590 // Send more than the account wants to pay or less than
591 // the account wants to deliver (if no SendMax).
592 // Adjust the amount to deliver.
593 if (partialPaymentAllowed && requiredMaxSourceAmount > maxSourceAmount)
594 {
595 requiredMaxSourceAmount = maxSourceAmount;
596 // No rounding. It'll change once MPT integrated into DEX.
597 amountDeliver = divide(maxSourceAmount, rate);
598 }
599
600 if (requiredMaxSourceAmount > maxSourceAmount ||
601 (deliverMin && amountDeliver < *deliverMin))
602 return tecPATH_PARTIAL;
603
604 PaymentSandbox pv(&view());
605 auto res = accountSend(
606 pv, account_, dstAccountID, amountDeliver, ctx_.journal);
607 if (res == tesSUCCESS)
608 {
609 pv.apply(ctx_.rawView());
610
611 // If the actual amount delivered is different from the original
612 // amount due to partial payment or transfer fee, we need to update
613 // DelieveredAmount using the actual delivered amount
614 if (view().rules().enabled(fixMPTDeliveredAmount) &&
615 amountDeliver != dstAmount)
616 ctx_.deliver(amountDeliver);
617 }
618 else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY)
619 res = tecPATH_PARTIAL;
620
621 return res;
622 }
623
624 XRPL_ASSERT(dstAmount.native(), "ripple::Payment::doApply : amount is XRP");
625
626 // Direct XRP payment.
627
628 auto const sleSrc = view().peek(keylet::account(account_));
629 if (!sleSrc)
630 return tefINTERNAL;
631
632 // ownerCount is the number of entries in this ledger for this
633 // account that require a reserve.
634 auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
635
636 // This is the total reserve in drops.
637 auto const reserve = view().fees().accountReserve(ownerCount);
638
639 // mPriorBalance is the balance on the sending account BEFORE the
640 // fees were charged. We want to make sure we have enough reserve
641 // to send. Allow final spend to use reserve for fee.
642 auto const mmm = std::max(reserve, ctx_.tx.getFieldAmount(sfFee).xrp());
643
644 if (mPriorBalance < dstAmount.xrp() + mmm)
645 {
646 // Vote no. However the transaction might succeed, if applied in
647 // a different order.
648 JLOG(j_.trace()) << "Delay transaction: Insufficient funds: "
649 << to_string(mPriorBalance) << " / "
650 << to_string(dstAmount.xrp() + mmm) << " ("
651 << to_string(reserve) << ")";
652
653 return tecUNFUNDED_PAYMENT;
654 }
655
656 // Pseudo-accounts cannot receive payments, other than these native to
657 // their underlying ledger object - implemented in their respective
658 // transaction types. Note, this is not amendment-gated because all writes
659 // to pseudo-account discriminator fields **are** amendment gated, hence the
660 // behaviour of this check will always match the active amendments.
661 if (isPseudoAccount(sleDst))
662 return tecNO_PERMISSION;
663
664 // The source account does have enough money. Make sure the
665 // source account has authority to deposit to the destination.
666 if (depositAuth)
667 {
668 // If depositPreauth is enabled, then an account that requires
669 // authorization has three ways to get an XRP Payment in:
670 // 1. If Account == Destination, or
671 // 2. If Account is deposit preauthorized by destination, or
672 // 3. If the destination's XRP balance is
673 // a. less than or equal to the base reserve and
674 // b. the deposit amount is less than or equal to the base reserve,
675 // then we allow the deposit.
676 //
677 // Rule 3 is designed to keep an account from getting wedged
678 // in an unusable state if it sets the lsfDepositAuth flag and
679 // then consumes all of its XRP. Without the rule if an
680 // account with lsfDepositAuth set spent all of its XRP, it
681 // would be unable to acquire more XRP required to pay fees.
682 //
683 // We choose the base reserve as our bound because it is
684 // a small number that seldom changes but is always sufficient
685 // to get the account un-wedged.
686
687 // Get the base reserve.
688 XRPAmount const dstReserve{view().fees().accountReserve(0)};
689
690 if (dstAmount > dstReserve ||
691 sleDst->getFieldAmount(sfBalance) > dstReserve)
692 {
693 if (auto err = verifyDepositPreauth(
694 ctx_.tx,
695 ctx_.view(),
696 account_,
697 dstAccountID,
698 sleDst,
699 ctx_.journal);
700 !isTesSuccess(err))
701 return err;
702 }
703 }
704
705 // Do the arithmetic for the transfer and make the ledger change.
706 sleSrc->setFieldAmount(sfBalance, mSourceBalance - dstAmount);
707 sleDst->setFieldAmount(
708 sfBalance, sleDst->getFieldAmount(sfBalance) + dstAmount);
709
710 // Re-arm the password change fee if we can and need to.
711 if ((sleDst->getFlags() & lsfPasswordSpent))
712 sleDst->clearFlag(lsfPasswordSpent);
713
714 return tesSUCCESS;
715}
716
717} // 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:249
TER doApply() override
Definition Payment.cpp:407
static TER preclaim(PreclaimContext const &ctx)
Definition Payment.cpp:307
static NotTEC preflight(PreflightContext const &ctx)
Definition Payment.cpp:69
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:672
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:651
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:665
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:145
ApplyView & view()
Definition Transactor.h:161
beast::Journal const j_
Definition Transactor.h:143
XRPAmount mPriorBalance
Definition Transactor.h:146
XRPAmount mSourceBalance
Definition Transactor.h:147
ApplyContext & ctx_
Definition Transactor.h:141
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
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
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:2174
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:2464
@ 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:2682
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:759
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:1115
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ 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
@ temINVALID_FLAG
Definition TER.h:111
@ temBAD_SEND_XRP_NO_DIRECT
Definition TER.h:101
@ temDISABLED
Definition TER.h:114
@ temBAD_SEND_XRP_MAX
Definition TER.h:100
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