rippled
Loading...
Searching...
No Matches
Escrow.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/HashRouter.h>
21#include <xrpld/app/tx/detail/Escrow.h>
22#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
23#include <xrpld/conditions/Condition.h>
24#include <xrpld/conditions/Fulfillment.h>
25
26#include <xrpl/basics/Log.h>
27#include <xrpl/basics/chrono.h>
28#include <xrpl/ledger/ApplyView.h>
29#include <xrpl/ledger/CredentialHelpers.h>
30#include <xrpl/ledger/View.h>
31#include <xrpl/protocol/Feature.h>
32#include <xrpl/protocol/Indexes.h>
33#include <xrpl/protocol/MPTAmount.h>
34#include <xrpl/protocol/TxFlags.h>
35#include <xrpl/protocol/XRPAmount.h>
36
37namespace ripple {
38
39// During an EscrowFinish, the transaction must specify both
40// a condition and a fulfillment. We track whether that
41// fulfillment matches and validates the condition.
44
45/*
46 Escrow
47 ======
48
49 Escrow is a feature of the XRP Ledger that allows you to send conditional
50 XRP payments. These conditional payments, called escrows, set aside XRP and
51 deliver it later when certain conditions are met. Conditions to successfully
52 finish an escrow include time-based unlocks and crypto-conditions. Escrows
53 can also be set to expire if not finished in time.
54
55 The XRP set aside in an escrow is locked up. No one can use or destroy the
56 XRP until the escrow has been successfully finished or canceled. Before the
57 expiration time, only the intended receiver can get the XRP. After the
58 expiration time, the XRP can only be returned to the sender.
59
60 For more details on escrow, including examples, diagrams and more please
61 visit https://xrpl.org/escrow.html
62
63 For details on specific transactions, including fields and validation rules
64 please see:
65
66 `EscrowCreate`
67 --------------
68 See: https://xrpl.org/escrowcreate.html
69
70 `EscrowFinish`
71 --------------
72 See: https://xrpl.org/escrowfinish.html
73
74 `EscrowCancel`
75 --------------
76 See: https://xrpl.org/escrowcancel.html
77*/
78
79//------------------------------------------------------------------------------
80
83{
84 auto const amount = ctx.tx[sfAmount];
85 return TxConsequences{ctx.tx, isXRP(amount) ? amount.xrp() : beast::zero};
86}
87
88template <ValidIssueType T>
89static NotTEC
91
92template <>
95{
96 STAmount const amount = ctx.tx[sfAmount];
97 if (amount.native() || amount <= beast::zero)
98 return temBAD_AMOUNT;
99
100 if (badCurrency() == amount.getCurrency())
101 return temBAD_CURRENCY;
102
103 return tesSUCCESS;
104}
105
106template <>
109{
110 if (!ctx.rules.enabled(featureMPTokensV1))
111 return temDISABLED;
112
113 auto const amount = ctx.tx[sfAmount];
114 if (amount.native() || amount.mpt() > MPTAmount{maxMPTokenAmount} ||
115 amount <= beast::zero)
116 return temBAD_AMOUNT;
117
118 return tesSUCCESS;
119}
120
123{
124 // 0 means "Allow any flags"
125 return ctx.rules.enabled(fix1543) ? tfUniversalMask : 0;
126}
127
128NotTEC
130{
131 STAmount const amount{ctx.tx[sfAmount]};
132 if (!isXRP(amount))
133 {
134 if (!ctx.rules.enabled(featureTokenEscrow))
135 return temBAD_AMOUNT;
136
137 if (auto const ret = std::visit(
138 [&]<typename T>(T const&) {
139 return escrowCreatePreflightHelper<T>(ctx);
140 },
141 amount.asset().value());
142 !isTesSuccess(ret))
143 return ret;
144 }
145 else
146 {
147 if (amount <= beast::zero)
148 return temBAD_AMOUNT;
149 }
150
151 // We must specify at least one timeout value
152 if (!ctx.tx[~sfCancelAfter] && !ctx.tx[~sfFinishAfter])
153 return temBAD_EXPIRATION;
154
155 // If both finish and cancel times are specified then the cancel time must
156 // be strictly after the finish time.
157 if (ctx.tx[~sfCancelAfter] && ctx.tx[~sfFinishAfter] &&
158 ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter])
159 return temBAD_EXPIRATION;
160
161 if (ctx.rules.enabled(fix1571))
162 {
163 // In the absence of a FinishAfter, the escrow can be finished
164 // immediately, which can be confusing. When creating an escrow,
165 // we want to ensure that either a FinishAfter time is explicitly
166 // specified or a completion condition is attached.
167 if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition])
168 return temMALFORMED;
169 }
170
171 if (auto const cb = ctx.tx[~sfCondition])
172 {
173 using namespace ripple::cryptoconditions;
174
176
177 auto condition = Condition::deserialize(*cb, ec);
178 if (!condition)
179 {
180 JLOG(ctx.j.debug())
181 << "Malformed condition during escrow creation: "
182 << ec.message();
183 return temMALFORMED;
184 }
185
186 // Conditions other than PrefixSha256 require the
187 // "CryptoConditionsSuite" amendment:
188 if (condition->type != Type::preimageSha256 &&
189 !ctx.rules.enabled(featureCryptoConditionsSuite))
190 return temDISABLED;
191 }
192
193 return tesSUCCESS;
194}
195
196template <ValidIssueType T>
197static TER
199 PreclaimContext const& ctx,
200 AccountID const& account,
201 AccountID const& dest,
202 STAmount const& amount);
203
204template <>
207 PreclaimContext const& ctx,
208 AccountID const& account,
209 AccountID const& dest,
210 STAmount const& amount)
211{
212 AccountID issuer = amount.getIssuer();
213 // If the issuer is the same as the account, return tecNO_PERMISSION
214 if (issuer == account)
215 return tecNO_PERMISSION;
216
217 // If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
218 auto const sleIssuer = ctx.view.read(keylet::account(issuer));
219 if (!sleIssuer)
220 return tecNO_ISSUER;
221 if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
222 return tecNO_PERMISSION;
223
224 // If the account does not have a trustline to the issuer, return tecNO_LINE
225 auto const sleRippleState =
226 ctx.view.read(keylet::line(account, issuer, amount.getCurrency()));
227 if (!sleRippleState)
228 return tecNO_LINE;
229
230 STAmount const balance = (*sleRippleState)[sfBalance];
231
232 // If balance is positive, issuer must have higher address than account
233 if (balance > beast::zero && issuer < account)
234 return tecNO_PERMISSION; // LCOV_EXCL_LINE
235
236 // If balance is negative, issuer must have lower address than account
237 if (balance < beast::zero && issuer > account)
238 return tecNO_PERMISSION; // LCOV_EXCL_LINE
239
240 // If the issuer has requireAuth set, check if the account is authorized
241 if (auto const ter = requireAuth(ctx.view, amount.issue(), account);
242 ter != tesSUCCESS)
243 return ter;
244
245 // If the issuer has requireAuth set, check if the destination is authorized
246 if (auto const ter = requireAuth(ctx.view, amount.issue(), dest);
247 ter != tesSUCCESS)
248 return ter;
249
250 // If the issuer has frozen the account, return tecFROZEN
251 if (isFrozen(ctx.view, account, amount.issue()))
252 return tecFROZEN;
253
254 // If the issuer has frozen the destination, return tecFROZEN
255 if (isFrozen(ctx.view, dest, amount.issue()))
256 return tecFROZEN;
257
258 STAmount const spendableAmount = accountHolds(
259 ctx.view,
260 account,
261 amount.getCurrency(),
262 issuer,
264 ctx.j);
265
266 // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
267 if (spendableAmount <= beast::zero)
269
270 // If the spendable amount is less than the amount, return
271 // tecINSUFFICIENT_FUNDS
272 if (spendableAmount < amount)
274
275 // If the amount is not addable to the balance, return tecPRECISION_LOSS
276 if (!canAdd(spendableAmount, amount))
277 return tecPRECISION_LOSS;
278
279 return tesSUCCESS;
280}
281
282template <>
285 PreclaimContext const& ctx,
286 AccountID const& account,
287 AccountID const& dest,
288 STAmount const& amount)
289{
290 AccountID issuer = amount.getIssuer();
291 // If the issuer is the same as the account, return tecNO_PERMISSION
292 if (issuer == account)
293 return tecNO_PERMISSION;
294
295 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
296 auto const issuanceKey =
298 auto const sleIssuance = ctx.view.read(issuanceKey);
299 if (!sleIssuance)
300 return tecOBJECT_NOT_FOUND;
301
302 // If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION
303 if (!sleIssuance->isFlag(lsfMPTCanEscrow))
304 return tecNO_PERMISSION;
305
306 // If the issuer is not the same as the issuer of the mpt, return
307 // tecNO_PERMISSION
308 if (sleIssuance->getAccountID(sfIssuer) != issuer)
309 return tecNO_PERMISSION; // LCOV_EXCL_LINE
310
311 // If the account does not have the mpt, return tecOBJECT_NOT_FOUND
312 if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, account)))
313 return tecOBJECT_NOT_FOUND;
314
315 // If the issuer has requireAuth set, check if the account is
316 // authorized
317 auto const& mptIssue = amount.get<MPTIssue>();
318 if (auto const ter =
319 requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth);
320 ter != tesSUCCESS)
321 return ter;
322
323 // If the issuer has requireAuth set, check if the destination is
324 // authorized
325 if (auto const ter =
326 requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
327 ter != tesSUCCESS)
328 return ter;
329
330 // If the issuer has frozen the account, return tecLOCKED
331 if (isFrozen(ctx.view, account, mptIssue))
332 return tecLOCKED;
333
334 // If the issuer has frozen the destination, return tecLOCKED
335 if (isFrozen(ctx.view, dest, mptIssue))
336 return tecLOCKED;
337
338 // If the mpt cannot be transferred, return tecNO_AUTH
339 if (auto const ter = canTransfer(ctx.view, mptIssue, account, dest);
340 ter != tesSUCCESS)
341 return ter;
342
343 STAmount const spendableAmount = accountHolds(
344 ctx.view,
345 account,
346 amount.get<MPTIssue>(),
349 ctx.j);
350
351 // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
352 if (spendableAmount <= beast::zero)
354
355 // If the spendable amount is less than the amount, return
356 // tecINSUFFICIENT_FUNDS
357 if (spendableAmount < amount)
359
360 return tesSUCCESS;
361}
362
363TER
365{
366 STAmount const amount{ctx.tx[sfAmount]};
367 AccountID const account{ctx.tx[sfAccount]};
368 AccountID const dest{ctx.tx[sfDestination]};
369
370 auto const sled = ctx.view.read(keylet::account(dest));
371 if (!sled)
372 return tecNO_DST;
373
374 // Pseudo-accounts cannot receive escrow. Note, this is not amendment-gated
375 // because all writes to pseudo-account discriminator fields **are**
376 // amendment gated, hence the behaviour of this check will always match the
377 // currently active amendments.
378 if (isPseudoAccount(sled))
379 return tecNO_PERMISSION;
380
381 if (!isXRP(amount))
382 {
383 if (!ctx.view.rules().enabled(featureTokenEscrow))
384 return temDISABLED; // LCOV_EXCL_LINE
385
386 if (auto const ret = std::visit(
387 [&]<typename T>(T const&) {
388 return escrowCreatePreclaimHelper<T>(
389 ctx, account, dest, amount);
390 },
391 amount.asset().value());
392 !isTesSuccess(ret))
393 return ret;
394 }
395 return tesSUCCESS;
396}
397
398template <ValidIssueType T>
399static TER
401 ApplyView& view,
402 AccountID const& issuer,
403 AccountID const& sender,
404 STAmount const& amount,
405 beast::Journal journal);
406
407template <>
410 ApplyView& view,
411 AccountID const& issuer,
412 AccountID const& sender,
413 STAmount const& amount,
414 beast::Journal journal)
415{
416 // Defensive: Issuer cannot create an escrow
417 if (issuer == sender)
418 return tecINTERNAL; // LCOV_EXCL_LINE
419
420 auto const ter = rippleCredit(
421 view,
422 sender,
423 issuer,
424 amount,
425 amount.holds<MPTIssue>() ? false : true,
426 journal);
427 if (ter != tesSUCCESS)
428 return ter; // LCOV_EXCL_LINE
429 return tesSUCCESS;
430}
431
432template <>
435 ApplyView& view,
436 AccountID const& issuer,
437 AccountID const& sender,
438 STAmount const& amount,
439 beast::Journal journal)
440{
441 // Defensive: Issuer cannot create an escrow
442 if (issuer == sender)
443 return tecINTERNAL; // LCOV_EXCL_LINE
444
445 auto const ter = rippleLockEscrowMPT(view, sender, amount, journal);
446 if (ter != tesSUCCESS)
447 return ter; // LCOV_EXCL_LINE
448 return tesSUCCESS;
449}
450
451TER
453{
454 auto const closeTime = ctx_.view().info().parentCloseTime;
455
456 // Prior to fix1571, the cancel and finish times could be greater
457 // than or equal to the parent ledgers' close time.
458 //
459 // With fix1571, we require that they both be strictly greater
460 // than the parent ledgers' close time.
461 if (ctx_.view().rules().enabled(fix1571))
462 {
463 if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter]))
464 return tecNO_PERMISSION;
465
466 if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter]))
467 return tecNO_PERMISSION;
468 }
469 else
470 {
471 // This is old code that needs to stay to support replaying old ledgers,
472 // but does not need to be covered by new tests.
473 // LCOV_EXCL_START
474 if (ctx_.tx[~sfCancelAfter])
475 {
476 auto const cancelAfter = ctx_.tx[sfCancelAfter];
477
478 if (closeTime.time_since_epoch().count() >= cancelAfter)
479 return tecNO_PERMISSION;
480 }
481
482 if (ctx_.tx[~sfFinishAfter])
483 {
484 auto const finishAfter = ctx_.tx[sfFinishAfter];
485
486 if (closeTime.time_since_epoch().count() >= finishAfter)
487 return tecNO_PERMISSION;
488 }
489 // LCOV_EXCL_STOP
490 }
491
492 auto const sle = ctx_.view().peek(keylet::account(account_));
493 if (!sle)
494 return tefINTERNAL; // LCOV_EXCL_LINE
495
496 // Check reserve and funds availability
497 STAmount const amount{ctx_.tx[sfAmount]};
498
499 auto const reserve =
500 ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1);
501
502 if (mSourceBalance < reserve)
504
505 // Check reserve and funds availability
506 if (isXRP(amount))
507 {
508 if (mSourceBalance < reserve + STAmount(amount).xrp())
509 return tecUNFUNDED;
510 }
511
512 // Check destination account
513 {
514 auto const sled =
515 ctx_.view().read(keylet::account(ctx_.tx[sfDestination]));
516 if (!sled)
517 return tecNO_DST; // LCOV_EXCL_LINE
518 if (((*sled)[sfFlags] & lsfRequireDestTag) &&
519 !ctx_.tx[~sfDestinationTag])
520 return tecDST_TAG_NEEDED;
521
522 // Obeying the lsfDisallowXRP flag was a bug. Piggyback on
523 // featureDepositAuth to remove the bug.
524 if (!ctx_.view().rules().enabled(featureDepositAuth) &&
525 ((*sled)[sfFlags] & lsfDisallowXRP))
526 return tecNO_TARGET;
527 }
528
529 // Create escrow in ledger. Note that we we use the value from the
530 // sequence or ticket. For more explanation see comments in SeqProxy.h.
531 Keylet const escrowKeylet = keylet::escrow(account_, ctx_.tx.getSeqValue());
532 auto const slep = std::make_shared<SLE>(escrowKeylet);
533 (*slep)[sfAmount] = amount;
534 (*slep)[sfAccount] = account_;
535 (*slep)[~sfCondition] = ctx_.tx[~sfCondition];
536 (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
537 (*slep)[sfDestination] = ctx_.tx[sfDestination];
538 (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
539 (*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
540 (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
541
542 if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
543 {
544 (*slep)[sfSequence] = ctx_.tx.getSeqValue();
545 }
546
547 if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
548 {
549 auto const xferRate = transferRate(ctx_.view(), amount);
550 if (xferRate != parityRate)
551 (*slep)[sfTransferRate] = xferRate.value;
552 }
553
554 ctx_.view().insert(slep);
555
556 // Add escrow to sender's owner directory
557 {
558 auto page = ctx_.view().dirInsert(
560 escrowKeylet,
562 if (!page)
563 return tecDIR_FULL; // LCOV_EXCL_LINE
564 (*slep)[sfOwnerNode] = *page;
565 }
566
567 // If it's not a self-send, add escrow to recipient's owner directory.
568 AccountID const dest = ctx_.tx[sfDestination];
569 if (dest != account_)
570 {
571 auto page = ctx_.view().dirInsert(
572 keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
573 if (!page)
574 return tecDIR_FULL; // LCOV_EXCL_LINE
575 (*slep)[sfDestinationNode] = *page;
576 }
577
578 // IOU escrow objects are added to the issuer's owner directory to help
579 // track the total locked balance. For MPT, this isn't necessary because the
580 // locked balance is already stored directly in the MPTokenIssuance object.
581 AccountID const issuer = amount.getIssuer();
582 if (!isXRP(amount) && issuer != account_ && issuer != dest &&
583 !amount.holds<MPTIssue>())
584 {
585 auto page = ctx_.view().dirInsert(
586 keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
587 if (!page)
588 return tecDIR_FULL; // LCOV_EXCL_LINE
589 (*slep)[sfIssuerNode] = *page;
590 }
591
592 // Deduct owner's balance
593 if (isXRP(amount))
594 (*sle)[sfBalance] = (*sle)[sfBalance] - amount;
595 else
596 {
597 if (auto const ret = std::visit(
598 [&]<typename T>(T const&) {
599 return escrowLockApplyHelper<T>(
600 ctx_.view(), issuer, account_, amount, j_);
601 },
602 amount.asset().value());
603 !isTesSuccess(ret))
604 {
605 return ret; // LCOV_EXCL_LINE
606 }
607 }
608
609 // increment owner count
611 ctx_.view().update(sle);
612 return tesSUCCESS;
613}
614
615//------------------------------------------------------------------------------
616
617static bool
619{
620 using namespace ripple::cryptoconditions;
621
623
624 auto condition = Condition::deserialize(c, ec);
625 if (!condition)
626 return false;
627
628 auto fulfillment = Fulfillment::deserialize(f, ec);
629 if (!fulfillment)
630 return false;
631
632 return validate(*fulfillment, *condition);
633}
634
635bool
637{
638 return !ctx.tx.isFieldPresent(sfCredentialIDs) ||
639 ctx.rules.enabled(featureCredentials);
640}
641
644{
645 // 0 means "Allow any flags"
646 return ctx.rules.enabled(fix1543) ? tfUniversalMask : 0;
647}
648
649NotTEC
651{
652 auto const cb = ctx.tx[~sfCondition];
653 auto const fb = ctx.tx[~sfFulfillment];
654
655 // If you specify a condition, then you must also specify
656 // a fulfillment.
657 if (static_cast<bool>(cb) != static_cast<bool>(fb))
658 return temMALFORMED;
659
660 return tesSUCCESS;
661}
662
663NotTEC
665{
666 auto const cb = ctx.tx[~sfCondition];
667 auto const fb = ctx.tx[~sfFulfillment];
668
669 if (cb && fb)
670 {
671 auto& router = ctx.app.getHashRouter();
672
673 auto const id = ctx.tx.getTransactionID();
674 auto const flags = router.getFlags(id);
675
676 // If we haven't checked the condition, check it
677 // now. Whether it passes or not isn't important
678 // in preflight.
679 if (!any(flags & (SF_CF_INVALID | SF_CF_VALID)))
680 {
681 if (checkCondition(*fb, *cb))
682 router.setFlags(id, SF_CF_VALID);
683 else
684 router.setFlags(id, SF_CF_INVALID);
685 }
686 }
687
688 if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
689 !isTesSuccess(err))
690 return err;
691
692 return tesSUCCESS;
693}
694
697{
698 XRPAmount extraFee{0};
699
700 if (auto const fb = tx[~sfFulfillment])
701 {
702 extraFee += view.fees().base * (32 + (fb->size() / 16));
703 }
704
705 return Transactor::calculateBaseFee(view, tx) + extraFee;
706}
707
708template <ValidIssueType T>
709static TER
711 PreclaimContext const& ctx,
712 AccountID const& dest,
713 STAmount const& amount);
714
715template <>
718 PreclaimContext const& ctx,
719 AccountID const& dest,
720 STAmount const& amount)
721{
722 AccountID issuer = amount.getIssuer();
723 // If the issuer is the same as the account, return tesSUCCESS
724 if (issuer == dest)
725 return tesSUCCESS;
726
727 // If the issuer has requireAuth set, check if the destination is authorized
728 if (auto const ter = requireAuth(ctx.view, amount.issue(), dest);
729 ter != tesSUCCESS)
730 return ter;
731
732 // If the issuer has deep frozen the destination, return tecFROZEN
733 if (isDeepFrozen(ctx.view, dest, amount.getCurrency(), amount.getIssuer()))
734 return tecFROZEN;
735
736 return tesSUCCESS;
737}
738
739template <>
742 PreclaimContext const& ctx,
743 AccountID const& dest,
744 STAmount const& amount)
745{
746 AccountID issuer = amount.getIssuer();
747 // If the issuer is the same as the dest, return tesSUCCESS
748 if (issuer == dest)
749 return tesSUCCESS;
750
751 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
752 auto const issuanceKey =
754 auto const sleIssuance = ctx.view.read(issuanceKey);
755 if (!sleIssuance)
756 return tecOBJECT_NOT_FOUND;
757
758 // If the issuer has requireAuth set, check if the destination is
759 // authorized
760 auto const& mptIssue = amount.get<MPTIssue>();
761 if (auto const ter =
762 requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
763 ter != tesSUCCESS)
764 return ter;
765
766 // If the issuer has frozen the destination, return tecLOCKED
767 if (isFrozen(ctx.view, dest, mptIssue))
768 return tecLOCKED;
769
770 return tesSUCCESS;
771}
772
773TER
775{
776 if (ctx.view.rules().enabled(featureCredentials))
777 {
778 if (auto const err =
779 credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
780 !isTesSuccess(err))
781 return err;
782 }
783
784 if (ctx.view.rules().enabled(featureTokenEscrow))
785 {
786 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
787 auto const slep = ctx.view.read(k);
788 if (!slep)
789 return tecNO_TARGET;
790
791 AccountID const dest = (*slep)[sfDestination];
792 STAmount const amount = (*slep)[sfAmount];
793
794 if (!isXRP(amount))
795 {
796 if (auto const ret = std::visit(
797 [&]<typename T>(T const&) {
798 return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
799 },
800 amount.asset().value());
801 !isTesSuccess(ret))
802 return ret;
803 }
804 }
805 return tesSUCCESS;
806}
807
808template <ValidIssueType T>
809static TER
811 ApplyView& view,
812 Rate lockedRate,
813 std::shared_ptr<SLE> const& sleDest,
814 STAmount const& xrpBalance,
815 STAmount const& amount,
816 AccountID const& issuer,
817 AccountID const& sender,
818 AccountID const& receiver,
819 bool createAsset,
820 beast::Journal journal);
821
822template <>
825 ApplyView& view,
826 Rate lockedRate,
827 std::shared_ptr<SLE> const& sleDest,
828 STAmount const& xrpBalance,
829 STAmount const& amount,
830 AccountID const& issuer,
831 AccountID const& sender,
832 AccountID const& receiver,
833 bool createAsset,
834 beast::Journal journal)
835{
836 Keylet const trustLineKey = keylet::line(receiver, amount.issue());
837 bool const recvLow = issuer > receiver;
838 bool const senderIssuer = issuer == sender;
839 bool const receiverIssuer = issuer == receiver;
840 bool const issuerHigh = issuer > receiver;
841
842 if (senderIssuer)
843 return tecINTERNAL; // LCOV_EXCL_LINE
844
845 if (receiverIssuer)
846 return tesSUCCESS;
847
848 if (!view.exists(trustLineKey) && createAsset && !receiverIssuer)
849 {
850 // Can the account cover the trust line's reserve?
851 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
852 xrpBalance < view.fees().accountReserve(ownerCount + 1))
853 {
854 JLOG(journal.trace()) << "Trust line does not exist. "
855 "Insufficent reserve to create line.";
856
858 }
859
860 Currency const currency = amount.getCurrency();
861 STAmount initialBalance(amount.issue());
862 initialBalance.setIssuer(noAccount());
863
864 // clang-format off
865 if (TER const ter = trustCreate(
866 view, // payment sandbox
867 recvLow, // is dest low?
868 issuer, // source
869 receiver, // destination
870 trustLineKey.key, // ledger index
871 sleDest, // Account to add to
872 false, // authorize account
873 (sleDest->getFlags() & lsfDefaultRipple) == 0,
874 false, // freeze trust line
875 false, // deep freeze trust line
876 initialBalance, // zero initial balance
877 Issue(currency, receiver), // limit of zero
878 0, // quality in
879 0, // quality out
880 journal); // journal
881 !isTesSuccess(ter))
882 {
883 return ter; // LCOV_EXCL_LINE
884 }
885 // clang-format on
886
887 view.update(sleDest);
888 }
889
890 if (!view.exists(trustLineKey) && !receiverIssuer)
891 return tecNO_LINE;
892
893 auto const xferRate = transferRate(view, amount);
894 // update if issuer rate is less than locked rate
895 if (xferRate < lockedRate)
896 lockedRate = xferRate;
897
898 // Transfer Rate only applies when:
899 // 1. Issuer is not involved in the transfer (senderIssuer or
900 // receiverIssuer)
901 // 2. The locked rate is different from the parity rate
902
903 // NOTE: Transfer fee in escrow works a bit differently from a normal
904 // payment. In escrow, the fee is deducted from the locked/sending amount,
905 // whereas in a normal payment, the transfer fee is taken on top of the
906 // sending amount.
907 auto finalAmt = amount;
908 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
909 {
910 // compute transfer fee, if any
911 auto const xferFee = amount.value() -
912 divideRound(amount, lockedRate, amount.issue(), true);
913 // compute balance to transfer
914 finalAmt = amount.value() - xferFee;
915 }
916
917 // validate the line limit if the account submitting txn is not the receiver
918 // of the funds
919 if (!createAsset)
920 {
921 auto const sleRippleState = view.peek(trustLineKey);
922 if (!sleRippleState)
923 return tecINTERNAL; // LCOV_EXCL_LINE
924
925 // if the issuer is the high, then we use the low limit
926 // otherwise we use the high limit
927 STAmount const lineLimit = sleRippleState->getFieldAmount(
928 issuerHigh ? sfLowLimit : sfHighLimit);
929
930 STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance);
931
932 // flip the sign of the line balance if the issuer is not high
933 if (!issuerHigh)
934 lineBalance.negate();
935
936 // add the final amount to the line balance
937 lineBalance += finalAmt;
938
939 // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED
940 if (lineLimit < lineBalance)
941 return tecLIMIT_EXCEEDED;
942 }
943
944 // if destination is not the issuer then transfer funds
945 if (!receiverIssuer)
946 {
947 auto const ter =
948 rippleCredit(view, issuer, receiver, finalAmt, true, journal);
949 if (ter != tesSUCCESS)
950 return ter; // LCOV_EXCL_LINE
951 }
952 return tesSUCCESS;
953}
954
955template <>
958 ApplyView& view,
959 Rate lockedRate,
960 std::shared_ptr<SLE> const& sleDest,
961 STAmount const& xrpBalance,
962 STAmount const& amount,
963 AccountID const& issuer,
964 AccountID const& sender,
965 AccountID const& receiver,
966 bool createAsset,
967 beast::Journal journal)
968{
969 bool const senderIssuer = issuer == sender;
970 bool const receiverIssuer = issuer == receiver;
971
972 auto const mptID = amount.get<MPTIssue>().getMptID();
973 auto const issuanceKey = keylet::mptIssuance(mptID);
974 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
975 createAsset && !receiverIssuer)
976 {
977 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
978 xrpBalance < view.fees().accountReserve(ownerCount + 1))
979 {
981 }
982
983 if (auto const ter =
984 MPTokenAuthorize::createMPToken(view, mptID, receiver, 0);
985 !isTesSuccess(ter))
986 {
987 return ter; // LCOV_EXCL_LINE
988 }
989
990 // update owner count.
991 adjustOwnerCount(view, sleDest, 1, journal);
992 }
993
994 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
995 !receiverIssuer)
996 return tecNO_PERMISSION;
997
998 auto const xferRate = transferRate(view, amount);
999 // update if issuer rate is less than locked rate
1000 if (xferRate < lockedRate)
1001 lockedRate = xferRate;
1002
1003 // Transfer Rate only applies when:
1004 // 1. Issuer is not involved in the transfer (senderIssuer or
1005 // receiverIssuer)
1006 // 2. The locked rate is different from the parity rate
1007
1008 // NOTE: Transfer fee in escrow works a bit differently from a normal
1009 // payment. In escrow, the fee is deducted from the locked/sending amount,
1010 // whereas in a normal payment, the transfer fee is taken on top of the
1011 // sending amount.
1012 auto finalAmt = amount;
1013 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
1014 {
1015 // compute transfer fee, if any
1016 auto const xferFee = amount.value() -
1017 divideRound(amount, lockedRate, amount.asset(), true);
1018 // compute balance to transfer
1019 finalAmt = amount.value() - xferFee;
1020 }
1021 return rippleUnlockEscrowMPT(
1022 view,
1023 sender,
1024 receiver,
1025 finalAmt,
1026 view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
1027 journal);
1028}
1029
1030TER
1032{
1033 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
1034 auto const slep = ctx_.view().peek(k);
1035 if (!slep)
1036 {
1037 if (ctx_.view().rules().enabled(featureTokenEscrow))
1038 return tecINTERNAL; // LCOV_EXCL_LINE
1039
1040 return tecNO_TARGET;
1041 }
1042
1043 // If a cancel time is present, a finish operation should only succeed prior
1044 // to that time. fix1571 corrects a logic error in the check that would make
1045 // a finish only succeed strictly after the cancel time.
1046 if (ctx_.view().rules().enabled(fix1571))
1047 {
1048 auto const now = ctx_.view().info().parentCloseTime;
1049
1050 // Too soon: can't execute before the finish time
1051 if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
1052 return tecNO_PERMISSION;
1053
1054 // Too late: can't execute after the cancel time
1055 if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
1056 return tecNO_PERMISSION;
1057 }
1058 else
1059 {
1060 // This is old code that needs to stay to support replaying old ledgers,
1061 // but does not need to be covered by new tests.
1062 // LCOV_EXCL_START
1063 // Too soon?
1064 if ((*slep)[~sfFinishAfter] &&
1066 (*slep)[sfFinishAfter])
1067 return tecNO_PERMISSION;
1068
1069 // Too late?
1070 if ((*slep)[~sfCancelAfter] &&
1072 (*slep)[sfCancelAfter])
1073 return tecNO_PERMISSION;
1074 // LCOV_EXCL_STOP
1075 }
1076
1077 // Check cryptocondition fulfillment
1078 {
1079 auto const id = ctx_.tx.getTransactionID();
1080 auto flags = ctx_.app.getHashRouter().getFlags(id);
1081
1082 auto const cb = ctx_.tx[~sfCondition];
1083
1084 // It's unlikely that the results of the check will
1085 // expire from the hash router, but if it happens,
1086 // simply re-run the check.
1087 if (cb && !any(flags & (SF_CF_INVALID | SF_CF_VALID)))
1088 {
1089 // LCOV_EXCL_START
1090 auto const fb = ctx_.tx[~sfFulfillment];
1091
1092 if (!fb)
1093 return tecINTERNAL;
1094
1095 if (checkCondition(*fb, *cb))
1096 flags = SF_CF_VALID;
1097 else
1098 flags = SF_CF_INVALID;
1099
1100 ctx_.app.getHashRouter().setFlags(id, flags);
1101 // LCOV_EXCL_STOP
1102 }
1103
1104 // If the check failed, then simply return an error
1105 // and don't look at anything else.
1106 if (any(flags & SF_CF_INVALID))
1108
1109 // Check against condition in the ledger entry:
1110 auto const cond = (*slep)[~sfCondition];
1111
1112 // If a condition wasn't specified during creation,
1113 // one shouldn't be included now.
1114 if (!cond && cb)
1116
1117 // If a condition was specified during creation of
1118 // the suspended payment, the identical condition
1119 // must be presented again. We don't check if the
1120 // fulfillment matches the condition since we did
1121 // that in preflight.
1122 if (cond && (cond != cb))
1124 }
1125
1126 // NOTE: Escrow payments cannot be used to fund accounts.
1127 AccountID const destID = (*slep)[sfDestination];
1128 auto const sled = ctx_.view().peek(keylet::account(destID));
1129 if (!sled)
1130 return tecNO_DST;
1131
1132 if (ctx_.view().rules().enabled(featureDepositAuth))
1133 {
1134 if (auto err = verifyDepositPreauth(
1135 ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
1136 !isTesSuccess(err))
1137 return err;
1138 }
1139
1140 AccountID const account = (*slep)[sfAccount];
1141
1142 // Remove escrow from owner directory
1143 {
1144 auto const page = (*slep)[sfOwnerNode];
1145 if (!ctx_.view().dirRemove(
1146 keylet::ownerDir(account), page, k.key, true))
1147 {
1148 // LCOV_EXCL_START
1149 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1150 return tefBAD_LEDGER;
1151 // LCOV_EXCL_STOP
1152 }
1153 }
1154
1155 // Remove escrow from recipient's owner directory, if present.
1156 if (auto const optPage = (*slep)[~sfDestinationNode])
1157 {
1158 if (!ctx_.view().dirRemove(
1159 keylet::ownerDir(destID), *optPage, k.key, true))
1160 {
1161 // LCOV_EXCL_START
1162 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1163 return tefBAD_LEDGER;
1164 // LCOV_EXCL_STOP
1165 }
1166 }
1167
1168 STAmount const amount = slep->getFieldAmount(sfAmount);
1169 // Transfer amount to destination
1170 if (isXRP(amount))
1171 (*sled)[sfBalance] = (*sled)[sfBalance] + amount;
1172 else
1173 {
1174 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1175 return temDISABLED; // LCOV_EXCL_LINE
1176
1177 Rate lockedRate = slep->isFieldPresent(sfTransferRate)
1178 ? ripple::Rate(slep->getFieldU32(sfTransferRate))
1179 : parityRate;
1180 auto const issuer = amount.getIssuer();
1181 bool const createAsset = destID == account_;
1182 if (auto const ret = std::visit(
1183 [&]<typename T>(T const&) {
1184 return escrowUnlockApplyHelper<T>(
1185 ctx_.view(),
1186 lockedRate,
1187 sled,
1189 amount,
1190 issuer,
1191 account,
1192 destID,
1193 createAsset,
1194 j_);
1195 },
1196 amount.asset().value());
1197 !isTesSuccess(ret))
1198 return ret;
1199
1200 // Remove escrow from issuers owner directory, if present.
1201 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1202 {
1203 if (!ctx_.view().dirRemove(
1204 keylet::ownerDir(issuer), *optPage, k.key, true))
1205 {
1206 // LCOV_EXCL_START
1207 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1208 return tefBAD_LEDGER;
1209 // LCOV_EXCL_STOP
1210 }
1211 }
1212 }
1213
1214 ctx_.view().update(sled);
1215
1216 // Adjust source owner count
1217 auto const sle = ctx_.view().peek(keylet::account(account));
1218 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1219 ctx_.view().update(sle);
1220
1221 // Remove escrow from ledger
1222 ctx_.view().erase(slep);
1223 return tesSUCCESS;
1224}
1225
1226//------------------------------------------------------------------------------
1227
1230{
1231 // 0 means "Allow any flags"
1232 return ctx.rules.enabled(fix1543) ? tfUniversalMask : 0;
1233}
1234
1235NotTEC
1237{
1238 return tesSUCCESS;
1239}
1240
1241template <ValidIssueType T>
1242static TER
1244 PreclaimContext const& ctx,
1245 AccountID const& account,
1246 STAmount const& amount);
1247
1248template <>
1251 PreclaimContext const& ctx,
1252 AccountID const& account,
1253 STAmount const& amount)
1254{
1255 AccountID issuer = amount.getIssuer();
1256 // If the issuer is the same as the account, return tecINTERNAL
1257 if (issuer == account)
1258 return tecINTERNAL; // LCOV_EXCL_LINE
1259
1260 // If the issuer has requireAuth set, check if the account is authorized
1261 if (auto const ter = requireAuth(ctx.view, amount.issue(), account);
1262 ter != tesSUCCESS)
1263 return ter;
1264
1265 return tesSUCCESS;
1266}
1267
1268template <>
1271 PreclaimContext const& ctx,
1272 AccountID const& account,
1273 STAmount const& amount)
1274{
1275 AccountID issuer = amount.getIssuer();
1276 // If the issuer is the same as the account, return tecINTERNAL
1277 if (issuer == account)
1278 return tecINTERNAL; // LCOV_EXCL_LINE
1279
1280 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
1281 auto const issuanceKey =
1283 auto const sleIssuance = ctx.view.read(issuanceKey);
1284 if (!sleIssuance)
1285 return tecOBJECT_NOT_FOUND;
1286
1287 // If the issuer has requireAuth set, check if the account is
1288 // authorized
1289 auto const& mptIssue = amount.get<MPTIssue>();
1290 if (auto const ter =
1291 requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth);
1292 ter != tesSUCCESS)
1293 return ter;
1294
1295 return tesSUCCESS;
1296}
1297
1298TER
1300{
1301 if (ctx.view.rules().enabled(featureTokenEscrow))
1302 {
1303 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
1304 auto const slep = ctx.view.read(k);
1305 if (!slep)
1306 return tecNO_TARGET;
1307
1308 AccountID const account = (*slep)[sfAccount];
1309 STAmount const amount = (*slep)[sfAmount];
1310
1311 if (!isXRP(amount))
1312 {
1313 if (auto const ret = std::visit(
1314 [&]<typename T>(T const&) {
1315 return escrowCancelPreclaimHelper<T>(
1316 ctx, account, amount);
1317 },
1318 amount.asset().value());
1319 !isTesSuccess(ret))
1320 return ret;
1321 }
1322 }
1323 return tesSUCCESS;
1324}
1325
1326TER
1328{
1329 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
1330 auto const slep = ctx_.view().peek(k);
1331 if (!slep)
1332 {
1333 if (ctx_.view().rules().enabled(featureTokenEscrow))
1334 return tecINTERNAL; // LCOV_EXCL_LINE
1335
1336 return tecNO_TARGET;
1337 }
1338
1339 if (ctx_.view().rules().enabled(fix1571))
1340 {
1341 auto const now = ctx_.view().info().parentCloseTime;
1342
1343 // No cancel time specified: can't execute at all.
1344 if (!(*slep)[~sfCancelAfter])
1345 return tecNO_PERMISSION;
1346
1347 // Too soon: can't execute before the cancel time.
1348 if (!after(now, (*slep)[sfCancelAfter]))
1349 return tecNO_PERMISSION;
1350 }
1351 else
1352 {
1353 // This is old code that needs to stay to support replaying old ledgers,
1354 // but does not need to be covered by new tests.
1355 // LCOV_EXCL_START
1356 // Too soon?
1357 if (!(*slep)[~sfCancelAfter] ||
1359 (*slep)[sfCancelAfter])
1360 return tecNO_PERMISSION;
1361 // LCOV_EXCL_STOP
1362 }
1363
1364 AccountID const account = (*slep)[sfAccount];
1365
1366 // Remove escrow from owner directory
1367 {
1368 auto const page = (*slep)[sfOwnerNode];
1369 if (!ctx_.view().dirRemove(
1370 keylet::ownerDir(account), page, k.key, true))
1371 {
1372 // LCOV_EXCL_START
1373 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1374 return tefBAD_LEDGER;
1375 // LCOV_EXCL_STOP
1376 }
1377 }
1378
1379 // Remove escrow from recipient's owner directory, if present.
1380 if (auto const optPage = (*slep)[~sfDestinationNode]; optPage)
1381 {
1382 if (!ctx_.view().dirRemove(
1383 keylet::ownerDir((*slep)[sfDestination]),
1384 *optPage,
1385 k.key,
1386 true))
1387 {
1388 // LCOV_EXCL_START
1389 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1390 return tefBAD_LEDGER;
1391 // LCOV_EXCL_STOP
1392 }
1393 }
1394
1395 auto const sle = ctx_.view().peek(keylet::account(account));
1396 STAmount const amount = slep->getFieldAmount(sfAmount);
1397
1398 // Transfer amount back to the owner
1399 if (isXRP(amount))
1400 (*sle)[sfBalance] = (*sle)[sfBalance] + amount;
1401 else
1402 {
1403 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1404 return temDISABLED; // LCOV_EXCL_LINE
1405
1406 auto const issuer = amount.getIssuer();
1407 bool const createAsset = account == account_;
1408 if (auto const ret = std::visit(
1409 [&]<typename T>(T const&) {
1410 return escrowUnlockApplyHelper<T>(
1411 ctx_.view(),
1412 parityRate,
1413 slep,
1415 amount,
1416 issuer,
1417 account, // sender and receiver are the same
1418 account,
1419 createAsset,
1420 j_);
1421 },
1422 amount.asset().value());
1423 !isTesSuccess(ret))
1424 return ret; // LCOV_EXCL_LINE
1425
1426 // Remove escrow from issuers owner directory, if present.
1427 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1428 {
1429 if (!ctx_.view().dirRemove(
1430 keylet::ownerDir(issuer), *optPage, k.key, true))
1431 {
1432 // LCOV_EXCL_START
1433 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1434 return tefBAD_LEDGER;
1435 // LCOV_EXCL_STOP
1436 }
1437 }
1438 }
1439
1440 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1441 ctx_.view().update(sle);
1442
1443 // Remove escrow from ledger
1444 ctx_.view().erase(slep);
1445
1446 return tesSUCCESS;
1447}
1448
1449} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:60
Stream fatal() const
Definition Journal.h:352
Stream debug() const
Definition Journal.h:328
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
virtual HashRouter & getHashRouter()=0
ApplyView & view()
Application & app
beast::Journal const journal
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:143
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition ApplyView.h:319
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
constexpr value_type const & value() const
Definition Asset.h:156
TER doApply() override
Definition Escrow.cpp:1327
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition Escrow.cpp:1229
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:1236
static TER preclaim(PreclaimContext const &ctx)
Definition Escrow.cpp:1299
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition Escrow.cpp:122
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:129
TER doApply() override
Definition Escrow.cpp:452
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
Definition Escrow.cpp:82
static TER preclaim(PreclaimContext const &ctx)
Definition Escrow.cpp:364
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition Escrow.cpp:643
static TER preclaim(PreclaimContext const &ctx)
Definition Escrow.cpp:774
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:650
static NotTEC preflightSigValidated(PreflightContext const &ctx)
Definition Escrow.cpp:664
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition Escrow.cpp:636
TER doApply() override
Definition Escrow.cpp:1031
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition Escrow.cpp:696
HashRouterFlags getFlags(uint256 const &key)
bool setFlags(uint256 const &key, HashRouterFlags flags)
Set the flags on a hash.
A currency issued by an account.
Definition Issue.h:33
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:46
static TER createMPToken(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, std::uint32_t const flags)
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 Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
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
Asset const & asset() const
Definition STAmount.h:483
constexpr TIss const & get() const
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:588
Currency const & getCurrency() const
Definition STAmount.h:502
STAmount const & value() const noexcept
Definition STAmount.h:594
AccountID const & getIssuer() const
Definition STAmount.h:508
Issue const & issue() const
Definition STAmount.h:496
bool native() const noexcept
Definition STAmount.h:458
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
std::uint32_t getSeqValue() const
Returns the first non-zero value of (Sequence, TicketSequence).
Definition STTx.cpp:231
uint256 getTransactionID() const
Definition STTx.h:219
An immutable linear range of bytes.
Definition Slice.h:46
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
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
T is_same_v
T message(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 mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:540
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:244
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:526
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition Indexes.cpp:389
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:374
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
AccountID const & noAccount()
A placeholder for empty accounts.
TER escrowFinishPreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:717
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
@ fhIGNORE_FREEZE
Definition View.h:77
TER escrowCreatePreclaimHelper< MPTIssue >(PreclaimContext const &ctx, AccountID const &account, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:284
bool isXRP(AccountID const &c)
Definition AccountID.h:90
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2989
static TER escrowFinishPreclaimHelper(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
Definition STAmount.cpp:528
TER escrowLockApplyHelper< Issue >(ApplyView &view, AccountID const &issuer, AccountID const &sender, STAmount const &amount, beast::Journal journal)
Definition Escrow.cpp:409
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:350
@ lsfRequireDestTag
@ lsfMPTCanEscrow
@ lsfDefaultRipple
@ lsfAllowTrustLineLocking
TER escrowCancelPreclaimHelper< MPTIssue >(PreclaimContext const &ctx, AccountID const &account, STAmount const &amount)
Definition Escrow.cpp:1270
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:1032
static TER escrowLockApplyHelper(ApplyView &view, AccountID const &issuer, AccountID const &sender, STAmount const &amount, beast::Journal journal)
@ ahIGNORE_AUTH
Definition View.h:80
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1050
STAmount divideRound(STAmount const &amount, Rate const &rate, bool roundUp)
Definition Rate2.cpp:104
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst, beast::Journal j)
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
Definition View.cpp:3086
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
constexpr HashRouterFlags SF_CF_VALID
Definition Escrow.cpp:43
@ tefBAD_LEDGER
Definition TER.h:170
@ tefINTERNAL
Definition TER.h:173
HashRouterFlags
Definition HashRouter.h:34
TER escrowCreatePreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &account, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:206
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
static bool checkCondition(Slice f, Slice c)
Definition Escrow.cpp:618
TER escrowUnlockApplyHelper< MPTIssue >(ApplyView &view, Rate lockedRate, std::shared_ptr< SLE > const &sleDest, STAmount const &xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
Definition Escrow.cpp:957
static NotTEC escrowCreatePreflightHelper(PreflightContext const &ctx)
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:762
static TER escrowCancelPreclaimHelper(PreclaimContext const &ctx, AccountID const &account, STAmount const &amount)
@ tecCRYPTOCONDITION_ERROR
Definition TER.h:312
@ tecNO_DST
Definition TER.h:290
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:292
@ tecLIMIT_EXCEEDED
Definition TER.h:361
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecNO_ISSUER
Definition TER.h:299
@ tecUNFUNDED
Definition TER.h:295
@ tecNO_TARGET
Definition TER.h:304
@ tecDIR_FULL
Definition TER.h:287
@ tecFROZEN
Definition TER.h:303
@ tecINSUFFICIENT_FUNDS
Definition TER.h:325
@ tecINTERNAL
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:305
@ tecDST_TAG_NEEDED
Definition TER.h:309
@ tecPRECISION_LOSS
Definition TER.h:363
@ tecNO_LINE
Definition TER.h:301
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tecLOCKED
Definition TER.h:358
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:2850
@ tesSUCCESS
Definition TER.h:244
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:387
constexpr HashRouterFlags SF_CF_INVALID
Definition Escrow.cpp:42
NotTEC escrowCreatePreflightHelper< MPTIssue >(PreflightContext const &ctx)
Definition Escrow.cpp:108
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
TER escrowFinishPreclaimHelper< MPTIssue >(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:741
NotTEC escrowCreatePreflightHelper< Issue >(PreflightContext const &ctx)
Definition Escrow.cpp:94
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3260
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1392
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
TER escrowCancelPreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &account, STAmount const &amount)
Definition Escrow.cpp:1250
TER escrowLockApplyHelper< MPTIssue >(ApplyView &view, AccountID const &issuer, AccountID const &sender, STAmount const &amount, beast::Journal journal)
Definition Escrow.cpp:434
static TER escrowUnlockApplyHelper(ApplyView &view, Rate lockedRate, std::shared_ptr< SLE > const &sleDest, STAmount const &xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1118
TER escrowUnlockApplyHelper< Issue >(ApplyView &view, Rate lockedRate, std::shared_ptr< SLE > const &sleDest, STAmount const &xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
Definition Escrow.cpp:824
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
static TER escrowCreatePreclaimHelper(PreclaimContext const &ctx, AccountID const &account, AccountID const &dest, STAmount const &amount)
@ temBAD_AMOUNT
Definition TER.h:89
@ temBAD_CURRENCY
Definition TER.h:90
@ temMALFORMED
Definition TER.h:87
@ temBAD_EXPIRATION
Definition TER.h:91
@ temDISABLED
Definition TER.h:114
XRPAmount base
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:39
uint256 key
Definition Keylet.h:40
NetClock::time_point parentCloseTime
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
T time_since_epoch(T... args)
T visit(T... args)