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