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
253NotTEC
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 auto const& amountAsset = dstAmount.asset();
274
275 // Granular permissions are only valid for direct payments.
276 if ((tx.isFieldPresent(sfSendMax) &&
277 tx[sfSendMax].asset() != amountAsset) ||
278 tx.isFieldPresent(sfPaths))
280
281 if (granularPermissions.contains(PaymentMint) && !isXRP(amountAsset) &&
282 amountAsset.getIssuer() == tx[sfAccount])
283 return tesSUCCESS;
284
285 if (granularPermissions.contains(PaymentBurn) && !isXRP(amountAsset) &&
286 amountAsset.getIssuer() == tx[sfDestination])
287 return tesSUCCESS;
288
290}
291
292TER
294{
295 // Ripple if source or destination is non-native or if there are paths.
296 std::uint32_t const txFlags = ctx.tx.getFlags();
297 bool const partialPaymentAllowed = txFlags & tfPartialPayment;
298 auto const hasPaths = ctx.tx.isFieldPresent(sfPaths);
299 auto const sendMax = ctx.tx[~sfSendMax];
300
301 AccountID const dstAccountID(ctx.tx[sfDestination]);
302 STAmount const dstAmount(ctx.tx[sfAmount]);
303
304 auto const k = keylet::account(dstAccountID);
305 auto const sleDst = ctx.view.read(k);
306
307 if (!sleDst)
308 {
309 // Destination account does not exist.
310 if (!dstAmount.native())
311 {
312 JLOG(ctx.j.trace())
313 << "Delay transaction: Destination account does not exist.";
314
315 // Another transaction could create the account and then this
316 // transaction would succeed.
317 return tecNO_DST;
318 }
319 else if (ctx.view.open() && partialPaymentAllowed)
320 {
321 // You cannot fund an account with a partial payment.
322 // Make retry work smaller, by rejecting this.
323 JLOG(ctx.j.trace()) << "Delay transaction: Partial payment not "
324 "allowed to create account.";
325
326 // Another transaction could create the account and then this
327 // transaction would succeed.
328 return telNO_DST_PARTIAL;
329 }
330 else if (dstAmount < STAmount(ctx.view.fees().reserve))
331 {
332 // accountReserve is the minimum amount that an account can have.
333 // Reserve is not scaled by load.
334 JLOG(ctx.j.trace())
335 << "Delay transaction: Destination account does not exist. "
336 << "Insufficent payment to create account.";
337
338 // TODO: dedupe
339 // Another transaction could create the account and then this
340 // transaction would succeed.
341 return tecNO_DST_INSUF_XRP;
342 }
343 }
344 else if (
345 (sleDst->getFlags() & lsfRequireDestTag) &&
346 !ctx.tx.isFieldPresent(sfDestinationTag))
347 {
348 // The tag is basically account-specific information we don't
349 // understand, but we can require someone to fill it in.
350
351 // We didn't make this test for a newly-formed account because there's
352 // no way for this field to be set.
353 JLOG(ctx.j.trace())
354 << "Malformed transaction: DestinationTag required.";
355
356 return tecDST_TAG_NEEDED;
357 }
358
359 // Payment with at least one intermediate step and uses transitive balances.
360 if ((hasPaths || sendMax || !dstAmount.native()) && ctx.view.open())
361 {
362 STPathSet const& paths = ctx.tx.getFieldPathSet(sfPaths);
363
364 if (paths.size() > MaxPathSize ||
365 std::any_of(paths.begin(), paths.end(), [](STPath const& path) {
366 return path.size() > MaxPathLength;
367 }))
368 {
369 return telBAD_PATH_COUNT;
370 }
371 }
372
373 if (auto const err =
374 credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
375 !isTesSuccess(err))
376 return err;
377
378 if (ctx.tx.isFieldPresent(sfDomainID))
379 {
381 ctx.view, ctx.tx[sfAccount], ctx.tx[sfDomainID]))
382 return tecNO_PERMISSION;
383
385 ctx.view, ctx.tx[sfDestination], ctx.tx[sfDomainID]))
386 return tecNO_PERMISSION;
387 }
388
389 return tesSUCCESS;
390}
391
392TER
394{
395 auto const deliverMin = ctx_.tx[~sfDeliverMin];
396
397 // Ripple if source or destination is non-native or if there are paths.
398 std::uint32_t const txFlags = ctx_.tx.getFlags();
399 bool const partialPaymentAllowed = txFlags & tfPartialPayment;
400 bool const limitQuality = txFlags & tfLimitQuality;
401 bool const defaultPathsAllowed = !(txFlags & tfNoRippleDirect);
402 auto const hasPaths = ctx_.tx.isFieldPresent(sfPaths);
403 auto const sendMax = ctx_.tx[~sfSendMax];
404
405 AccountID const dstAccountID(ctx_.tx.getAccountID(sfDestination));
406 STAmount const dstAmount(ctx_.tx.getFieldAmount(sfAmount));
407 bool const mptDirect = dstAmount.holds<MPTIssue>();
408 STAmount const maxSourceAmount =
409 getMaxSourceAmount(account_, dstAmount, sendMax);
410
411 JLOG(j_.trace()) << "maxSourceAmount=" << maxSourceAmount.getFullText()
412 << " dstAmount=" << dstAmount.getFullText();
413
414 // Open a ledger for editing.
415 auto const k = keylet::account(dstAccountID);
416 SLE::pointer sleDst = view().peek(k);
417
418 if (!sleDst)
419 {
420 std::uint32_t const seqno{
421 view().rules().enabled(featureDeletableAccounts) ? view().seq()
422 : 1};
423
424 // Create the account.
425 sleDst = std::make_shared<SLE>(k);
426 sleDst->setAccountID(sfAccount, dstAccountID);
427 sleDst->setFieldU32(sfSequence, seqno);
428
429 view().insert(sleDst);
430 }
431 else
432 {
433 // Tell the engine that we are intending to change the destination
434 // account. The source account gets always charged a fee so it's always
435 // marked as modified.
436 view().update(sleDst);
437 }
438
439 // Determine whether the destination requires deposit authorization.
440 bool const depositAuth = view().rules().enabled(featureDepositAuth);
441 bool const reqDepositAuth =
442 sleDst->getFlags() & lsfDepositAuth && depositAuth;
443
444 bool const depositPreauth = view().rules().enabled(featureDepositPreauth);
445
446 bool const ripple =
447 (hasPaths || sendMax || !dstAmount.native()) && !mptDirect;
448
449 // If the destination has lsfDepositAuth set, then only direct XRP
450 // payments (no intermediate steps) are allowed to the destination.
451 if (!depositPreauth && ripple && reqDepositAuth)
452 return tecNO_PERMISSION;
453
454 if (ripple)
455 {
456 // Ripple payment with at least one intermediate step and uses
457 // transitive balances.
458
459 if (depositPreauth && depositAuth)
460 {
461 // If depositPreauth is enabled, then an account that requires
462 // authorization has two ways to get an IOU Payment in:
463 // 1. If Account == Destination, or
464 // 2. If Account is deposit preauthorized by destination.
465
466 if (auto err = verifyDepositPreauth(
467 ctx_.tx,
468 ctx_.view(),
469 account_,
470 dstAccountID,
471 sleDst,
472 ctx_.journal);
473 !isTesSuccess(err))
474 return err;
475 }
476
478 rcInput.partialPaymentAllowed = partialPaymentAllowed;
479 rcInput.defaultPathsAllowed = defaultPathsAllowed;
480 rcInput.limitQuality = limitQuality;
481 rcInput.isLedgerOpen = view().open();
482
484 {
485 PaymentSandbox pv(&view());
486 JLOG(j_.debug()) << "Entering RippleCalc in payment: "
489 pv,
490 maxSourceAmount,
491 dstAmount,
492 dstAccountID,
493 account_,
494 ctx_.tx.getFieldPathSet(sfPaths),
495 ctx_.tx[~sfDomainID],
496 ctx_.app.logs(),
497 &rcInput);
498 // VFALCO NOTE We might not need to apply, depending
499 // on the TER. But always applying *should*
500 // be safe.
501 pv.apply(ctx_.rawView());
502 }
503
504 // TODO: is this right? If the amount is the correct amount, was
505 // the delivered amount previously set?
506 if (rc.result() == tesSUCCESS && rc.actualAmountOut != dstAmount)
507 {
508 if (deliverMin && rc.actualAmountOut < *deliverMin)
510 else
512 }
513
514 auto terResult = rc.result();
515
516 // Because of its overhead, if RippleCalc
517 // fails with a retry code, claim a fee
518 // instead. Maybe the user will be more
519 // careful with their path spec next time.
520 if (isTerRetry(terResult))
521 terResult = tecPATH_DRY;
522 return terResult;
523 }
524 else if (mptDirect)
525 {
526 JLOG(j_.trace()) << " dstAmount=" << dstAmount.getFullText();
527 auto const& mptIssue = dstAmount.get<MPTIssue>();
528
529 if (auto const ter = requireAuth(view(), mptIssue, account_);
530 ter != tesSUCCESS)
531 return ter;
532
533 if (auto const ter = requireAuth(view(), mptIssue, dstAccountID);
534 ter != tesSUCCESS)
535 return ter;
536
537 if (auto const ter =
538 canTransfer(view(), mptIssue, account_, dstAccountID);
539 ter != tesSUCCESS)
540 return ter;
541
542 if (auto err = verifyDepositPreauth(
543 ctx_.tx,
544 ctx_.view(),
545 account_,
546 dstAccountID,
547 sleDst,
548 ctx_.journal);
549 !isTesSuccess(err))
550 return err;
551
552 auto const& issuer = mptIssue.getIssuer();
553
554 // Transfer rate
555 Rate rate{QUALITY_ONE};
556 // Payment between the holders
557 if (account_ != issuer && dstAccountID != issuer)
558 {
559 // If globally/individually locked then
560 // - can't send between holders
561 // - holder can send back to issuer
562 // - issuer can send to holder
563 if (isAnyFrozen(view(), {account_, dstAccountID}, mptIssue))
564 return tecLOCKED;
565
566 // Get the rate for a payment between the holders.
567 rate = transferRate(view(), mptIssue.getMptID());
568 }
569
570 // Amount to deliver.
571 STAmount amountDeliver = dstAmount;
572 // Factor in the transfer rate.
573 // No rounding. It'll change once MPT integrated into DEX.
574 STAmount requiredMaxSourceAmount = multiply(dstAmount, rate);
575
576 // Send more than the account wants to pay or less than
577 // the account wants to deliver (if no SendMax).
578 // Adjust the amount to deliver.
579 if (partialPaymentAllowed && requiredMaxSourceAmount > maxSourceAmount)
580 {
581 requiredMaxSourceAmount = maxSourceAmount;
582 // No rounding. It'll change once MPT integrated into DEX.
583 amountDeliver = divide(maxSourceAmount, rate);
584 }
585
586 if (requiredMaxSourceAmount > maxSourceAmount ||
587 (deliverMin && amountDeliver < *deliverMin))
588 return tecPATH_PARTIAL;
589
590 PaymentSandbox pv(&view());
591 auto res = accountSend(
592 pv, account_, dstAccountID, amountDeliver, ctx_.journal);
593 if (res == tesSUCCESS)
594 {
595 pv.apply(ctx_.rawView());
596
597 // If the actual amount delivered is different from the original
598 // amount due to partial payment or transfer fee, we need to update
599 // DelieveredAmount using the actual delivered amount
600 if (view().rules().enabled(fixMPTDeliveredAmount) &&
601 amountDeliver != dstAmount)
602 ctx_.deliver(amountDeliver);
603 }
604 else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY)
605 res = tecPATH_PARTIAL;
606
607 return res;
608 }
609
610 XRPL_ASSERT(dstAmount.native(), "ripple::Payment::doApply : amount is XRP");
611
612 // Direct XRP payment.
613
614 auto const sleSrc = view().peek(keylet::account(account_));
615 if (!sleSrc)
616 return tefINTERNAL; // LCOV_EXCL_LINE
617
618 // ownerCount is the number of entries in this ledger for this
619 // account that require a reserve.
620 auto const ownerCount = sleSrc->getFieldU32(sfOwnerCount);
621
622 // This is the total reserve in drops.
623 auto const reserve = view().fees().accountReserve(ownerCount);
624
625 // mPriorBalance is the balance on the sending account BEFORE the
626 // fees were charged. We want to make sure we have enough reserve
627 // to send. Allow final spend to use reserve for fee.
628 auto const mmm = std::max(reserve, ctx_.tx.getFieldAmount(sfFee).xrp());
629
630 if (mPriorBalance < dstAmount.xrp() + mmm)
631 {
632 // Vote no. However the transaction might succeed, if applied in
633 // a different order.
634 JLOG(j_.trace()) << "Delay transaction: Insufficient funds: "
635 << to_string(mPriorBalance) << " / "
636 << to_string(dstAmount.xrp() + mmm) << " ("
637 << to_string(reserve) << ")";
638
639 return tecUNFUNDED_PAYMENT;
640 }
641
642 // Pseudo-accounts cannot receive payments, other than these native to
643 // their underlying ledger object - implemented in their respective
644 // transaction types. Note, this is not amendment-gated because all writes
645 // to pseudo-account discriminator fields **are** amendment gated, hence the
646 // behaviour of this check will always match the active amendments.
647 if (isPseudoAccount(sleDst))
648 return tecNO_PERMISSION;
649
650 // The source account does have enough money. Make sure the
651 // source account has authority to deposit to the destination.
652 if (depositAuth)
653 {
654 // If depositPreauth is enabled, then an account that requires
655 // authorization has three ways to get an XRP Payment in:
656 // 1. If Account == Destination, or
657 // 2. If Account is deposit preauthorized by destination, or
658 // 3. If the destination's XRP balance is
659 // a. less than or equal to the base reserve and
660 // b. the deposit amount is less than or equal to the base reserve,
661 // then we allow the deposit.
662 //
663 // Rule 3 is designed to keep an account from getting wedged
664 // in an unusable state if it sets the lsfDepositAuth flag and
665 // then consumes all of its XRP. Without the rule if an
666 // account with lsfDepositAuth set spent all of its XRP, it
667 // would be unable to acquire more XRP required to pay fees.
668 //
669 // We choose the base reserve as our bound because it is
670 // a small number that seldom changes but is always sufficient
671 // to get the account un-wedged.
672
673 // Get the base reserve.
674 XRPAmount const dstReserve{view().fees().reserve};
675
676 if (dstAmount > dstReserve ||
677 sleDst->getFieldAmount(sfBalance) > dstReserve)
678 {
679 if (auto err = verifyDepositPreauth(
680 ctx_.tx,
681 ctx_.view(),
682 account_,
683 dstAccountID,
684 sleDst,
685 ctx_.journal);
686 !isTesSuccess(err))
687 return err;
688 }
689 }
690
691 // Do the arithmetic for the transfer and make the ledger change.
692 sleSrc->setFieldAmount(sfBalance, mSourceBalance - dstAmount);
693 sleDst->setFieldAmount(
694 sfBalance, sleDst->getFieldAmount(sfBalance) + dstAmount);
695
696 // Re-arm the password change fee if we can and need to.
697 if ((sleDst->getFlags() & lsfPasswordSpent))
698 sleDst->clearFlag(lsfPasswordSpent);
699
700 return tesSUCCESS;
701}
702
703} // 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 NotTEC checkPermission(ReadView const &view, STTx const &tx)
Definition Payment.cpp:254
static std::size_t const MaxPathSize
Definition Payment.h:30
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition Payment.cpp:69
TER doApply() override
Definition Payment.cpp:393
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition Payment.cpp:82
static TER preclaim(PreclaimContext const &ctx)
Definition Payment.cpp:293
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:283
std::uint64_t mantissa() const noexcept
Definition STAmount.h:477
std::string getFullText() const override
Definition STAmount.cpp:673
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:249
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
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
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:2191
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:2485
@ 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:2704
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:762
@ tecNO_DST
Definition TER.h:291
@ tecINSUFFICIENT_FUNDS
Definition TER.h:326
@ tecNO_PERMISSION
Definition TER.h:306
@ tecDST_TAG_NEEDED
Definition TER.h:310
@ tecPATH_PARTIAL
Definition TER.h:283
@ tecUNFUNDED_PAYMENT
Definition TER.h:286
@ tecPATH_DRY
Definition TER.h:295
@ tecNO_DST_INSUF_XRP
Definition TER.h:292
@ tecLOCKED
Definition TER.h:359
bool isTerRetry(TER x) noexcept
Definition TER.h:672
constexpr std::uint32_t tfNoRippleDirect
Definition TxFlags.h:107
@ tesSUCCESS
Definition TER.h:245
bool isTesSuccess(TER x) noexcept
Definition TER.h:678
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
@ terNO_DELEGATE_PERMISSION
Definition TER.h:230
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