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