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