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/HashRouter.h>
21#include <xrpld/app/tx/detail/Escrow.h>
22#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
23#include <xrpld/conditions/Condition.h>
24#include <xrpld/conditions/Fulfillment.h>
25
26#include <xrpl/basics/Log.h>
27#include <xrpl/basics/chrono.h>
28#include <xrpl/ledger/ApplyView.h>
29#include <xrpl/ledger/CredentialHelpers.h>
30#include <xrpl/ledger/View.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
37namespace ripple {
38
39// During an EscrowFinish, the transaction must specify both
40// a condition and a fulfillment. We track whether that
41// fulfillment matches and validates the condition.
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
83{
84 auto const amount = ctx.tx[sfAmount];
85 return TxConsequences{ctx.tx, isXRP(amount) ? amount.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, AuthType::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, AuthType::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(fixIncludeKeyletFields))
542 {
543 (*slep)[sfSequence] = ctx_.tx.getSeqValue();
544 }
545
546 if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
547 {
548 auto const xferRate = transferRate(ctx_.view(), amount);
549 if (xferRate != parityRate)
550 (*slep)[sfTransferRate] = xferRate.value;
551 }
552
553 ctx_.view().insert(slep);
554
555 // Add escrow to sender's owner directory
556 {
557 auto page = ctx_.view().dirInsert(
559 escrowKeylet,
561 if (!page)
562 return tecDIR_FULL; // LCOV_EXCL_LINE
563 (*slep)[sfOwnerNode] = *page;
564 }
565
566 // If it's not a self-send, add escrow to recipient's owner directory.
567 AccountID const dest = ctx_.tx[sfDestination];
568 if (dest != account_)
569 {
570 auto page = ctx_.view().dirInsert(
571 keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
572 if (!page)
573 return tecDIR_FULL; // LCOV_EXCL_LINE
574 (*slep)[sfDestinationNode] = *page;
575 }
576
577 // IOU escrow objects are added to the issuer's owner directory to help
578 // track the total locked balance. For MPT, this isn't necessary because the
579 // locked balance is already stored directly in the MPTokenIssuance object.
580 AccountID const issuer = amount.getIssuer();
581 if (!isXRP(amount) && issuer != account_ && issuer != dest &&
582 !amount.holds<MPTIssue>())
583 {
584 auto page = ctx_.view().dirInsert(
585 keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
586 if (!page)
587 return tecDIR_FULL; // LCOV_EXCL_LINE
588 (*slep)[sfIssuerNode] = *page;
589 }
590
591 // Deduct owner's balance
592 if (isXRP(amount))
593 (*sle)[sfBalance] = (*sle)[sfBalance] - amount;
594 else
595 {
596 if (auto const ret = std::visit(
597 [&]<typename T>(T const&) {
598 return escrowLockApplyHelper<T>(
599 ctx_.view(), issuer, account_, amount, j_);
600 },
601 amount.asset().value());
602 !isTesSuccess(ret))
603 return ret; // LCOV_EXCL_LINE
604 }
605
606 // increment owner count
608 ctx_.view().update(sle);
609 return tesSUCCESS;
610}
611
612//------------------------------------------------------------------------------
613
614static bool
616{
617 using namespace ripple::cryptoconditions;
618
620
621 auto condition = Condition::deserialize(c, ec);
622 if (!condition)
623 return false;
624
625 auto fulfillment = Fulfillment::deserialize(f, ec);
626 if (!fulfillment)
627 return false;
628
629 return validate(*fulfillment, *condition);
630}
631
632NotTEC
634{
635 if (ctx.rules.enabled(fix1543) && ctx.tx.getFlags() & tfUniversalMask)
636 return temINVALID_FLAG;
637
638 if (ctx.tx.isFieldPresent(sfCredentialIDs) &&
639 !ctx.rules.enabled(featureCredentials))
640 return temDISABLED;
641
642 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
643 return ret;
644
645 auto const cb = ctx.tx[~sfCondition];
646 auto const fb = ctx.tx[~sfFulfillment];
647
648 // If you specify a condition, then you must also specify
649 // a fulfillment.
650 if (static_cast<bool>(cb) != static_cast<bool>(fb))
651 return temMALFORMED;
652
653 // Verify the transaction signature. If it doesn't work
654 // then don't do any more work.
655 {
656 auto const ret = preflight2(ctx);
657 if (!isTesSuccess(ret))
658 return ret;
659 }
660
661 if (cb && fb)
662 {
663 auto& router = ctx.app.getHashRouter();
664
665 auto const id = ctx.tx.getTransactionID();
666 auto const flags = router.getFlags(id);
667
668 // If we haven't checked the condition, check it
669 // now. Whether it passes or not isn't important
670 // in preflight.
671 if (!any(flags & (SF_CF_INVALID | SF_CF_VALID)))
672 {
673 if (checkCondition(*fb, *cb))
674 router.setFlags(id, SF_CF_VALID);
675 else
676 router.setFlags(id, SF_CF_INVALID);
677 }
678 }
679
680 if (auto const err = credentials::checkFields(ctx.tx, ctx.j);
681 !isTesSuccess(err))
682 return err;
683
684 return tesSUCCESS;
685}
686
689{
690 XRPAmount extraFee{0};
691
692 if (auto const fb = tx[~sfFulfillment])
693 {
694 extraFee += view.fees().base * (32 + (fb->size() / 16));
695 }
696
697 return Transactor::calculateBaseFee(view, tx) + extraFee;
698}
699
700template <ValidIssueType T>
701static TER
703 PreclaimContext const& ctx,
704 AccountID const& dest,
705 STAmount const& amount);
706
707template <>
710 PreclaimContext const& ctx,
711 AccountID const& dest,
712 STAmount const& amount)
713{
714 AccountID issuer = amount.getIssuer();
715 // If the issuer is the same as the account, return tesSUCCESS
716 if (issuer == dest)
717 return tesSUCCESS;
718
719 // If the issuer has requireAuth set, check if the destination is authorized
720 if (auto const ter = requireAuth(ctx.view, amount.issue(), dest);
721 ter != tesSUCCESS)
722 return ter;
723
724 // If the issuer has deep frozen the destination, return tecFROZEN
725 if (isDeepFrozen(ctx.view, dest, amount.getCurrency(), amount.getIssuer()))
726 return tecFROZEN;
727
728 return tesSUCCESS;
729}
730
731template <>
734 PreclaimContext const& ctx,
735 AccountID const& dest,
736 STAmount const& amount)
737{
738 AccountID issuer = amount.getIssuer();
739 // If the issuer is the same as the dest, return tesSUCCESS
740 if (issuer == dest)
741 return tesSUCCESS;
742
743 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
744 auto const issuanceKey =
746 auto const sleIssuance = ctx.view.read(issuanceKey);
747 if (!sleIssuance)
748 return tecOBJECT_NOT_FOUND;
749
750 // If the issuer has requireAuth set, check if the destination is
751 // authorized
752 auto const& mptIssue = amount.get<MPTIssue>();
753 if (auto const ter =
754 requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
755 ter != tesSUCCESS)
756 return ter;
757
758 // If the issuer has frozen the destination, return tecLOCKED
759 if (isFrozen(ctx.view, dest, mptIssue))
760 return tecLOCKED;
761
762 return tesSUCCESS;
763}
764
765TER
767{
768 if (ctx.view.rules().enabled(featureCredentials))
769 {
770 if (auto const err =
771 credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
772 !isTesSuccess(err))
773 return err;
774 }
775
776 if (ctx.view.rules().enabled(featureTokenEscrow))
777 {
778 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
779 auto const slep = ctx.view.read(k);
780 if (!slep)
781 return tecNO_TARGET;
782
783 AccountID const dest = (*slep)[sfDestination];
784 STAmount const amount = (*slep)[sfAmount];
785
786 if (!isXRP(amount))
787 {
788 if (auto const ret = std::visit(
789 [&]<typename T>(T const&) {
790 return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
791 },
792 amount.asset().value());
793 !isTesSuccess(ret))
794 return ret;
795 }
796 }
797 return tesSUCCESS;
798}
799
800template <ValidIssueType T>
801static TER
803 ApplyView& view,
804 Rate lockedRate,
805 std::shared_ptr<SLE> const& sleDest,
806 STAmount const& xrpBalance,
807 STAmount const& amount,
808 AccountID const& issuer,
809 AccountID const& sender,
810 AccountID const& receiver,
811 bool createAsset,
812 beast::Journal journal);
813
814template <>
817 ApplyView& view,
818 Rate lockedRate,
819 std::shared_ptr<SLE> const& sleDest,
820 STAmount const& xrpBalance,
821 STAmount const& amount,
822 AccountID const& issuer,
823 AccountID const& sender,
824 AccountID const& receiver,
825 bool createAsset,
826 beast::Journal journal)
827{
828 Keylet const trustLineKey = keylet::line(receiver, amount.issue());
829 bool const recvLow = issuer > receiver;
830 bool const senderIssuer = issuer == sender;
831 bool const receiverIssuer = issuer == receiver;
832 bool const issuerHigh = issuer > receiver;
833
834 // LCOV_EXCL_START
835 if (senderIssuer)
836 return tecINTERNAL;
837 // LCOV_EXCL_STOP
838
839 if (receiverIssuer)
840 return tesSUCCESS;
841
842 if (!view.exists(trustLineKey) && createAsset && !receiverIssuer)
843 {
844 // Can the account cover the trust line's reserve?
845 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
846 xrpBalance < view.fees().accountReserve(ownerCount + 1))
847 {
848 JLOG(journal.trace()) << "Trust line does not exist. "
849 "Insufficent reserve to create line.";
850
852 }
853
854 Currency const currency = amount.getCurrency();
855 STAmount initialBalance(amount.issue());
856 initialBalance.setIssuer(noAccount());
857
858 // clang-format off
859 if (TER const ter = trustCreate(
860 view, // payment sandbox
861 recvLow, // is dest low?
862 issuer, // source
863 receiver, // destination
864 trustLineKey.key, // ledger index
865 sleDest, // Account to add to
866 false, // authorize account
867 (sleDest->getFlags() & lsfDefaultRipple) == 0,
868 false, // freeze trust line
869 false, // deep freeze trust line
870 initialBalance, // zero initial balance
871 Issue(currency, receiver), // limit of zero
872 0, // quality in
873 0, // quality out
874 journal); // journal
875 !isTesSuccess(ter))
876 {
877 return ter; // LCOV_EXCL_LINE
878 }
879 // clang-format on
880
881 view.update(sleDest);
882 }
883
884 if (!view.exists(trustLineKey) && !receiverIssuer)
885 return tecNO_LINE;
886
887 auto const xferRate = transferRate(view, amount);
888 // update if issuer rate is less than locked rate
889 if (xferRate < lockedRate)
890 lockedRate = xferRate;
891
892 // Transfer Rate only applies when:
893 // 1. Issuer is not involved in the transfer (senderIssuer or
894 // receiverIssuer)
895 // 2. The locked rate is different from the parity rate
896
897 // NOTE: Transfer fee in escrow works a bit differently from a normal
898 // payment. In escrow, the fee is deducted from the locked/sending amount,
899 // whereas in a normal payment, the transfer fee is taken on top of the
900 // sending amount.
901 auto finalAmt = amount;
902 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
903 {
904 // compute transfer fee, if any
905 auto const xferFee = amount.value() -
906 divideRound(amount, lockedRate, amount.issue(), true);
907 // compute balance to transfer
908 finalAmt = amount.value() - xferFee;
909 }
910
911 // validate the line limit if the account submitting txn is not the receiver
912 // of the funds
913 if (!createAsset)
914 {
915 auto const sleRippleState = view.peek(trustLineKey);
916 if (!sleRippleState)
917 return tecINTERNAL; // LCOV_EXCL_LINE
918
919 // if the issuer is the high, then we use the low limit
920 // otherwise we use the high limit
921 STAmount const lineLimit = sleRippleState->getFieldAmount(
922 issuerHigh ? sfLowLimit : sfHighLimit);
923
924 STAmount lineBalance = sleRippleState->getFieldAmount(sfBalance);
925
926 // flip the sign of the line balance if the issuer is not high
927 if (!issuerHigh)
928 lineBalance.negate();
929
930 // add the final amount to the line balance
931 lineBalance += finalAmt;
932
933 // if the transfer would exceed the line limit return tecLIMIT_EXCEEDED
934 if (lineLimit < lineBalance)
935 return tecLIMIT_EXCEEDED;
936 }
937
938 // if destination is not the issuer then transfer funds
939 if (!receiverIssuer)
940 {
941 auto const ter =
942 rippleCredit(view, issuer, receiver, finalAmt, true, journal);
943 if (ter != tesSUCCESS)
944 return ter; // LCOV_EXCL_LINE
945 }
946 return tesSUCCESS;
947}
948
949template <>
952 ApplyView& view,
953 Rate lockedRate,
954 std::shared_ptr<SLE> const& sleDest,
955 STAmount const& xrpBalance,
956 STAmount const& amount,
957 AccountID const& issuer,
958 AccountID const& sender,
959 AccountID const& receiver,
960 bool createAsset,
961 beast::Journal journal)
962{
963 bool const senderIssuer = issuer == sender;
964 bool const receiverIssuer = issuer == receiver;
965
966 auto const mptID = amount.get<MPTIssue>().getMptID();
967 auto const issuanceKey = keylet::mptIssuance(mptID);
968 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
969 createAsset && !receiverIssuer)
970 {
971 if (std::uint32_t const ownerCount = {sleDest->at(sfOwnerCount)};
972 xrpBalance < view.fees().accountReserve(ownerCount + 1))
973 {
975 }
976
977 if (auto const ter =
978 MPTokenAuthorize::createMPToken(view, mptID, receiver, 0);
979 !isTesSuccess(ter))
980 {
981 return ter; // LCOV_EXCL_LINE
982 }
983
984 // update owner count.
985 adjustOwnerCount(view, sleDest, 1, journal);
986 }
987
988 if (!view.exists(keylet::mptoken(issuanceKey.key, receiver)) &&
989 !receiverIssuer)
990 return tecNO_PERMISSION;
991
992 auto const xferRate = transferRate(view, amount);
993 // update if issuer rate is less than locked rate
994 if (xferRate < lockedRate)
995 lockedRate = xferRate;
996
997 // Transfer Rate only applies when:
998 // 1. Issuer is not involved in the transfer (senderIssuer or
999 // receiverIssuer)
1000 // 2. The locked rate is different from the parity rate
1001
1002 // NOTE: Transfer fee in escrow works a bit differently from a normal
1003 // payment. In escrow, the fee is deducted from the locked/sending amount,
1004 // whereas in a normal payment, the transfer fee is taken on top of the
1005 // sending amount.
1006 auto finalAmt = amount;
1007 if ((!senderIssuer && !receiverIssuer) && lockedRate != parityRate)
1008 {
1009 // compute transfer fee, if any
1010 auto const xferFee = amount.value() -
1011 divideRound(amount, lockedRate, amount.asset(), true);
1012 // compute balance to transfer
1013 finalAmt = amount.value() - xferFee;
1014 }
1015 return rippleUnlockEscrowMPT(
1016 view,
1017 sender,
1018 receiver,
1019 finalAmt,
1020 view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt,
1021 journal);
1022}
1023
1024TER
1026{
1027 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
1028 auto const slep = ctx_.view().peek(k);
1029 if (!slep)
1030 {
1031 if (ctx_.view().rules().enabled(featureTokenEscrow))
1032 return tecINTERNAL; // LCOV_EXCL_LINE
1033
1034 return tecNO_TARGET;
1035 }
1036
1037 // If a cancel time is present, a finish operation should only succeed prior
1038 // to that time. fix1571 corrects a logic error in the check that would make
1039 // a finish only succeed strictly after the cancel time.
1040 if (ctx_.view().rules().enabled(fix1571))
1041 {
1042 auto const now = ctx_.view().info().parentCloseTime;
1043
1044 // Too soon: can't execute before the finish time
1045 if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
1046 return tecNO_PERMISSION;
1047
1048 // Too late: can't execute after the cancel time
1049 if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
1050 return tecNO_PERMISSION;
1051 }
1052 else
1053 {
1054 // Too soon?
1055 if ((*slep)[~sfFinishAfter] &&
1057 (*slep)[sfFinishAfter])
1058 return tecNO_PERMISSION;
1059
1060 // Too late?
1061 if ((*slep)[~sfCancelAfter] &&
1063 (*slep)[sfCancelAfter])
1064 return tecNO_PERMISSION;
1065 }
1066
1067 // Check cryptocondition fulfillment
1068 {
1069 auto const id = ctx_.tx.getTransactionID();
1070 auto flags = ctx_.app.getHashRouter().getFlags(id);
1071
1072 auto const cb = ctx_.tx[~sfCondition];
1073
1074 // It's unlikely that the results of the check will
1075 // expire from the hash router, but if it happens,
1076 // simply re-run the check.
1077 if (cb && !any(flags & (SF_CF_INVALID | SF_CF_VALID)))
1078 {
1079 auto const fb = ctx_.tx[~sfFulfillment];
1080
1081 if (!fb)
1082 return tecINTERNAL;
1083
1084 if (checkCondition(*fb, *cb))
1085 flags = SF_CF_VALID;
1086 else
1087 flags = SF_CF_INVALID;
1088
1089 ctx_.app.getHashRouter().setFlags(id, flags);
1090 }
1091
1092 // If the check failed, then simply return an error
1093 // and don't look at anything else.
1094 if (any(flags & SF_CF_INVALID))
1096
1097 // Check against condition in the ledger entry:
1098 auto const cond = (*slep)[~sfCondition];
1099
1100 // If a condition wasn't specified during creation,
1101 // one shouldn't be included now.
1102 if (!cond && cb)
1104
1105 // If a condition was specified during creation of
1106 // the suspended payment, the identical condition
1107 // must be presented again. We don't check if the
1108 // fulfillment matches the condition since we did
1109 // that in preflight.
1110 if (cond && (cond != cb))
1112 }
1113
1114 // NOTE: Escrow payments cannot be used to fund accounts.
1115 AccountID const destID = (*slep)[sfDestination];
1116 auto const sled = ctx_.view().peek(keylet::account(destID));
1117 if (!sled)
1118 return tecNO_DST;
1119
1120 if (ctx_.view().rules().enabled(featureDepositAuth))
1121 {
1122 if (auto err = verifyDepositPreauth(
1123 ctx_.tx, ctx_.view(), account_, destID, sled, ctx_.journal);
1124 !isTesSuccess(err))
1125 return err;
1126 }
1127
1128 AccountID const account = (*slep)[sfAccount];
1129
1130 // Remove escrow from owner directory
1131 {
1132 auto const page = (*slep)[sfOwnerNode];
1133 if (!ctx_.view().dirRemove(
1134 keylet::ownerDir(account), page, k.key, true))
1135 {
1136 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1137 return tefBAD_LEDGER;
1138 }
1139 }
1140
1141 // Remove escrow from recipient's owner directory, if present.
1142 if (auto const optPage = (*slep)[~sfDestinationNode])
1143 {
1144 if (!ctx_.view().dirRemove(
1145 keylet::ownerDir(destID), *optPage, k.key, true))
1146 {
1147 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1148 return tefBAD_LEDGER;
1149 }
1150 }
1151
1152 STAmount const amount = slep->getFieldAmount(sfAmount);
1153 // Transfer amount to destination
1154 if (isXRP(amount))
1155 (*sled)[sfBalance] = (*sled)[sfBalance] + amount;
1156 else
1157 {
1158 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1159 return temDISABLED; // LCOV_EXCL_LINE
1160
1161 Rate lockedRate = slep->isFieldPresent(sfTransferRate)
1162 ? ripple::Rate(slep->getFieldU32(sfTransferRate))
1163 : parityRate;
1164 auto const issuer = amount.getIssuer();
1165 bool const createAsset = destID == account_;
1166 if (auto const ret = std::visit(
1167 [&]<typename T>(T const&) {
1168 return escrowUnlockApplyHelper<T>(
1169 ctx_.view(),
1170 lockedRate,
1171 sled,
1173 amount,
1174 issuer,
1175 account,
1176 destID,
1177 createAsset,
1178 j_);
1179 },
1180 amount.asset().value());
1181 !isTesSuccess(ret))
1182 return ret;
1183
1184 // Remove escrow from issuers owner directory, if present.
1185 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1186 {
1187 if (!ctx_.view().dirRemove(
1188 keylet::ownerDir(issuer), *optPage, k.key, true))
1189 {
1190 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1191 return tefBAD_LEDGER; // LCOV_EXCL_LINE
1192 }
1193 }
1194 }
1195
1196 ctx_.view().update(sled);
1197
1198 // Adjust source owner count
1199 auto const sle = ctx_.view().peek(keylet::account(account));
1200 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1201 ctx_.view().update(sle);
1202
1203 // Remove escrow from ledger
1204 ctx_.view().erase(slep);
1205 return tesSUCCESS;
1206}
1207
1208//------------------------------------------------------------------------------
1209
1210NotTEC
1212{
1213 if (ctx.rules.enabled(fix1543) && ctx.tx.getFlags() & tfUniversalMask)
1214 return temINVALID_FLAG;
1215
1216 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
1217 return ret;
1218
1219 return preflight2(ctx);
1220}
1221
1222template <ValidIssueType T>
1223static TER
1225 PreclaimContext const& ctx,
1226 AccountID const& account,
1227 STAmount const& amount);
1228
1229template <>
1232 PreclaimContext const& ctx,
1233 AccountID const& account,
1234 STAmount const& amount)
1235{
1236 AccountID issuer = amount.getIssuer();
1237 // If the issuer is the same as the account, return tecINTERNAL
1238 if (issuer == account)
1239 return tecINTERNAL; // LCOV_EXCL_LINE
1240
1241 // If the issuer has requireAuth set, check if the account is authorized
1242 if (auto const ter = requireAuth(ctx.view, amount.issue(), account);
1243 ter != tesSUCCESS)
1244 return ter;
1245
1246 return tesSUCCESS;
1247}
1248
1249template <>
1252 PreclaimContext const& ctx,
1253 AccountID const& account,
1254 STAmount const& amount)
1255{
1256 AccountID issuer = amount.getIssuer();
1257 // If the issuer is the same as the account, return tecINTERNAL
1258 if (issuer == account)
1259 return tecINTERNAL; // LCOV_EXCL_LINE
1260
1261 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
1262 auto const issuanceKey =
1264 auto const sleIssuance = ctx.view.read(issuanceKey);
1265 if (!sleIssuance)
1266 return tecOBJECT_NOT_FOUND;
1267
1268 // If the issuer has requireAuth set, check if the account is
1269 // authorized
1270 auto const& mptIssue = amount.get<MPTIssue>();
1271 if (auto const ter =
1272 requireAuth(ctx.view, mptIssue, account, AuthType::WeakAuth);
1273 ter != tesSUCCESS)
1274 return ter;
1275
1276 return tesSUCCESS;
1277}
1278
1279TER
1281{
1282 if (ctx.view.rules().enabled(featureTokenEscrow))
1283 {
1284 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
1285 auto const slep = ctx.view.read(k);
1286 if (!slep)
1287 return tecNO_TARGET;
1288
1289 AccountID const account = (*slep)[sfAccount];
1290 STAmount const amount = (*slep)[sfAmount];
1291
1292 if (!isXRP(amount))
1293 {
1294 if (auto const ret = std::visit(
1295 [&]<typename T>(T const&) {
1296 return escrowCancelPreclaimHelper<T>(
1297 ctx, account, amount);
1298 },
1299 amount.asset().value());
1300 !isTesSuccess(ret))
1301 return ret;
1302 }
1303 }
1304 return tesSUCCESS;
1305}
1306
1307TER
1309{
1310 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
1311 auto const slep = ctx_.view().peek(k);
1312 if (!slep)
1313 {
1314 if (ctx_.view().rules().enabled(featureTokenEscrow))
1315 return tecINTERNAL; // LCOV_EXCL_LINE
1316
1317 return tecNO_TARGET;
1318 }
1319
1320 if (ctx_.view().rules().enabled(fix1571))
1321 {
1322 auto const now = ctx_.view().info().parentCloseTime;
1323
1324 // No cancel time specified: can't execute at all.
1325 if (!(*slep)[~sfCancelAfter])
1326 return tecNO_PERMISSION;
1327
1328 // Too soon: can't execute before the cancel time.
1329 if (!after(now, (*slep)[sfCancelAfter]))
1330 return tecNO_PERMISSION;
1331 }
1332 else
1333 {
1334 // Too soon?
1335 if (!(*slep)[~sfCancelAfter] ||
1337 (*slep)[sfCancelAfter])
1338 return tecNO_PERMISSION;
1339 }
1340
1341 AccountID const account = (*slep)[sfAccount];
1342
1343 // Remove escrow from owner directory
1344 {
1345 auto const page = (*slep)[sfOwnerNode];
1346 if (!ctx_.view().dirRemove(
1347 keylet::ownerDir(account), page, k.key, true))
1348 {
1349 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
1350 return tefBAD_LEDGER;
1351 }
1352 }
1353
1354 // Remove escrow from recipient's owner directory, if present.
1355 if (auto const optPage = (*slep)[~sfDestinationNode]; optPage)
1356 {
1357 if (!ctx_.view().dirRemove(
1358 keylet::ownerDir((*slep)[sfDestination]),
1359 *optPage,
1360 k.key,
1361 true))
1362 {
1363 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1364 return tefBAD_LEDGER;
1365 }
1366 }
1367
1368 auto const sle = ctx_.view().peek(keylet::account(account));
1369 STAmount const amount = slep->getFieldAmount(sfAmount);
1370
1371 // Transfer amount back to the owner
1372 if (isXRP(amount))
1373 (*sle)[sfBalance] = (*sle)[sfBalance] + amount;
1374 else
1375 {
1376 if (!ctx_.view().rules().enabled(featureTokenEscrow))
1377 return temDISABLED; // LCOV_EXCL_LINE
1378
1379 auto const issuer = amount.getIssuer();
1380 bool const createAsset = account == account_;
1381 if (auto const ret = std::visit(
1382 [&]<typename T>(T const&) {
1383 return escrowUnlockApplyHelper<T>(
1384 ctx_.view(),
1385 parityRate,
1386 slep,
1388 amount,
1389 issuer,
1390 account, // sender and receiver are the same
1391 account,
1392 createAsset,
1393 j_);
1394 },
1395 amount.asset().value());
1396 !isTesSuccess(ret))
1397 return ret; // LCOV_EXCL_LINE
1398
1399 // Remove escrow from issuers owner directory, if present.
1400 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
1401 {
1402 if (!ctx_.view().dirRemove(
1403 keylet::ownerDir(issuer), *optPage, k.key, true))
1404 {
1405 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
1406 return tefBAD_LEDGER; // LCOV_EXCL_LINE
1407 }
1408 }
1409 }
1410
1411 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
1412 ctx_.view().update(sle);
1413
1414 // Remove escrow from ledger
1415 ctx_.view().erase(slep);
1416
1417 return tesSUCCESS;
1418}
1419
1420} // 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()
Application & app
beast::Journal const journal
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:143
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition ApplyView.h:317
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:1308
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:1211
static TER preclaim(PreclaimContext const &ctx)
Definition Escrow.cpp:1280
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:766
static NotTEC preflight(PreflightContext const &ctx)
Definition Escrow.cpp:633
TER doApply() override
Definition Escrow.cpp:1025
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition Escrow.cpp:688
HashRouterFlags getFlags(uint256 const &key)
bool setFlags(uint256 const &key, HashRouterFlags flags)
Set the flags on a hash.
A currency issued by an account.
Definition Issue.h: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:51
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)
AccountID const account_
Definition Transactor.h:145
ApplyView & view()
Definition Transactor.h:161
beast::Journal const j_
Definition Transactor.h:143
XRPAmount mPriorBalance
Definition Transactor.h:146
XRPAmount mSourceBalance
Definition Transactor.h:147
ApplyContext & ctx_
Definition Transactor.h:141
Class describing the consequences to the account of applying a transaction if the transaction consume...
Definition applySteps.h:58
T is_same_v
T message(T... args)
NotTEC checkFields(STTx const &tx, beast::Journal j)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp: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
AccountID const & noAccount()
A placeholder for empty accounts.
TER escrowFinishPreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:709
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
@ fhIGNORE_FREEZE
Definition View.h:77
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
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2968
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 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:347
@ lsfRequireDestTag
@ lsfMPTCanEscrow
@ lsfDefaultRipple
@ lsfAllowTrustLineLocking
TER escrowCancelPreclaimHelper< MPTIssue >(PreclaimContext const &ctx, AccountID const &account, STAmount const &amount)
Definition Escrow.cpp:1251
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:1029
static TER escrowLockApplyHelper(ApplyView &view, AccountID const &issuer, AccountID const &sender, STAmount const &amount, beast::Journal journal)
@ ahIGNORE_AUTH
Definition View.h:80
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1047
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
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:247
TER rippleUnlockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, AccountID const &uGranteeID, STAmount const &netAmount, STAmount const &grossAmount, beast::Journal j)
Definition View.cpp:3065
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2464
constexpr HashRouterFlags SF_CF_VALID
Definition Escrow.cpp:43
@ tefBAD_LEDGER
Definition TER.h:170
@ tefINTERNAL
Definition TER.h:173
HashRouterFlags
Definition HashRouter.h:34
TER escrowCreatePreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &account, AccountID const &dest, STAmount const &amount)
Definition Escrow.cpp:205
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:2682
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
static bool checkCondition(Slice f, Slice c)
Definition Escrow.cpp:615
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:951
static NotTEC escrowCreatePreflightHelper(PreflightContext const &ctx)
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:759
static TER escrowCancelPreclaimHelper(PreclaimContext const &ctx, AccountID const &account, STAmount const &amount)
@ 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
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:2829
@ 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:384
constexpr HashRouterFlags SF_CF_INVALID
Definition Escrow.cpp:42
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:733
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:3239
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uSrcQualityIn, std::uint32_t uSrcQualityOut, beast::Journal j)
Create a trust line.
Definition View.cpp:1389
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
TER escrowCancelPreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &account, STAmount const &amount)
Definition Escrow.cpp:1231
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:1115
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:816
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
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:39
uint256 key
Definition Keylet.h:40
NetClock::time_point parentCloseTime
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
beast::Journal const j
Definition Transactor.h:88
State information when preflighting a tx.
Definition Transactor.h:35
beast::Journal const j
Definition Transactor.h:42
Represents a transfer rate.
Definition Rate.h:40
T time_since_epoch(T... args)
T visit(T... args)