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
464 // Create escrow in ledger. Note that we we use the value from the
465 // sequence or ticket. For more explanation see comments in SeqProxy.h.
466 Keylet const escrowKeylet = keylet::escrow(account_, ctx_.tx.getSeqValue());
467 auto const slep = std::make_shared<SLE>(escrowKeylet);
468 (*slep)[sfAmount] = amount;
469 (*slep)[sfAccount] = account_;
470 (*slep)[~sfCondition] = ctx_.tx[~sfCondition];
471 (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
472 (*slep)[sfDestination] = ctx_.tx[sfDestination];
473 (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
474 (*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
475 (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
476
477 if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
478 {
479 (*slep)[sfSequence] = ctx_.tx.getSeqValue();
480 }
481
482 if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
483 {
484 auto const xferRate = transferRate(ctx_.view(), amount);
485 if (xferRate != parityRate)
486 (*slep)[sfTransferRate] = xferRate.value;
487 }
488
489 ctx_.view().insert(slep);
490
491 // Add escrow to sender's owner directory
492 {
493 auto page = ctx_.view().dirInsert(
495 escrowKeylet,
497 if (!page)
498 return tecDIR_FULL; // LCOV_EXCL_LINE
499 (*slep)[sfOwnerNode] = *page;
500 }
501
502 // If it's not a self-send, add escrow to recipient's owner directory.
503 AccountID const dest = ctx_.tx[sfDestination];
504 if (dest != account_)
505 {
506 auto page = ctx_.view().dirInsert(
507 keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
508 if (!page)
509 return tecDIR_FULL; // LCOV_EXCL_LINE
510 (*slep)[sfDestinationNode] = *page;
511 }
512
513 // IOU escrow objects are added to the issuer's owner directory to help
514 // track the total locked balance. For MPT, this isn't necessary because the
515 // locked balance is already stored directly in the MPTokenIssuance object.
516 AccountID const issuer = amount.getIssuer();
517 if (!isXRP(amount) && issuer != account_ && issuer != dest &&
518 !amount.holds<MPTIssue>())
519 {
520 auto page = ctx_.view().dirInsert(
521 keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
522 if (!page)
523 return tecDIR_FULL; // LCOV_EXCL_LINE
524 (*slep)[sfIssuerNode] = *page;
525 }
526
527 // Deduct owner's balance
528 if (isXRP(amount))
529 (*sle)[sfBalance] = (*sle)[sfBalance] - amount;
530 else
531 {
532 if (auto const ret = std::visit(
533 [&]<typename T>(T const&) {
534 return escrowLockApplyHelper<T>(
535 ctx_.view(), issuer, account_, amount, j_);
536 },
537 amount.asset().value());
538 !isTesSuccess(ret))
539 {
540 return ret; // LCOV_EXCL_LINE
541 }
542 }
543
544 // increment owner count
546 ctx_.view().update(sle);
547 return tesSUCCESS;
548}
549
550//------------------------------------------------------------------------------
551
552static bool
554{
555 using namespace ripple::cryptoconditions;
556
558
559 auto condition = Condition::deserialize(c, ec);
560 if (!condition)
561 return false;
562
563 auto fulfillment = Fulfillment::deserialize(f, ec);
564 if (!fulfillment)
565 return false;
566
567 return validate(*fulfillment, *condition);
568}
569
570bool
572{
573 return !ctx.tx.isFieldPresent(sfCredentialIDs) ||
574 ctx.rules.enabled(featureCredentials);
575}
576
577NotTEC
579{
580 auto const cb = ctx.tx[~sfCondition];
581 auto const fb = ctx.tx[~sfFulfillment];
582
583 // If you specify a condition, then you must also specify
584 // a fulfillment.
585 if (static_cast<bool>(cb) != static_cast<bool>(fb))
586 return temMALFORMED;
587
588 return tesSUCCESS;
589}
590
591NotTEC
593{
594 auto const cb = ctx.tx[~sfCondition];
595 auto const fb = ctx.tx[~sfFulfillment];
596
597 if (cb && fb)
598 {
599 auto& router = ctx.app.getHashRouter();
600
601 auto const id = ctx.tx.getTransactionID();
602 auto const flags = router.getFlags(id);
603
604 // If we haven't checked the condition, check it
605 // now. Whether it passes or not isn't important
606 // in preflight.
607 if (!any(flags & (SF_CF_INVALID | SF_CF_VALID)))
608 {
609 if (checkCondition(*fb, *cb))
610 router.setFlags(id, SF_CF_VALID);
611 else
612 router.setFlags(id, SF_CF_INVALID);
613 }
614 }
615
616 if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
617 !isTesSuccess(err))
618 return err;
619
620 return tesSUCCESS;
621}
622
625{
626 XRPAmount extraFee{0};
627
628 if (auto const fb = tx[~sfFulfillment])
629 {
630 extraFee += view.fees().base * (32 + (fb->size() / 16));
631 }
632
633 return Transactor::calculateBaseFee(view, tx) + extraFee;
634}
635
636template <ValidIssueType T>
637static TER
639 PreclaimContext const& ctx,
640 AccountID const& dest,
641 STAmount const& amount);
642
643template <>
646 PreclaimContext const& ctx,
647 AccountID const& dest,
648 STAmount const& amount)
649{
650 AccountID issuer = amount.getIssuer();
651 // If the issuer is the same as the account, return tesSUCCESS
652 if (issuer == dest)
653 return tesSUCCESS;
654
655 // If the issuer has requireAuth set, check if the destination is authorized
656 if (auto const ter = requireAuth(ctx.view, amount.issue(), dest);
657 ter != tesSUCCESS)
658 return ter;
659
660 // If the issuer has deep frozen the destination, return tecFROZEN
661 if (isDeepFrozen(ctx.view, dest, amount.getCurrency(), amount.getIssuer()))
662 return tecFROZEN;
663
664 return tesSUCCESS;
665}
666
667template <>
670 PreclaimContext const& ctx,
671 AccountID const& dest,
672 STAmount const& amount)
673{
674 AccountID issuer = amount.getIssuer();
675 // If the issuer is the same as the dest, return tesSUCCESS
676 if (issuer == dest)
677 return tesSUCCESS;
678
679 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
680 auto const issuanceKey =
682 auto const sleIssuance = ctx.view.read(issuanceKey);
683 if (!sleIssuance)
684 return tecOBJECT_NOT_FOUND;
685
686 // If the issuer has requireAuth set, check if the destination is
687 // authorized
688 auto const& mptIssue = amount.get<MPTIssue>();
689 if (auto const ter =
690 requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
691 ter != tesSUCCESS)
692 return ter;
693
694 // If the issuer has frozen the destination, return tecLOCKED
695 if (isFrozen(ctx.view, dest, mptIssue))
696 return tecLOCKED;
697
698 return tesSUCCESS;
699}
700
701TER
703{
704 if (ctx.view.rules().enabled(featureCredentials))
705 {
706 if (auto const err =
707 credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
708 !isTesSuccess(err))
709 return err;
710 }
711
712 if (ctx.view.rules().enabled(featureTokenEscrow))
713 {
714 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
715 auto const slep = ctx.view.read(k);
716 if (!slep)
717 return tecNO_TARGET;
718
719 AccountID const dest = (*slep)[sfDestination];
720 STAmount const amount = (*slep)[sfAmount];
721
722 if (!isXRP(amount))
723 {
724 if (auto const ret = std::visit(
725 [&]<typename T>(T const&) {
726 return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
727 },
728 amount.asset().value());
729 !isTesSuccess(ret))
730 return ret;
731 }
732 }
733 return tesSUCCESS;
734}
735
736template <ValidIssueType T>
737static TER
739 ApplyView& view,
740 Rate lockedRate,
741 std::shared_ptr<SLE> const& sleDest,
742 STAmount const& xrpBalance,
743 STAmount const& amount,
744 AccountID const& issuer,
745 AccountID const& sender,
746 AccountID const& receiver,
747 bool createAsset,
748 beast::Journal journal);
749
750template <>
753 ApplyView& view,
754 Rate lockedRate,
755 std::shared_ptr<SLE> const& sleDest,
756 STAmount const& xrpBalance,
757 STAmount const& amount,
758 AccountID const& issuer,
759 AccountID const& sender,
760 AccountID const& receiver,
761 bool createAsset,
762 beast::Journal journal)
763{
764 Keylet const trustLineKey = keylet::line(receiver, amount.issue());
765 bool const recvLow = issuer > receiver;
766 bool const senderIssuer = issuer == sender;
767 bool const receiverIssuer = issuer == receiver;
768 bool const issuerHigh = issuer > receiver;
769
770 if (senderIssuer)
771 return tecINTERNAL; // LCOV_EXCL_LINE
772
773 if (receiverIssuer)
774 return tesSUCCESS;
775
776 if (!view.exists(trustLineKey) && createAsset && !receiverIssuer)
777 {
778 // Can the account cover the trust line's reserve?
779 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
780 xrpBalance < view.fees().accountReserve(ownerCount + 1))
781 {
782 JLOG(journal.trace()) << "Trust line does not exist. "
783 "Insufficent reserve to create line.";
784
786 }
787
788 Currency const currency = amount.getCurrency();
789 STAmount initialBalance(amount.issue());
790 initialBalance.setIssuer(noAccount());
791
792 // clang-format off
793 if (TER const ter = trustCreate(
794 view, // payment sandbox
795 recvLow, // is dest low?
796 issuer, // source
797 receiver, // destination
798 trustLineKey.key, // ledger index
799 sleDest, // Account to add to
800 false, // authorize account
801 (sleDest->getFlags() & lsfDefaultRipple) == 0,
802 false, // freeze trust line
803 false, // deep freeze trust line
804 initialBalance, // zero initial balance
805 Issue(currency, receiver), // limit of zero
806 0, // quality in
807 0, // quality out
808 journal); // journal
809 !isTesSuccess(ter))
810 {
811 return ter; // LCOV_EXCL_LINE
812 }
813 // clang-format on
814
815 view.update(sleDest);
816 }
817
818 if (!view.exists(trustLineKey) && !receiverIssuer)
819 return tecNO_LINE;
820
821 auto const xferRate = transferRate(view, amount);
822 // update if issuer rate is less than locked rate
823 if (xferRate < lockedRate)
824 lockedRate = xferRate;
825
826 // Transfer Rate only applies when:
827 // 1. Issuer is not involved in the transfer (senderIssuer or
828 // receiverIssuer)
829 // 2. The locked rate is different from the parity rate
830
831 // NOTE: Transfer fee in escrow works a bit differently from a normal
832 // payment. In escrow, the fee is deducted from the locked/sending amount,
833 // whereas in a normal payment, the transfer fee is taken on top of the
834 // sending amount.
835 auto finalAmt = amount;
836 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
837 {
838 // compute transfer fee, if any
839 auto const xferFee = amount.value() -
840 divideRound(amount, lockedRate, amount.issue(), true);
841 // compute balance to transfer
842 finalAmt = amount.value() - xferFee;
843 }
844
845 // validate the line limit if the account submitting txn is not the receiver
846 // of the funds
847 if (!createAsset)
848 {
849 auto const sleRippleState = view.peek(trustLineKey);
850 if (!sleRippleState)
851 return tecINTERNAL; // LCOV_EXCL_LINE
852
853 // if the issuer is the high, then we use the low limit
854 // otherwise we use the high limit
855 STAmount const lineLimit = sleRippleState->getFieldAmount(
856 issuerHigh ? sfLowLimit : sfHighLimit);
857
858 STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance);
859
860 // flip the sign of the line balance if the issuer is not high
861 if (!issuerHigh)
862 lineBalance.negate();
863
864 // add the final amount to the line balance
865 lineBalance += finalAmt;
866
867 // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED
868 if (lineLimit < lineBalance)
869 return tecLIMIT_EXCEEDED;
870 }
871
872 // if destination is not the issuer then transfer funds
873 if (!receiverIssuer)
874 {
875 auto const ter =
876 rippleCredit(view, issuer, receiver, finalAmt, true, journal);
877 if (ter != tesSUCCESS)
878 return ter; // LCOV_EXCL_LINE
879 }
880 return tesSUCCESS;
881}
882
883template <>
886 ApplyView& view,
887 Rate lockedRate,
888 std::shared_ptr<SLE> const& sleDest,
889 STAmount const& xrpBalance,
890 STAmount const& amount,
891 AccountID const& issuer,
892 AccountID const& sender,
893 AccountID const& receiver,
894 bool createAsset,
895 beast::Journal journal)
896{
897 bool const senderIssuer = issuer == sender;
898 bool const receiverIssuer = issuer == receiver;
899
900 auto const mptID = amount.get<MPTIssue>().getMptID();
901 auto const issuanceKey = keylet::mptIssuance(mptID);
902 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
903 createAsset && !receiverIssuer)
904 {
905 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
906 xrpBalance < view.fees().accountReserve(ownerCount + 1))
907 {
909 }
910
911 if (auto const ter =
912 MPTokenAuthorize::createMPToken(view, mptID, receiver, 0);
913 !isTesSuccess(ter))
914 {
915 return ter; // LCOV_EXCL_LINE
916 }
917
918 // update owner count.
919 adjustOwnerCount(view, sleDest, 1, journal);
920 }
921
922 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
923 !receiverIssuer)
924 return tecNO_PERMISSION;
925
926 auto const xferRate = transferRate(view, amount);
927 // update if issuer rate is less than locked rate
928 if (xferRate < lockedRate)
929 lockedRate = xferRate;
930
931 // Transfer Rate only applies when:
932 // 1. Issuer is not involved in the transfer (senderIssuer or
933 // receiverIssuer)
934 // 2. The locked rate is different from the parity rate
935
936 // NOTE: Transfer fee in escrow works a bit differently from a normal
937 // payment. In escrow, the fee is deducted from the locked/sending amount,
938 // whereas in a normal payment, the transfer fee is taken on top of the
939 // sending amount.
940 auto finalAmt = amount;
941 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
942 {
943 // compute transfer fee, if any
944 auto const xferFee = amount.value() -
945 divideRound(amount, lockedRate, amount.asset(), true);
946 // compute balance to transfer
947 finalAmt = amount.value() - xferFee;
948 }
950 view,
951 sender,
952 receiver,
953 finalAmt,
954 view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
955 journal);
956}
957
958TER
960{
961 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
962 auto const slep = ctx_.view().peek(k);
963 if (!slep)
964 {
965 if (ctx_.view().rules().enabled(featureTokenEscrow))
966 return tecINTERNAL; // LCOV_EXCL_LINE
967
968 return tecNO_TARGET;
969 }
970
971 // If a cancel time is present, a finish operation should only succeed prior
972 // to that time.
973 auto const now = ctx_.view().info().parentCloseTime;
974
975 // Too soon: can't execute before the finish time
976 if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
977 return tecNO_PERMISSION;
978
979 // Too late: can't execute after the cancel time
980 if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
981 return tecNO_PERMISSION;
982
983 // Check cryptocondition fulfillment
984 {
985 auto const id = ctx_.tx.getTransactionID();
986 auto flags = ctx_.app.getHashRouter().getFlags(id);
987
988 auto const cb = ctx_.tx[~sfCondition];
989
990 // It's unlikely that the results of the check will
991 // expire from the hash router, but if it happens,
992 // simply re-run the check.
993 if (cb && !any(flags & (SF_CF_INVALID | SF_CF_VALID)))
994 {
995 // LCOV_EXCL_START
996 auto const fb = ctx_.tx[~sfFulfillment];
997
998 if (!fb)
999 return tecINTERNAL;
1000
1001 if (checkCondition(*fb, *cb))
1002 flags = SF_CF_VALID;
1003 else
1004 flags = SF_CF_INVALID;
1005
1006 ctx_.app.getHashRouter().setFlags(id, flags);
1007 // LCOV_EXCL_STOP
1008 }
1009
1010 // If the check failed, then simply return an error
1011 // and don't look at anything else.
1012 if (any(flags & SF_CF_INVALID))
1014
1015 // Check against condition in the ledger entry:
1016 auto const cond = (*slep)[~sfCondition];
1017
1018 // If a condition wasn't specified during creation,
1019 // one shouldn't be included now.
1020 if (!cond && cb)
1022
1023 // If a condition was specified during creation of
1024 // the suspended payment, the identical condition
1025 // must be presented again. We don't check if the
1026 // fulfillment matches the condition since we did
1027 // that in preflight.
1028 if (cond && (cond != cb))
1030 }
1031
1032 // NOTE: Escrow payments cannot be used to fund accounts.
1033 AccountID const destID = (*slep)[sfDestination];
1034 auto const sled = ctx_.view().peek(keylet::account(destID));
1035 if (!sled)
1036 return tecNO_DST;
1037
1038 if (auto err = verifyDepositPreauth(
1039 ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
1040 !isTesSuccess(err))
1041 return err;
1042
1043 AccountID const account = (*slep)[sfAccount];
1044
1045 // Remove escrow from owner directory
1046 {
1047 auto const page = (*slep)[sfOwnerNode];
1048 if (!ctx_.view().dirRemove(
1049 keylet::ownerDir(account), page, k.key, true))
1050 {
1051 // LCOV_EXCL_START
1052 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1053 return tefBAD_LEDGER;
1054 // LCOV_EXCL_STOP
1055 }
1056 }
1057
1058 // Remove escrow from recipient's owner directory, if present.
1059 if (auto const optPage = (*slep)[~sfDestinationNode])
1060 {
1061 if (!ctx_.view().dirRemove(
1062 keylet::ownerDir(destID), *optPage, k.key, true))
1063 {
1064 // LCOV_EXCL_START
1065 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1066 return tefBAD_LEDGER;
1067 // LCOV_EXCL_STOP
1068 }
1069 }
1070
1071 STAmount const amount = slep->getFieldAmount(sfAmount);
1072 // Transfer amount to destination
1073 if (isXRP(amount))
1074 (*sled)[sfBalance] = (*sled)[sfBalance] + amount;
1075 else
1076 {
1077 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1078 return temDISABLED; // LCOV_EXCL_LINE
1079
1080 Rate lockedRate = slep->isFieldPresent(sfTransferRate)
1081 ? ripple::Rate(slep->getFieldU32(sfTransferRate))
1082 : parityRate;
1083 auto const issuer = amount.getIssuer();
1084 bool const createAsset = destID == account_;
1085 if (auto const ret = std::visit(
1086 [&]<typename T>(T const&) {
1087 return escrowUnlockApplyHelper<T>(
1088 ctx_.view(),
1089 lockedRate,
1090 sled,
1092 amount,
1093 issuer,
1094 account,
1095 destID,
1096 createAsset,
1097 j_);
1098 },
1099 amount.asset().value());
1100 !isTesSuccess(ret))
1101 return ret;
1102
1103 // Remove escrow from issuers owner directory, if present.
1104 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1105 {
1106 if (!ctx_.view().dirRemove(
1107 keylet::ownerDir(issuer), *optPage, k.key, true))
1108 {
1109 // LCOV_EXCL_START
1110 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1111 return tefBAD_LEDGER;
1112 // LCOV_EXCL_STOP
1113 }
1114 }
1115 }
1116
1117 ctx_.view().update(sled);
1118
1119 // Adjust source owner count
1120 auto const sle = ctx_.view().peek(keylet::account(account));
1121 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1122 ctx_.view().update(sle);
1123
1124 // Remove escrow from ledger
1125 ctx_.view().erase(slep);
1126 return tesSUCCESS;
1127}
1128
1129//------------------------------------------------------------------------------
1130
1131NotTEC
1133{
1134 return tesSUCCESS;
1135}
1136
1137template <ValidIssueType T>
1138static TER
1140 PreclaimContext const& ctx,
1141 AccountID const& account,
1142 STAmount const& amount);
1143
1144template <>
1147 PreclaimContext const& ctx,
1148 AccountID const& account,
1149 STAmount const& amount)
1150{
1151 AccountID issuer = amount.getIssuer();
1152 // If the issuer is the same as the account, return tecINTERNAL
1153 if (issuer == account)
1154 return tecINTERNAL; // LCOV_EXCL_LINE
1155
1156 // If the issuer has requireAuth set, check if the account is authorized
1157 if (auto const ter = requireAuth(ctx.view, amount.issue(), account);
1158 ter != tesSUCCESS)
1159 return ter;
1160
1161 return tesSUCCESS;
1162}
1163
1164template <>
1167 PreclaimContext const& ctx,
1168 AccountID const& account,
1169 STAmount const& amount)
1170{
1171 AccountID issuer = amount.getIssuer();
1172 // If the issuer is the same as the account, return tecINTERNAL
1173 if (issuer == account)
1174 return tecINTERNAL; // LCOV_EXCL_LINE
1175
1176 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
1177 auto const issuanceKey =
1179 auto const sleIssuance = ctx.view.read(issuanceKey);
1180 if (!sleIssuance)
1181 return tecOBJECT_NOT_FOUND;
1182
1183 // If the issuer has requireAuth set, check if the account is
1184 // authorized
1185 auto const& mptIssue = amount.get<MPTIssue>();
1186 if (auto const ter =
1187 requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth);
1188 ter != tesSUCCESS)
1189 return ter;
1190
1191 return tesSUCCESS;
1192}
1193
1194TER
1196{
1197 if (ctx.view.rules().enabled(featureTokenEscrow))
1198 {
1199 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
1200 auto const slep = ctx.view.read(k);
1201 if (!slep)
1202 return tecNO_TARGET;
1203
1204 AccountID const account = (*slep)[sfAccount];
1205 STAmount const amount = (*slep)[sfAmount];
1206
1207 if (!isXRP(amount))
1208 {
1209 if (auto const ret = std::visit(
1210 [&]<typename T>(T const&) {
1211 return escrowCancelPreclaimHelper<T>(
1212 ctx, account, amount);
1213 },
1214 amount.asset().value());
1215 !isTesSuccess(ret))
1216 return ret;
1217 }
1218 }
1219 return tesSUCCESS;
1220}
1221
1222TER
1224{
1225 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
1226 auto const slep = ctx_.view().peek(k);
1227 if (!slep)
1228 {
1229 if (ctx_.view().rules().enabled(featureTokenEscrow))
1230 return tecINTERNAL; // LCOV_EXCL_LINE
1231
1232 return tecNO_TARGET;
1233 }
1234
1235 auto const now = ctx_.view().info().parentCloseTime;
1236
1237 // No cancel time specified: can't execute at all.
1238 if (!(*slep)[~sfCancelAfter])
1239 return tecNO_PERMISSION;
1240
1241 // Too soon: can't execute before the cancel time.
1242 if (!after(now, (*slep)[sfCancelAfter]))
1243 return tecNO_PERMISSION;
1244
1245 AccountID const account = (*slep)[sfAccount];
1246
1247 // Remove escrow from owner directory
1248 {
1249 auto const page = (*slep)[sfOwnerNode];
1250 if (!ctx_.view().dirRemove(
1251 keylet::ownerDir(account), page, k.key, true))
1252 {
1253 // LCOV_EXCL_START
1254 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1255 return tefBAD_LEDGER;
1256 // LCOV_EXCL_STOP
1257 }
1258 }
1259
1260 // Remove escrow from recipient's owner directory, if present.
1261 if (auto const optPage = (*slep)[~sfDestinationNode]; optPage)
1262 {
1263 if (!ctx_.view().dirRemove(
1264 keylet::ownerDir((*slep)[sfDestination]),
1265 *optPage,
1266 k.key,
1267 true))
1268 {
1269 // LCOV_EXCL_START
1270 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1271 return tefBAD_LEDGER;
1272 // LCOV_EXCL_STOP
1273 }
1274 }
1275
1276 auto const sle = ctx_.view().peek(keylet::account(account));
1277 STAmount const amount = slep->getFieldAmount(sfAmount);
1278
1279 // Transfer amount back to the owner
1280 if (isXRP(amount))
1281 (*sle)[sfBalance] = (*sle)[sfBalance] + amount;
1282 else
1283 {
1284 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1285 return temDISABLED; // LCOV_EXCL_LINE
1286
1287 auto const issuer = amount.getIssuer();
1288 bool const createAsset = account == account_;
1289 if (auto const ret = std::visit(
1290 [&]<typename T>(T const&) {
1291 return escrowUnlockApplyHelper<T>(
1292 ctx_.view(),
1293 parityRate,
1294 slep,
1296 amount,
1297 issuer,
1298 account, // sender and receiver are the same
1299 account,
1300 createAsset,
1301 j_);
1302 },
1303 amount.asset().value());
1304 !isTesSuccess(ret))
1305 return ret; // LCOV_EXCL_LINE
1306
1307 // Remove escrow from issuers owner directory, if present.
1308 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1309 {
1310 if (!ctx_.view().dirRemove(
1311 keylet::ownerDir(issuer), *optPage, k.key, true))
1312 {
1313 // LCOV_EXCL_START
1314 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1315 return tefBAD_LEDGER;
1316 // LCOV_EXCL_STOP
1317 }
1318 }
1319 }
1320
1321 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1322 ctx_.view().update(sle);
1323
1324 // Remove escrow from ledger
1325 ctx_.view().erase(slep);
1326
1327 return tesSUCCESS;
1328}
1329
1330} // 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:1223
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:1132
static TER preclaim(PreclaimContext const &ctx)
Definition Escrow.cpp:1195
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:702
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:578
static NotTEC preflightSigValidated(PreflightContext const &ctx)
Definition Escrow.cpp:592
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition Escrow.cpp:571
TER doApply() override
Definition Escrow.cpp:959
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition Escrow.cpp:624
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:221
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:645
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:1166
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:553
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:885
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:669
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:1146
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:752
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)