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
158 return tesSUCCESS;
159}
160
161template <ValidIssueType T>
162static TER
164 PreclaimContext const& ctx,
165 AccountID const& account,
166 AccountID const& dest,
167 STAmount const& amount);
168
169template <>
172 PreclaimContext const& ctx,
173 AccountID const& account,
174 AccountID const& dest,
175 STAmount const& amount)
176{
177 AccountID issuer = amount.getIssuer();
178 // If the issuer is the same as the account, return tecNO_PERMISSION
179 if (issuer == account)
180 return tecNO_PERMISSION;
181
182 // If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
183 auto const sleIssuer = ctx.view.read(keylet::account(issuer));
184 if (!sleIssuer)
185 return tecNO_ISSUER;
186 if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
187 return tecNO_PERMISSION;
188
189 // If the account does not have a trustline to the issuer, return tecNO_LINE
190 auto const sleRippleState =
191 ctx.view.read(keylet::line(account, issuer, amount.getCurrency()));
192 if (!sleRippleState)
193 return tecNO_LINE;
194
195 STAmount const balance = (*sleRippleState)[sfBalance];
196
197 // If balance is positive, issuer must have higher address than account
198 if (balance > beast::zero && issuer < account)
199 return tecNO_PERMISSION; // LCOV_EXCL_LINE
200
201 // If balance is negative, issuer must have lower address than account
202 if (balance < beast::zero && issuer > account)
203 return tecNO_PERMISSION; // LCOV_EXCL_LINE
204
205 // If the issuer has requireAuth set, check if the account is authorized
206 if (auto const ter = requireAuth(ctx.view, amount.issue(), account);
207 ter != tesSUCCESS)
208 return ter;
209
210 // If the issuer has requireAuth set, check if the destination is authorized
211 if (auto const ter = requireAuth(ctx.view, amount.issue(), dest);
212 ter != tesSUCCESS)
213 return ter;
214
215 // If the issuer has frozen the account, return tecFROZEN
216 if (isFrozen(ctx.view, account, amount.issue()))
217 return tecFROZEN;
218
219 // If the issuer has frozen the destination, return tecFROZEN
220 if (isFrozen(ctx.view, dest, amount.issue()))
221 return tecFROZEN;
222
223 STAmount const spendableAmount = accountHolds(
224 ctx.view,
225 account,
226 amount.getCurrency(),
227 issuer,
229 ctx.j);
230
231 // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
232 if (spendableAmount <= beast::zero)
234
235 // If the spendable amount is less than the amount, return
236 // tecINSUFFICIENT_FUNDS
237 if (spendableAmount < amount)
239
240 // If the amount is not addable to the balance, return tecPRECISION_LOSS
241 if (!canAdd(spendableAmount, amount))
242 return tecPRECISION_LOSS;
243
244 return tesSUCCESS;
245}
246
247template <>
250 PreclaimContext const& ctx,
251 AccountID const& account,
252 AccountID const& dest,
253 STAmount const& amount)
254{
255 AccountID issuer = amount.getIssuer();
256 // If the issuer is the same as the account, return tecNO_PERMISSION
257 if (issuer == account)
258 return tecNO_PERMISSION;
259
260 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
261 auto const issuanceKey =
263 auto const sleIssuance = ctx.view.read(issuanceKey);
264 if (!sleIssuance)
265 return tecOBJECT_NOT_FOUND;
266
267 // If the lsfMPTCanEscrow is not enabled, return tecNO_PERMISSION
268 if (!sleIssuance->isFlag(lsfMPTCanEscrow))
269 return tecNO_PERMISSION;
270
271 // If the issuer is not the same as the issuer of the mpt, return
272 // tecNO_PERMISSION
273 if (sleIssuance->getAccountID(sfIssuer) != issuer)
274 return tecNO_PERMISSION; // LCOV_EXCL_LINE
275
276 // If the account does not have the mpt, return tecOBJECT_NOT_FOUND
277 if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, account)))
278 return tecOBJECT_NOT_FOUND;
279
280 // If the issuer has requireAuth set, check if the account is
281 // authorized
282 auto const& mptIssue = amount.get<MPTIssue>();
283 if (auto const ter =
284 requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth);
285 ter != tesSUCCESS)
286 return ter;
287
288 // If the issuer has requireAuth set, check if the destination is
289 // authorized
290 if (auto const ter =
291 requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
292 ter != tesSUCCESS)
293 return ter;
294
295 // If the issuer has frozen the account, return tecLOCKED
296 if (isFrozen(ctx.view, account, mptIssue))
297 return tecLOCKED;
298
299 // If the issuer has frozen the destination, return tecLOCKED
300 if (isFrozen(ctx.view, dest, mptIssue))
301 return tecLOCKED;
302
303 // If the mpt cannot be transferred, return tecNO_AUTH
304 if (auto const ter = canTransfer(ctx.view, mptIssue, account, dest);
305 ter != tesSUCCESS)
306 return ter;
307
308 STAmount const spendableAmount = accountHolds(
309 ctx.view,
310 account,
311 amount.get<MPTIssue>(),
314 ctx.j);
315
316 // If the balance is less than or equal to 0, return tecINSUFFICIENT_FUNDS
317 if (spendableAmount <= beast::zero)
319
320 // If the spendable amount is less than the amount, return
321 // tecINSUFFICIENT_FUNDS
322 if (spendableAmount < amount)
324
325 return tesSUCCESS;
326}
327
328TER
330{
331 STAmount const amount{ctx.tx[sfAmount]};
332 AccountID const account{ctx.tx[sfAccount]};
333 AccountID const dest{ctx.tx[sfDestination]};
334
335 auto const sled = ctx.view.read(keylet::account(dest));
336 if (!sled)
337 return tecNO_DST;
338
339 // Pseudo-accounts cannot receive escrow. Note, this is not amendment-gated
340 // because all writes to pseudo-account discriminator fields **are**
341 // amendment gated, hence the behaviour of this check will always match the
342 // currently active amendments.
343 if (isPseudoAccount(sled))
344 return tecNO_PERMISSION;
345
346 if (!isXRP(amount))
347 {
348 if (!ctx.view.rules().enabled(featureTokenEscrow))
349 return temDISABLED; // LCOV_EXCL_LINE
350
351 if (auto const ret = std::visit(
352 [&]<typename T>(T const&) {
353 return escrowCreatePreclaimHelper<T>(
354 ctx, account, dest, amount);
355 },
356 amount.asset().value());
357 !isTesSuccess(ret))
358 return ret;
359 }
360 return tesSUCCESS;
361}
362
363template <ValidIssueType T>
364static TER
366 ApplyView& view,
367 AccountID const& issuer,
368 AccountID const& sender,
369 STAmount const& amount,
370 beast::Journal journal);
371
372template <>
375 ApplyView& view,
376 AccountID const& issuer,
377 AccountID const& sender,
378 STAmount const& amount,
379 beast::Journal journal)
380{
381 // Defensive: Issuer cannot create an escrow
382 if (issuer == sender)
383 return tecINTERNAL; // LCOV_EXCL_LINE
384
385 auto const ter = rippleCredit(
386 view,
387 sender,
388 issuer,
389 amount,
390 amount.holds<MPTIssue>() ? false : true,
391 journal);
392 if (ter != tesSUCCESS)
393 return ter; // LCOV_EXCL_LINE
394 return tesSUCCESS;
395}
396
397template <>
400 ApplyView& view,
401 AccountID const& issuer,
402 AccountID const& sender,
403 STAmount const& amount,
404 beast::Journal journal)
405{
406 // Defensive: Issuer cannot create an escrow
407 if (issuer == sender)
408 return tecINTERNAL; // LCOV_EXCL_LINE
409
410 auto const ter = rippleLockEscrowMPT(view, sender, amount, journal);
411 if (ter != tesSUCCESS)
412 return ter; // LCOV_EXCL_LINE
413 return tesSUCCESS;
414}
415
416TER
418{
419 auto const closeTime = ctx_.view().info().parentCloseTime;
420
421 if (ctx_.tx[~sfCancelAfter] && after(closeTime, ctx_.tx[sfCancelAfter]))
422 return tecNO_PERMISSION;
423
424 if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter]))
425 return tecNO_PERMISSION;
426
427 auto const sle = ctx_.view().peek(keylet::account(account_));
428 if (!sle)
429 return tefINTERNAL; // LCOV_EXCL_LINE
430
431 // Check reserve and funds availability
432 STAmount const amount{ctx_.tx[sfAmount]};
433
434 auto const reserve =
435 ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1);
436
437 if (mSourceBalance < reserve)
439
440 // Check reserve and funds availability
441 if (isXRP(amount))
442 {
443 if (mSourceBalance < reserve + STAmount(amount).xrp())
444 return tecUNFUNDED;
445 }
446
447 // Check destination account
448 {
449 auto const sled =
450 ctx_.view().read(keylet::account(ctx_.tx[sfDestination]));
451 if (!sled)
452 return tecNO_DST; // LCOV_EXCL_LINE
453 if (((*sled)[sfFlags] & lsfRequireDestTag) &&
454 !ctx_.tx[~sfDestinationTag])
455 return tecDST_TAG_NEEDED;
456 }
457
458 // Create escrow in ledger. Note that we we use the value from the
459 // sequence or ticket. For more explanation see comments in SeqProxy.h.
460 Keylet const escrowKeylet = keylet::escrow(account_, ctx_.tx.getSeqValue());
461 auto const slep = std::make_shared<SLE>(escrowKeylet);
462 (*slep)[sfAmount] = amount;
463 (*slep)[sfAccount] = account_;
464 (*slep)[~sfCondition] = ctx_.tx[~sfCondition];
465 (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
466 (*slep)[sfDestination] = ctx_.tx[sfDestination];
467 (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
468 (*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
469 (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
470
471 if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
472 {
473 (*slep)[sfSequence] = ctx_.tx.getSeqValue();
474 }
475
476 if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
477 {
478 auto const xferRate = transferRate(ctx_.view(), amount);
479 if (xferRate != parityRate)
480 (*slep)[sfTransferRate] = xferRate.value;
481 }
482
483 ctx_.view().insert(slep);
484
485 // Add escrow to sender's owner directory
486 {
487 auto page = ctx_.view().dirInsert(
489 escrowKeylet,
491 if (!page)
492 return tecDIR_FULL; // LCOV_EXCL_LINE
493 (*slep)[sfOwnerNode] = *page;
494 }
495
496 // If it's not a self-send, add escrow to recipient's owner directory.
497 AccountID const dest = ctx_.tx[sfDestination];
498 if (dest != account_)
499 {
500 auto page = ctx_.view().dirInsert(
501 keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
502 if (!page)
503 return tecDIR_FULL; // LCOV_EXCL_LINE
504 (*slep)[sfDestinationNode] = *page;
505 }
506
507 // IOU escrow objects are added to the issuer's owner directory to help
508 // track the total locked balance. For MPT, this isn't necessary because the
509 // locked balance is already stored directly in the MPTokenIssuance object.
510 AccountID const issuer = amount.getIssuer();
511 if (!isXRP(amount) && issuer != account_ && issuer != dest &&
512 !amount.holds<MPTIssue>())
513 {
514 auto page = ctx_.view().dirInsert(
515 keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
516 if (!page)
517 return tecDIR_FULL; // LCOV_EXCL_LINE
518 (*slep)[sfIssuerNode] = *page;
519 }
520
521 // Deduct owner's balance
522 if (isXRP(amount))
523 (*sle)[sfBalance] = (*sle)[sfBalance] - amount;
524 else
525 {
526 if (auto const ret = std::visit(
527 [&]<typename T>(T const&) {
528 return escrowLockApplyHelper<T>(
529 ctx_.view(), issuer, account_, amount, j_);
530 },
531 amount.asset().value());
532 !isTesSuccess(ret))
533 {
534 return ret; // LCOV_EXCL_LINE
535 }
536 }
537
538 // increment owner count
540 ctx_.view().update(sle);
541 return tesSUCCESS;
542}
543
544//------------------------------------------------------------------------------
545
546static bool
548{
549 using namespace ripple::cryptoconditions;
550
552
553 auto condition = Condition::deserialize(c, ec);
554 if (!condition)
555 return false;
556
557 auto fulfillment = Fulfillment::deserialize(f, ec);
558 if (!fulfillment)
559 return false;
560
561 return validate(*fulfillment, *condition);
562}
563
564bool
566{
567 return !ctx.tx.isFieldPresent(sfCredentialIDs) ||
568 ctx.rules.enabled(featureCredentials);
569}
570
571NotTEC
573{
574 auto const cb = ctx.tx[~sfCondition];
575 auto const fb = ctx.tx[~sfFulfillment];
576
577 // If you specify a condition, then you must also specify
578 // a fulfillment.
579 if (static_cast<bool>(cb) != static_cast<bool>(fb))
580 return temMALFORMED;
581
582 return tesSUCCESS;
583}
584
585NotTEC
587{
588 auto const cb = ctx.tx[~sfCondition];
589 auto const fb = ctx.tx[~sfFulfillment];
590
591 if (cb && fb)
592 {
593 auto& router = ctx.app.getHashRouter();
594
595 auto const id = ctx.tx.getTransactionID();
596 auto const flags = router.getFlags(id);
597
598 // If we haven't checked the condition, check it
599 // now. Whether it passes or not isn't important
600 // in preflight.
601 if (!any(flags & (SF_CF_INVALID | SF_CF_VALID)))
602 {
603 if (checkCondition(*fb, *cb))
604 router.setFlags(id, SF_CF_VALID);
605 else
606 router.setFlags(id, SF_CF_INVALID);
607 }
608 }
609
610 if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
611 !isTesSuccess(err))
612 return err;
613
614 return tesSUCCESS;
615}
616
619{
620 XRPAmount extraFee{0};
621
622 if (auto const fb = tx[~sfFulfillment])
623 {
624 extraFee += view.fees().base * (32 + (fb->size() / 16));
625 }
626
627 return Transactor::calculateBaseFee(view, tx) + extraFee;
628}
629
630template <ValidIssueType T>
631static TER
633 PreclaimContext const& ctx,
634 AccountID const& dest,
635 STAmount const& amount);
636
637template <>
640 PreclaimContext const& ctx,
641 AccountID const& dest,
642 STAmount const& amount)
643{
644 AccountID issuer = amount.getIssuer();
645 // If the issuer is the same as the account, return tesSUCCESS
646 if (issuer == dest)
647 return tesSUCCESS;
648
649 // If the issuer has requireAuth set, check if the destination is authorized
650 if (auto const ter = requireAuth(ctx.view, amount.issue(), dest);
651 ter != tesSUCCESS)
652 return ter;
653
654 // If the issuer has deep frozen the destination, return tecFROZEN
655 if (isDeepFrozen(ctx.view, dest, amount.getCurrency(), amount.getIssuer()))
656 return tecFROZEN;
657
658 return tesSUCCESS;
659}
660
661template <>
664 PreclaimContext const& ctx,
665 AccountID const& dest,
666 STAmount const& amount)
667{
668 AccountID issuer = amount.getIssuer();
669 // If the issuer is the same as the dest, return tesSUCCESS
670 if (issuer == dest)
671 return tesSUCCESS;
672
673 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
674 auto const issuanceKey =
676 auto const sleIssuance = ctx.view.read(issuanceKey);
677 if (!sleIssuance)
678 return tecOBJECT_NOT_FOUND;
679
680 // If the issuer has requireAuth set, check if the destination is
681 // authorized
682 auto const& mptIssue = amount.get<MPTIssue>();
683 if (auto const ter =
684 requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
685 ter != tesSUCCESS)
686 return ter;
687
688 // If the issuer has frozen the destination, return tecLOCKED
689 if (isFrozen(ctx.view, dest, mptIssue))
690 return tecLOCKED;
691
692 return tesSUCCESS;
693}
694
695TER
697{
698 if (ctx.view.rules().enabled(featureCredentials))
699 {
700 if (auto const err =
701 credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
702 !isTesSuccess(err))
703 return err;
704 }
705
706 if (ctx.view.rules().enabled(featureTokenEscrow))
707 {
708 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
709 auto const slep = ctx.view.read(k);
710 if (!slep)
711 return tecNO_TARGET;
712
713 AccountID const dest = (*slep)[sfDestination];
714 STAmount const amount = (*slep)[sfAmount];
715
716 if (!isXRP(amount))
717 {
718 if (auto const ret = std::visit(
719 [&]<typename T>(T const&) {
720 return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
721 },
722 amount.asset().value());
723 !isTesSuccess(ret))
724 return ret;
725 }
726 }
727 return tesSUCCESS;
728}
729
730template <ValidIssueType T>
731static TER
733 ApplyView& view,
734 Rate lockedRate,
735 std::shared_ptr<SLE> const& sleDest,
736 STAmount const& xrpBalance,
737 STAmount const& amount,
738 AccountID const& issuer,
739 AccountID const& sender,
740 AccountID const& receiver,
741 bool createAsset,
742 beast::Journal journal);
743
744template <>
747 ApplyView& view,
748 Rate lockedRate,
749 std::shared_ptr<SLE> const& sleDest,
750 STAmount const& xrpBalance,
751 STAmount const& amount,
752 AccountID const& issuer,
753 AccountID const& sender,
754 AccountID const& receiver,
755 bool createAsset,
756 beast::Journal journal)
757{
758 Keylet const trustLineKey = keylet::line(receiver, amount.issue());
759 bool const recvLow = issuer > receiver;
760 bool const senderIssuer = issuer == sender;
761 bool const receiverIssuer = issuer == receiver;
762 bool const issuerHigh = issuer > receiver;
763
764 if (senderIssuer)
765 return tecINTERNAL; // LCOV_EXCL_LINE
766
767 if (receiverIssuer)
768 return tesSUCCESS;
769
770 if (!view.exists(trustLineKey) && createAsset && !receiverIssuer)
771 {
772 // Can the account cover the trust line's reserve?
773 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
774 xrpBalance < view.fees().accountReserve(ownerCount + 1))
775 {
776 JLOG(journal.trace()) << "Trust line does not exist. "
777 "Insufficent reserve to create line.";
778
780 }
781
782 Currency const currency = amount.getCurrency();
783 STAmount initialBalance(amount.issue());
784 initialBalance.setIssuer(noAccount());
785
786 // clang-format off
787 if (TER const ter = trustCreate(
788 view, // payment sandbox
789 recvLow, // is dest low?
790 issuer, // source
791 receiver, // destination
792 trustLineKey.key, // ledger index
793 sleDest, // Account to add to
794 false, // authorize account
795 (sleDest->getFlags() & lsfDefaultRipple) == 0,
796 false, // freeze trust line
797 false, // deep freeze trust line
798 initialBalance, // zero initial balance
799 Issue(currency, receiver), // limit of zero
800 0, // quality in
801 0, // quality out
802 journal); // journal
803 !isTesSuccess(ter))
804 {
805 return ter; // LCOV_EXCL_LINE
806 }
807 // clang-format on
808
809 view.update(sleDest);
810 }
811
812 if (!view.exists(trustLineKey) && !receiverIssuer)
813 return tecNO_LINE;
814
815 auto const xferRate = transferRate(view, amount);
816 // update if issuer rate is less than locked rate
817 if (xferRate < lockedRate)
818 lockedRate = xferRate;
819
820 // Transfer Rate only applies when:
821 // 1. Issuer is not involved in the transfer (senderIssuer or
822 // receiverIssuer)
823 // 2. The locked rate is different from the parity rate
824
825 // NOTE: Transfer fee in escrow works a bit differently from a normal
826 // payment. In escrow, the fee is deducted from the locked/sending amount,
827 // whereas in a normal payment, the transfer fee is taken on top of the
828 // sending amount.
829 auto finalAmt = amount;
830 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
831 {
832 // compute transfer fee, if any
833 auto const xferFee = amount.value() -
834 divideRound(amount, lockedRate, amount.issue(), true);
835 // compute balance to transfer
836 finalAmt = amount.value() - xferFee;
837 }
838
839 // validate the line limit if the account submitting txn is not the receiver
840 // of the funds
841 if (!createAsset)
842 {
843 auto const sleRippleState = view.peek(trustLineKey);
844 if (!sleRippleState)
845 return tecINTERNAL; // LCOV_EXCL_LINE
846
847 // if the issuer is the high, then we use the low limit
848 // otherwise we use the high limit
849 STAmount const lineLimit = sleRippleState->getFieldAmount(
850 issuerHigh ? sfLowLimit : sfHighLimit);
851
852 STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance);
853
854 // flip the sign of the line balance if the issuer is not high
855 if (!issuerHigh)
856 lineBalance.negate();
857
858 // add the final amount to the line balance
859 lineBalance += finalAmt;
860
861 // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED
862 if (lineLimit < lineBalance)
863 return tecLIMIT_EXCEEDED;
864 }
865
866 // if destination is not the issuer then transfer funds
867 if (!receiverIssuer)
868 {
869 auto const ter =
870 rippleCredit(view, issuer, receiver, finalAmt, true, journal);
871 if (ter != tesSUCCESS)
872 return ter; // LCOV_EXCL_LINE
873 }
874 return tesSUCCESS;
875}
876
877template <>
880 ApplyView& view,
881 Rate lockedRate,
882 std::shared_ptr<SLE> const& sleDest,
883 STAmount const& xrpBalance,
884 STAmount const& amount,
885 AccountID const& issuer,
886 AccountID const& sender,
887 AccountID const& receiver,
888 bool createAsset,
889 beast::Journal journal)
890{
891 bool const senderIssuer = issuer == sender;
892 bool const receiverIssuer = issuer == receiver;
893
894 auto const mptID = amount.get<MPTIssue>().getMptID();
895 auto const issuanceKey = keylet::mptIssuance(mptID);
896 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
897 createAsset && !receiverIssuer)
898 {
899 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
900 xrpBalance < view.fees().accountReserve(ownerCount + 1))
901 {
903 }
904
905 if (auto const ter =
906 MPTokenAuthorize::createMPToken(view, mptID, receiver, 0);
907 !isTesSuccess(ter))
908 {
909 return ter; // LCOV_EXCL_LINE
910 }
911
912 // update owner count.
913 adjustOwnerCount(view, sleDest, 1, journal);
914 }
915
916 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
917 !receiverIssuer)
918 return tecNO_PERMISSION;
919
920 auto const xferRate = transferRate(view, amount);
921 // update if issuer rate is less than locked rate
922 if (xferRate < lockedRate)
923 lockedRate = xferRate;
924
925 // Transfer Rate only applies when:
926 // 1. Issuer is not involved in the transfer (senderIssuer or
927 // receiverIssuer)
928 // 2. The locked rate is different from the parity rate
929
930 // NOTE: Transfer fee in escrow works a bit differently from a normal
931 // payment. In escrow, the fee is deducted from the locked/sending amount,
932 // whereas in a normal payment, the transfer fee is taken on top of the
933 // sending amount.
934 auto finalAmt = amount;
935 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
936 {
937 // compute transfer fee, if any
938 auto const xferFee = amount.value() -
939 divideRound(amount, lockedRate, amount.asset(), true);
940 // compute balance to transfer
941 finalAmt = amount.value() - xferFee;
942 }
944 view,
945 sender,
946 receiver,
947 finalAmt,
948 view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
949 journal);
950}
951
952TER
954{
955 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
956 auto const slep = ctx_.view().peek(k);
957 if (!slep)
958 {
959 if (ctx_.view().rules().enabled(featureTokenEscrow))
960 return tecINTERNAL; // LCOV_EXCL_LINE
961
962 return tecNO_TARGET;
963 }
964
965 // If a cancel time is present, a finish operation should only succeed prior
966 // to that time.
967 auto const now = ctx_.view().info().parentCloseTime;
968
969 // Too soon: can't execute before the finish time
970 if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
971 return tecNO_PERMISSION;
972
973 // Too late: can't execute after the cancel time
974 if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
975 return tecNO_PERMISSION;
976
977 // Check cryptocondition fulfillment
978 {
979 auto const id = ctx_.tx.getTransactionID();
980 auto flags = ctx_.app.getHashRouter().getFlags(id);
981
982 auto const cb = ctx_.tx[~sfCondition];
983
984 // It's unlikely that the results of the check will
985 // expire from the hash router, but if it happens,
986 // simply re-run the check.
987 if (cb && !any(flags & (SF_CF_INVALID | SF_CF_VALID)))
988 {
989 // LCOV_EXCL_START
990 auto const fb = ctx_.tx[~sfFulfillment];
991
992 if (!fb)
993 return tecINTERNAL;
994
995 if (checkCondition(*fb, *cb))
996 flags = SF_CF_VALID;
997 else
998 flags = SF_CF_INVALID;
999
1000 ctx_.app.getHashRouter().setFlags(id, flags);
1001 // LCOV_EXCL_STOP
1002 }
1003
1004 // If the check failed, then simply return an error
1005 // and don't look at anything else.
1006 if (any(flags & SF_CF_INVALID))
1008
1009 // Check against condition in the ledger entry:
1010 auto const cond = (*slep)[~sfCondition];
1011
1012 // If a condition wasn't specified during creation,
1013 // one shouldn't be included now.
1014 if (!cond && cb)
1016
1017 // If a condition was specified during creation of
1018 // the suspended payment, the identical condition
1019 // must be presented again. We don't check if the
1020 // fulfillment matches the condition since we did
1021 // that in preflight.
1022 if (cond && (cond != cb))
1024 }
1025
1026 // NOTE: Escrow payments cannot be used to fund accounts.
1027 AccountID const destID = (*slep)[sfDestination];
1028 auto const sled = ctx_.view().peek(keylet::account(destID));
1029 if (!sled)
1030 return tecNO_DST;
1031
1032 if (auto err = verifyDepositPreauth(
1033 ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
1034 !isTesSuccess(err))
1035 return err;
1036
1037 AccountID const account = (*slep)[sfAccount];
1038
1039 // Remove escrow from owner directory
1040 {
1041 auto const page = (*slep)[sfOwnerNode];
1042 if (!ctx_.view().dirRemove(
1043 keylet::ownerDir(account), page, k.key, true))
1044 {
1045 // LCOV_EXCL_START
1046 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1047 return tefBAD_LEDGER;
1048 // LCOV_EXCL_STOP
1049 }
1050 }
1051
1052 // Remove escrow from recipient's owner directory, if present.
1053 if (auto const optPage = (*slep)[~sfDestinationNode])
1054 {
1055 if (!ctx_.view().dirRemove(
1056 keylet::ownerDir(destID), *optPage, k.key, true))
1057 {
1058 // LCOV_EXCL_START
1059 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1060 return tefBAD_LEDGER;
1061 // LCOV_EXCL_STOP
1062 }
1063 }
1064
1065 STAmount const amount = slep->getFieldAmount(sfAmount);
1066 // Transfer amount to destination
1067 if (isXRP(amount))
1068 (*sled)[sfBalance] = (*sled)[sfBalance] + amount;
1069 else
1070 {
1071 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1072 return temDISABLED; // LCOV_EXCL_LINE
1073
1074 Rate lockedRate = slep->isFieldPresent(sfTransferRate)
1075 ? ripple::Rate(slep->getFieldU32(sfTransferRate))
1076 : parityRate;
1077 auto const issuer = amount.getIssuer();
1078 bool const createAsset = destID == account_;
1079 if (auto const ret = std::visit(
1080 [&]<typename T>(T const&) {
1081 return escrowUnlockApplyHelper<T>(
1082 ctx_.view(),
1083 lockedRate,
1084 sled,
1086 amount,
1087 issuer,
1088 account,
1089 destID,
1090 createAsset,
1091 j_);
1092 },
1093 amount.asset().value());
1094 !isTesSuccess(ret))
1095 return ret;
1096
1097 // Remove escrow from issuers owner directory, if present.
1098 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1099 {
1100 if (!ctx_.view().dirRemove(
1101 keylet::ownerDir(issuer), *optPage, k.key, true))
1102 {
1103 // LCOV_EXCL_START
1104 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1105 return tefBAD_LEDGER;
1106 // LCOV_EXCL_STOP
1107 }
1108 }
1109 }
1110
1111 ctx_.view().update(sled);
1112
1113 // Adjust source owner count
1114 auto const sle = ctx_.view().peek(keylet::account(account));
1115 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1116 ctx_.view().update(sle);
1117
1118 // Remove escrow from ledger
1119 ctx_.view().erase(slep);
1120 return tesSUCCESS;
1121}
1122
1123//------------------------------------------------------------------------------
1124
1125NotTEC
1127{
1128 return tesSUCCESS;
1129}
1130
1131template <ValidIssueType T>
1132static TER
1134 PreclaimContext const& ctx,
1135 AccountID const& account,
1136 STAmount const& amount);
1137
1138template <>
1141 PreclaimContext const& ctx,
1142 AccountID const& account,
1143 STAmount const& amount)
1144{
1145 AccountID issuer = amount.getIssuer();
1146 // If the issuer is the same as the account, return tecINTERNAL
1147 if (issuer == account)
1148 return tecINTERNAL; // LCOV_EXCL_LINE
1149
1150 // If the issuer has requireAuth set, check if the account is authorized
1151 if (auto const ter = requireAuth(ctx.view, amount.issue(), account);
1152 ter != tesSUCCESS)
1153 return ter;
1154
1155 return tesSUCCESS;
1156}
1157
1158template <>
1161 PreclaimContext const& ctx,
1162 AccountID const& account,
1163 STAmount const& amount)
1164{
1165 AccountID issuer = amount.getIssuer();
1166 // If the issuer is the same as the account, return tecINTERNAL
1167 if (issuer == account)
1168 return tecINTERNAL; // LCOV_EXCL_LINE
1169
1170 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
1171 auto const issuanceKey =
1173 auto const sleIssuance = ctx.view.read(issuanceKey);
1174 if (!sleIssuance)
1175 return tecOBJECT_NOT_FOUND;
1176
1177 // If the issuer has requireAuth set, check if the account is
1178 // authorized
1179 auto const& mptIssue = amount.get<MPTIssue>();
1180 if (auto const ter =
1181 requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth);
1182 ter != tesSUCCESS)
1183 return ter;
1184
1185 return tesSUCCESS;
1186}
1187
1188TER
1190{
1191 if (ctx.view.rules().enabled(featureTokenEscrow))
1192 {
1193 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
1194 auto const slep = ctx.view.read(k);
1195 if (!slep)
1196 return tecNO_TARGET;
1197
1198 AccountID const account = (*slep)[sfAccount];
1199 STAmount const amount = (*slep)[sfAmount];
1200
1201 if (!isXRP(amount))
1202 {
1203 if (auto const ret = std::visit(
1204 [&]<typename T>(T const&) {
1205 return escrowCancelPreclaimHelper<T>(
1206 ctx, account, amount);
1207 },
1208 amount.asset().value());
1209 !isTesSuccess(ret))
1210 return ret;
1211 }
1212 }
1213 return tesSUCCESS;
1214}
1215
1216TER
1218{
1219 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
1220 auto const slep = ctx_.view().peek(k);
1221 if (!slep)
1222 {
1223 if (ctx_.view().rules().enabled(featureTokenEscrow))
1224 return tecINTERNAL; // LCOV_EXCL_LINE
1225
1226 return tecNO_TARGET;
1227 }
1228
1229 auto const now = ctx_.view().info().parentCloseTime;
1230
1231 // No cancel time specified: can't execute at all.
1232 if (!(*slep)[~sfCancelAfter])
1233 return tecNO_PERMISSION;
1234
1235 // Too soon: can't execute before the cancel time.
1236 if (!after(now, (*slep)[sfCancelAfter]))
1237 return tecNO_PERMISSION;
1238
1239 AccountID const account = (*slep)[sfAccount];
1240
1241 // Remove escrow from owner directory
1242 {
1243 auto const page = (*slep)[sfOwnerNode];
1244 if (!ctx_.view().dirRemove(
1245 keylet::ownerDir(account), page, k.key, true))
1246 {
1247 // LCOV_EXCL_START
1248 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1249 return tefBAD_LEDGER;
1250 // LCOV_EXCL_STOP
1251 }
1252 }
1253
1254 // Remove escrow from recipient's owner directory, if present.
1255 if (auto const optPage = (*slep)[~sfDestinationNode]; optPage)
1256 {
1257 if (!ctx_.view().dirRemove(
1258 keylet::ownerDir((*slep)[sfDestination]),
1259 *optPage,
1260 k.key,
1261 true))
1262 {
1263 // LCOV_EXCL_START
1264 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1265 return tefBAD_LEDGER;
1266 // LCOV_EXCL_STOP
1267 }
1268 }
1269
1270 auto const sle = ctx_.view().peek(keylet::account(account));
1271 STAmount const amount = slep->getFieldAmount(sfAmount);
1272
1273 // Transfer amount back to the owner
1274 if (isXRP(amount))
1275 (*sle)[sfBalance] = (*sle)[sfBalance] + amount;
1276 else
1277 {
1278 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1279 return temDISABLED; // LCOV_EXCL_LINE
1280
1281 auto const issuer = amount.getIssuer();
1282 bool const createAsset = account == account_;
1283 if (auto const ret = std::visit(
1284 [&]<typename T>(T const&) {
1285 return escrowUnlockApplyHelper<T>(
1286 ctx_.view(),
1287 parityRate,
1288 slep,
1290 amount,
1291 issuer,
1292 account, // sender and receiver are the same
1293 account,
1294 createAsset,
1295 j_);
1296 },
1297 amount.asset().value());
1298 !isTesSuccess(ret))
1299 return ret; // LCOV_EXCL_LINE
1300
1301 // Remove escrow from issuers owner directory, if present.
1302 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1303 {
1304 if (!ctx_.view().dirRemove(
1305 keylet::ownerDir(issuer), *optPage, k.key, true))
1306 {
1307 // LCOV_EXCL_START
1308 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1309 return tefBAD_LEDGER;
1310 // LCOV_EXCL_STOP
1311 }
1312 }
1313 }
1314
1315 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1316 ctx_.view().update(sle);
1317
1318 // Remove escrow from ledger
1319 ctx_.view().erase(slep);
1320
1321 return tesSUCCESS;
1322}
1323
1324} // 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:1217
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:1126
static TER preclaim(PreclaimContext const &ctx)
Definition Escrow.cpp:1189
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:103
TER doApply() override
Definition Escrow.cpp:417
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
Definition Escrow.cpp:63
static TER preclaim(PreclaimContext const &ctx)
Definition Escrow.cpp:329
static TER preclaim(PreclaimContext const &ctx)
Definition Escrow.cpp:696
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:572
static NotTEC preflightSigValidated(PreflightContext const &ctx)
Definition Escrow.cpp:586
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition Escrow.cpp:565
TER doApply() override
Definition Escrow.cpp:953
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition Escrow.cpp:618
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:199
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:639
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:249
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:374
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:1160
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:171
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:547
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:879
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:663
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:1140
TER escrowLockApplyHelper< MPTIssue >(ApplyView &view, AccountID const &issuer, AccountID const &sender, STAmount const &amount, beast::Journal journal)
Definition Escrow.cpp:399
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:746
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)