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); !isTesSuccess(err))
676 return err;
677
678 return tesSUCCESS;
679}
680
683{
684 XRPAmount extraFee{0};
685
686 if (auto const fb = tx[~sfFulfillment])
687 {
688 extraFee += view.fees().base * (32 + (fb->size() / 16));
689 }
690
691 return Transactor::calculateBaseFee(view, tx) + extraFee;
692}
693
694template <ValidIssueType T>
695static TER
697 PreclaimContext const& ctx,
698 AccountID const& dest,
699 STAmount const& amount);
700
701template <>
704 PreclaimContext const& ctx,
705 AccountID const& dest,
706 STAmount const& amount)
707{
708 AccountID issuer = amount.getIssuer();
709 // If the issuer is the same as the account, return tesSUCCESS
710 if (issuer == dest)
711 return tesSUCCESS;
712
713 // If the issuer has requireAuth set, check if the destination is authorized
714 if (auto const ter = requireAuth(ctx.view, amount.issue(), dest);
715 ter != tesSUCCESS)
716 return ter;
717
718 // If the issuer has deep frozen the destination, return tecFROZEN
719 if (isDeepFrozen(ctx.view, dest, amount.getCurrency(), amount.getIssuer()))
720 return tecFROZEN;
721
722 return tesSUCCESS;
723}
724
725template <>
728 PreclaimContext const& ctx,
729 AccountID const& dest,
730 STAmount const& amount)
731{
732 AccountID issuer = amount.getIssuer();
733 // If the issuer is the same as the dest, return tesSUCCESS
734 if (issuer == dest)
735 return tesSUCCESS;
736
737 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
738 auto const issuanceKey =
740 auto const sleIssuance = ctx.view.read(issuanceKey);
741 if (!sleIssuance)
742 return tecOBJECT_NOT_FOUND;
743
744 // If the issuer has requireAuth set, check if the destination is
745 // authorized
746 auto const& mptIssue = amount.get<MPTIssue>();
747 if (auto const ter =
748 requireAuth(ctx.view, mptIssue, dest, MPTAuthType::WeakAuth);
749 ter != tesSUCCESS)
750 return ter;
751
752 // If the issuer has frozen the destination, return tecLOCKED
753 if (isFrozen(ctx.view, dest, mptIssue))
754 return tecLOCKED;
755
756 return tesSUCCESS;
757}
758
759TER
761{
762 if (ctx.view.rules().enabled(featureCredentials))
763 {
764 if (auto const err = credentials::valid(ctx, ctx.tx[sfAccount]);
765 !isTesSuccess(err))
766 return err;
767 }
768
769 if (ctx.view.rules().enabled(featureTokenEscrow))
770 {
771 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
772 auto const slep = ctx.view.read(k);
773 if (!slep)
774 return tecNO_TARGET;
775
776 AccountID const dest = (*slep)[sfDestination];
777 STAmount const amount = (*slep)[sfAmount];
778
779 if (!isXRP(amount))
780 {
781 if (auto const ret = std::visit(
782 [&]<typename T>(T const&) {
783 return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
784 },
785 amount.asset().value());
786 !isTesSuccess(ret))
787 return ret;
788 }
789 }
790 return tesSUCCESS;
791}
792
793template <ValidIssueType T>
794static TER
796 ApplyView& view,
797 Rate lockedRate,
798 std::shared_ptr<SLE> const& sleDest,
799 STAmount const& xrpBalance,
800 STAmount const& amount,
801 AccountID const& issuer,
802 AccountID const& sender,
803 AccountID const& receiver,
804 bool createAsset,
805 beast::Journal journal);
806
807template <>
810 ApplyView& view,
811 Rate lockedRate,
812 std::shared_ptr<SLE> const& sleDest,
813 STAmount const& xrpBalance,
814 STAmount const& amount,
815 AccountID const& issuer,
816 AccountID const& sender,
817 AccountID const& receiver,
818 bool createAsset,
819 beast::Journal journal)
820{
821 Keylet const trustLineKey = keylet::line(receiver, amount.issue());
822 bool const recvLow = issuer > receiver;
823 bool const senderIssuer = issuer == sender;
824 bool const receiverIssuer = issuer == receiver;
825 bool const issuerHigh = issuer > receiver;
826
827 // LCOV_EXCL_START
828 if (senderIssuer)
829 return tecINTERNAL;
830 // LCOV_EXCL_STOP
831
832 if (receiverIssuer)
833 return tesSUCCESS;
834
835 if (!view.exists(trustLineKey) && createAsset && !receiverIssuer)
836 {
837 // Can the account cover the trust line's reserve?
838 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
839 xrpBalance < view.fees().accountReserve(ownerCount + 1))
840 {
841 JLOG(journal.trace()) << "Trust line does not exist. "
842 "Insufficent reserve to create line.";
843
845 }
846
847 Currency const currency = amount.getCurrency();
848 STAmount initialBalance(amount.issue());
849 initialBalance.setIssuer(noAccount());
850
851 // clang-format off
852 if (TER const ter = trustCreate(
853 view, // payment sandbox
854 recvLow, // is dest low?
855 issuer, // source
856 receiver, // destination
857 trustLineKey.key, // ledger index
858 sleDest, // Account to add to
859 false, // authorize account
860 (sleDest->getFlags() & lsfDefaultRipple) == 0,
861 false, // freeze trust line
862 false, // deep freeze trust line
863 initialBalance, // zero initial balance
864 Issue(currency, receiver), // limit of zero
865 0, // quality in
866 0, // quality out
867 journal); // journal
868 !isTesSuccess(ter))
869 {
870 return ter; // LCOV_EXCL_LINE
871 }
872 // clang-format on
873
874 view.update(sleDest);
875 }
876
877 if (!view.exists(trustLineKey) && !receiverIssuer)
878 return tecNO_LINE;
879
880 auto const xferRate = transferRate(view, amount);
881 // update if issuer rate is less than locked rate
882 if (xferRate < lockedRate)
883 lockedRate = xferRate;
884
885 // Transfer Rate only applies when:
886 // 1. Issuer is not involved in the transfer (senderIssuer or
887 // receiverIssuer)
888 // 2. The locked rate is different from the parity rate
889
890 // NOTE: Transfer fee in escrow works a bit differently from a normal
891 // payment. In escrow, the fee is deducted from the locked/sending amount,
892 // whereas in a normal payment, the transfer fee is taken on top of the
893 // sending amount.
894 auto finalAmt = amount;
895 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
896 {
897 // compute transfer fee, if any
898 auto const xferFee = amount.value() -
899 divideRound(amount, lockedRate, amount.issue(), true);
900 // compute balance to transfer
901 finalAmt = amount.value() - xferFee;
902 }
903
904 // validate the line limit if the account submitting txn is not the receiver
905 // of the funds
906 if (!createAsset)
907 {
908 auto const sleRippleState = view.peek(trustLineKey);
909 if (!sleRippleState)
910 return tecINTERNAL; // LCOV_EXCL_LINE
911
912 // if the issuer is the high, then we use the low limit
913 // otherwise we use the high limit
914 STAmount const lineLimit = sleRippleState->getFieldAmount(
915 issuerHigh ? sfLowLimit : sfHighLimit);
916
917 STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance);
918
919 // flip the sign of the line balance if the issuer is not high
920 if (!issuerHigh)
921 lineBalance.negate();
922
923 // add the final amount to the line balance
924 lineBalance += finalAmt;
925
926 // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED
927 if (lineLimit < lineBalance)
928 return tecLIMIT_EXCEEDED;
929 }
930
931 // if destination is not the issuer then transfer funds
932 if (!receiverIssuer)
933 {
934 auto const ter =
935 rippleCredit(view, issuer, receiver, finalAmt, true, journal);
936 if (ter != tesSUCCESS)
937 return ter; // LCOV_EXCL_LINE
938 }
939 return tesSUCCESS;
940}
941
942template <>
945 ApplyView& view,
946 Rate lockedRate,
947 std::shared_ptr<SLE> const& sleDest,
948 STAmount const& xrpBalance,
949 STAmount const& amount,
950 AccountID const& issuer,
951 AccountID const& sender,
952 AccountID const& receiver,
953 bool createAsset,
954 beast::Journal journal)
955{
956 bool const senderIssuer = issuer == sender;
957 bool const receiverIssuer = issuer == receiver;
958
959 auto const mptID = amount.get<MPTIssue>().getMptID();
960 auto const issuanceKey = keylet::mptIssuance(mptID);
961 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
962 createAsset && !receiverIssuer)
963 {
964 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
965 xrpBalance < view.fees().accountReserve(ownerCount + 1))
966 {
968 }
969
970 if (auto const ter =
971 MPTokenAuthorize::createMPToken(view, mptID, receiver, 0);
972 !isTesSuccess(ter))
973 {
974 return ter; // LCOV_EXCL_LINE
975 }
976
977 // update owner count.
978 adjustOwnerCount(view, sleDest, 1, journal);
979 }
980
981 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
982 !receiverIssuer)
983 return tecNO_PERMISSION;
984
985 auto const xferRate = transferRate(view, amount);
986 // update if issuer rate is less than locked rate
987 if (xferRate < lockedRate)
988 lockedRate = xferRate;
989
990 // Transfer Rate only applies when:
991 // 1. Issuer is not involved in the transfer (senderIssuer or
992 // receiverIssuer)
993 // 2. The locked rate is different from the parity rate
994
995 // NOTE: Transfer fee in escrow works a bit differently from a normal
996 // payment. In escrow, the fee is deducted from the locked/sending amount,
997 // whereas in a normal payment, the transfer fee is taken on top of the
998 // sending amount.
999 auto finalAmt = amount;
1000 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
1001 {
1002 // compute transfer fee, if any
1003 auto const xferFee = amount.value() -
1004 divideRound(amount, lockedRate, amount.asset(), true);
1005 // compute balance to transfer
1006 finalAmt = amount.value() - xferFee;
1007 }
1008
1009 return rippleUnlockEscrowMPT(view, sender, receiver, finalAmt, journal);
1010}
1011
1012TER
1014{
1015 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
1016 auto const slep = ctx_.view().peek(k);
1017 if (!slep)
1018 {
1019 if (ctx_.view().rules().enabled(featureTokenEscrow))
1020 return tecINTERNAL; // LCOV_EXCL_LINE
1021
1022 return tecNO_TARGET;
1023 }
1024
1025 // If a cancel time is present, a finish operation should only succeed prior
1026 // to that time. fix1571 corrects a logic error in the check that would make
1027 // a finish only succeed strictly after the cancel time.
1028 if (ctx_.view().rules().enabled(fix1571))
1029 {
1030 auto const now = ctx_.view().info().parentCloseTime;
1031
1032 // Too soon: can't execute before the finish time
1033 if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
1034 return tecNO_PERMISSION;
1035
1036 // Too late: can't execute after the cancel time
1037 if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
1038 return tecNO_PERMISSION;
1039 }
1040 else
1041 {
1042 // Too soon?
1043 if ((*slep)[~sfFinishAfter] &&
1045 (*slep)[sfFinishAfter])
1046 return tecNO_PERMISSION;
1047
1048 // Too late?
1049 if ((*slep)[~sfCancelAfter] &&
1051 (*slep)[sfCancelAfter])
1052 return tecNO_PERMISSION;
1053 }
1054
1055 // Check cryptocondition fulfillment
1056 {
1057 auto const id = ctx_.tx.getTransactionID();
1058 auto flags = ctx_.app.getHashRouter().getFlags(id);
1059
1060 auto const cb = ctx_.tx[~sfCondition];
1061
1062 // It's unlikely that the results of the check will
1063 // expire from the hash router, but if it happens,
1064 // simply re-run the check.
1065 if (cb && !(flags & (SF_CF_INVALID | SF_CF_VALID)))
1066 {
1067 auto const fb = ctx_.tx[~sfFulfillment];
1068
1069 if (!fb)
1070 return tecINTERNAL;
1071
1072 if (checkCondition(*fb, *cb))
1073 flags = SF_CF_VALID;
1074 else
1075 flags = SF_CF_INVALID;
1076
1077 ctx_.app.getHashRouter().setFlags(id, flags);
1078 }
1079
1080 // If the check failed, then simply return an error
1081 // and don't look at anything else.
1082 if (flags & SF_CF_INVALID)
1084
1085 // Check against condition in the ledger entry:
1086 auto const cond = (*slep)[~sfCondition];
1087
1088 // If a condition wasn't specified during creation,
1089 // one shouldn't be included now.
1090 if (!cond && cb)
1092
1093 // If a condition was specified during creation of
1094 // the suspended payment, the identical condition
1095 // must be presented again. We don't check if the
1096 // fulfillment matches the condition since we did
1097 // that in preflight.
1098 if (cond && (cond != cb))
1100 }
1101
1102 // NOTE: Escrow payments cannot be used to fund accounts.
1103 AccountID const destID = (*slep)[sfDestination];
1104 auto const sled = ctx_.view().peek(keylet::account(destID));
1105 if (!sled)
1106 return tecNO_DST;
1107
1108 if (ctx_.view().rules().enabled(featureDepositAuth))
1109 {
1110 if (auto err = verifyDepositPreauth(ctx_, account_, destID, sled);
1111 !isTesSuccess(err))
1112 return err;
1113 }
1114
1115 AccountID const account = (*slep)[sfAccount];
1116
1117 // Remove escrow from owner directory
1118 {
1119 auto const page = (*slep)[sfOwnerNode];
1120 if (!ctx_.view().dirRemove(
1121 keylet::ownerDir(account), page, k.key, true))
1122 {
1123 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1124 return tefBAD_LEDGER;
1125 }
1126 }
1127
1128 // Remove escrow from recipient's owner directory, if present.
1129 if (auto const optPage = (*slep)[~sfDestinationNode])
1130 {
1131 if (!ctx_.view().dirRemove(
1132 keylet::ownerDir(destID), *optPage, k.key, true))
1133 {
1134 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1135 return tefBAD_LEDGER;
1136 }
1137 }
1138
1139 STAmount const amount = slep->getFieldAmount(sfAmount);
1140 // Transfer amount to destination
1141 if (isXRP(amount))
1142 (*sled)[sfBalance] = (*sled)[sfBalance] + amount;
1143 else
1144 {
1145 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1146 return temDISABLED; // LCOV_EXCL_LINE
1147
1148 Rate lockedRate = slep->isFieldPresent(sfTransferRate)
1149 ? ripple::Rate(slep->getFieldU32(sfTransferRate))
1150 : parityRate;
1151 auto const issuer = amount.getIssuer();
1152 bool const createAsset = destID == account_;
1153 if (auto const ret = std::visit(
1154 [&]<typename T>(T const&) {
1155 return escrowUnlockApplyHelper<T>(
1156 ctx_.view(),
1157 lockedRate,
1158 sled,
1160 amount,
1161 issuer,
1162 account,
1163 destID,
1164 createAsset,
1165 j_);
1166 },
1167 amount.asset().value());
1168 !isTesSuccess(ret))
1169 return ret;
1170
1171 // Remove escrow from issuers owner directory, if present.
1172 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1173 {
1174 if (!ctx_.view().dirRemove(
1175 keylet::ownerDir(issuer), *optPage, k.key, true))
1176 {
1177 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1178 return tefBAD_LEDGER; // LCOV_EXCL_LINE
1179 }
1180 }
1181 }
1182
1183 ctx_.view().update(sled);
1184
1185 // Adjust source owner count
1186 auto const sle = ctx_.view().peek(keylet::account(account));
1187 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1188 ctx_.view().update(sle);
1189
1190 // Remove escrow from ledger
1191 ctx_.view().erase(slep);
1192 return tesSUCCESS;
1193}
1194
1195//------------------------------------------------------------------------------
1196
1197NotTEC
1199{
1200 if (ctx.rules.enabled(fix1543) && ctx.tx.getFlags() & tfUniversalMask)
1201 return temINVALID_FLAG;
1202
1203 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
1204 return ret;
1205
1206 return preflight2(ctx);
1207}
1208
1209template <ValidIssueType T>
1210static TER
1212 PreclaimContext const& ctx,
1213 AccountID const& account,
1214 STAmount const& amount);
1215
1216template <>
1219 PreclaimContext const& ctx,
1220 AccountID const& account,
1221 STAmount const& amount)
1222{
1223 AccountID issuer = amount.getIssuer();
1224 // If the issuer is the same as the account, return tecINTERNAL
1225 if (issuer == account)
1226 return tecINTERNAL; // LCOV_EXCL_LINE
1227
1228 // If the issuer has requireAuth set, check if the account is authorized
1229 if (auto const ter = requireAuth(ctx.view, amount.issue(), account);
1230 ter != tesSUCCESS)
1231 return ter;
1232
1233 return tesSUCCESS;
1234}
1235
1236template <>
1239 PreclaimContext const& ctx,
1240 AccountID const& account,
1241 STAmount const& amount)
1242{
1243 AccountID issuer = amount.getIssuer();
1244 // If the issuer is the same as the account, return tecINTERNAL
1245 if (issuer == account)
1246 return tecINTERNAL; // LCOV_EXCL_LINE
1247
1248 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
1249 auto const issuanceKey =
1251 auto const sleIssuance = ctx.view.read(issuanceKey);
1252 if (!sleIssuance)
1253 return tecOBJECT_NOT_FOUND;
1254
1255 // If the issuer has requireAuth set, check if the account is
1256 // authorized
1257 auto const& mptIssue = amount.get<MPTIssue>();
1258 if (auto const ter =
1259 requireAuth(ctx.view, mptIssue, account, MPTAuthType::WeakAuth);
1260 ter != tesSUCCESS)
1261 return ter;
1262
1263 return tesSUCCESS;
1264}
1265
1266TER
1268{
1269 if (ctx.view.rules().enabled(featureTokenEscrow))
1270 {
1271 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
1272 auto const slep = ctx.view.read(k);
1273 if (!slep)
1274 return tecNO_TARGET;
1275
1276 AccountID const account = (*slep)[sfAccount];
1277 STAmount const amount = (*slep)[sfAmount];
1278
1279 if (!isXRP(amount))
1280 {
1281 if (auto const ret = std::visit(
1282 [&]<typename T>(T const&) {
1283 return escrowCancelPreclaimHelper<T>(
1284 ctx, account, amount);
1285 },
1286 amount.asset().value());
1287 !isTesSuccess(ret))
1288 return ret;
1289 }
1290 }
1291 return tesSUCCESS;
1292}
1293
1294TER
1296{
1297 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
1298 auto const slep = ctx_.view().peek(k);
1299 if (!slep)
1300 {
1301 if (ctx_.view().rules().enabled(featureTokenEscrow))
1302 return tecINTERNAL; // LCOV_EXCL_LINE
1303
1304 return tecNO_TARGET;
1305 }
1306
1307 if (ctx_.view().rules().enabled(fix1571))
1308 {
1309 auto const now = ctx_.view().info().parentCloseTime;
1310
1311 // No cancel time specified: can't execute at all.
1312 if (!(*slep)[~sfCancelAfter])
1313 return tecNO_PERMISSION;
1314
1315 // Too soon: can't execute before the cancel time.
1316 if (!after(now, (*slep)[sfCancelAfter]))
1317 return tecNO_PERMISSION;
1318 }
1319 else
1320 {
1321 // Too soon?
1322 if (!(*slep)[~sfCancelAfter] ||
1324 (*slep)[sfCancelAfter])
1325 return tecNO_PERMISSION;
1326 }
1327
1328 AccountID const account = (*slep)[sfAccount];
1329
1330 // Remove escrow from owner directory
1331 {
1332 auto const page = (*slep)[sfOwnerNode];
1333 if (!ctx_.view().dirRemove(
1334 keylet::ownerDir(account), page, k.key, true))
1335 {
1336 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1337 return tefBAD_LEDGER;
1338 }
1339 }
1340
1341 // Remove escrow from recipient's owner directory, if present.
1342 if (auto const optPage = (*slep)[~sfDestinationNode]; optPage)
1343 {
1344 if (!ctx_.view().dirRemove(
1345 keylet::ownerDir((*slep)[sfDestination]),
1346 *optPage,
1347 k.key,
1348 true))
1349 {
1350 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1351 return tefBAD_LEDGER;
1352 }
1353 }
1354
1355 auto const sle = ctx_.view().peek(keylet::account(account));
1356 STAmount const amount = slep->getFieldAmount(sfAmount);
1357
1358 // Transfer amount back to the owner
1359 if (isXRP(amount))
1360 (*sle)[sfBalance] = (*sle)[sfBalance] + amount;
1361 else
1362 {
1363 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1364 return temDISABLED; // LCOV_EXCL_LINE
1365
1366 auto const issuer = amount.getIssuer();
1367 bool const createAsset = account == account_;
1368 if (auto const ret = std::visit(
1369 [&]<typename T>(T const&) {
1370 return escrowUnlockApplyHelper<T>(
1371 ctx_.view(),
1372 parityRate,
1373 slep,
1375 amount,
1376 issuer,
1377 account, // sender and receiver are the same
1378 account,
1379 createAsset,
1380 j_);
1381 },
1382 amount.asset().value());
1383 !isTesSuccess(ret))
1384 return ret; // LCOV_EXCL_LINE
1385
1386 // Remove escrow from issuers owner directory, if present.
1387 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1388 {
1389 if (!ctx_.view().dirRemove(
1390 keylet::ownerDir(issuer), *optPage, k.key, true))
1391 {
1392 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1393 return tefBAD_LEDGER; // LCOV_EXCL_LINE
1394 }
1395 }
1396 }
1397
1398 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1399 ctx_.view().update(sle);
1400
1401 // Remove escrow from ledger
1402 ctx_.view().erase(slep);
1403
1404 return tesSUCCESS;
1405}
1406
1407} // 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:1295
static NotTEC preflight(PreflightContext const &ctx)
Definition: Escrow.cpp:1198
static TER preclaim(PreclaimContext const &ctx)
Definition: Escrow.cpp:1267
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:760
static NotTEC preflight(PreflightContext const &ctx)
Definition: Escrow.cpp:628
TER doApply() override
Definition: Escrow.cpp:1013
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition: Escrow.cpp:682
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:36
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:224
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(PreflightContext const &ctx)
TER valid(PreclaimContext const &ctx, AccountID const &src)
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:26
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:703
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:91
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:1238
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
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:944
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
TER verifyDepositPreauth(ApplyContext &ctx, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst)
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:727
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:1218
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:809
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)