rippled
Loading...
Searching...
No Matches
View.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 <xrpl/basics/Expected.h>
21#include <xrpl/basics/Log.h>
22#include <xrpl/basics/chrono.h>
23#include <xrpl/beast/utility/instrumentation.h>
24#include <xrpl/ledger/CredentialHelpers.h>
25#include <xrpl/ledger/ReadView.h>
26#include <xrpl/ledger/View.h>
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/Indexes.h>
29#include <xrpl/protocol/LedgerFormats.h>
30#include <xrpl/protocol/MPTIssue.h>
31#include <xrpl/protocol/Protocol.h>
32#include <xrpl/protocol/Quality.h>
33#include <xrpl/protocol/TER.h>
34#include <xrpl/protocol/TxFlags.h>
35#include <xrpl/protocol/digest.h>
36#include <xrpl/protocol/st.h>
37
38#include <type_traits>
39#include <variant>
40
41namespace ripple {
42
43namespace detail {
44
45template <
46 class V,
47 class N,
48 class = std::enable_if_t<
51bool
53 V& view,
54 uint256 const& root,
56 unsigned int& index,
57 uint256& entry)
58{
59 auto const& svIndexes = page->getFieldV256(sfIndexes);
60 XRPL_ASSERT(
61 index <= svIndexes.size(),
62 "ripple::detail::internalDirNext : index inside range");
63
64 if (index >= svIndexes.size())
65 {
66 auto const next = page->getFieldU64(sfIndexNext);
67
68 if (!next)
69 {
70 entry.zero();
71 return false;
72 }
73
74 if constexpr (std::is_const_v<N>)
75 page = view.read(keylet::page(root, next));
76 else
77 page = view.peek(keylet::page(root, next));
78
79 XRPL_ASSERT(page, "ripple::detail::internalDirNext : non-null root");
80
81 if (!page)
82 return false;
83
84 index = 0;
85
86 return internalDirNext(view, root, page, index, entry);
87 }
88
89 entry = svIndexes[index++];
90 return true;
91}
92
93template <
94 class V,
95 class N,
96 class = std::enable_if_t<
99bool
101 V& view,
102 uint256 const& root,
103 std::shared_ptr<N>& page,
104 unsigned int& index,
105 uint256& entry)
106{
107 if constexpr (std::is_const_v<N>)
108 page = view.read(keylet::page(root));
109 else
110 page = view.peek(keylet::page(root));
111
112 if (!page)
113 return false;
114
115 index = 0;
116
117 return internalDirNext(view, root, page, index, entry);
118}
119
120} // namespace detail
121
122bool
124 ApplyView& view,
125 uint256 const& root,
127 unsigned int& index,
128 uint256& entry)
129{
130 return detail::internalDirFirst(view, root, page, index, entry);
131}
132
133bool
135 ApplyView& view,
136 uint256 const& root,
138 unsigned int& index,
139 uint256& entry)
140{
141 return detail::internalDirNext(view, root, page, index, entry);
142}
143
144bool
146 ReadView const& view,
147 uint256 const& root,
149 unsigned int& index,
150 uint256& entry)
151{
152 return detail::internalDirFirst(view, root, page, index, entry);
153}
154
155bool
157 ReadView const& view,
158 uint256 const& root,
160 unsigned int& index,
161 uint256& entry)
162{
163 return detail::internalDirNext(view, root, page, index, entry);
164}
165
166//------------------------------------------------------------------------------
167//
168// Observers
169//
170//------------------------------------------------------------------------------
171
172bool
174{
175 using d = NetClock::duration;
176 using tp = NetClock::time_point;
177
178 return exp && (view.parentCloseTime() >= tp{d{*exp}});
179}
180
181bool
182isGlobalFrozen(ReadView const& view, AccountID const& issuer)
183{
184 if (isXRP(issuer))
185 return false;
186 if (auto const sle = view.read(keylet::account(issuer)))
187 return sle->isFlag(lsfGlobalFreeze);
188 return false;
189}
190
191bool
192isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
193{
194 if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())))
195 return sle->isFlag(lsfMPTLocked);
196 return false;
197}
198
199bool
200isGlobalFrozen(ReadView const& view, Asset const& asset)
201{
202 return std::visit(
203 [&]<ValidIssueType TIss>(TIss const& issue) {
204 if constexpr (std::is_same_v<TIss, Issue>)
205 return isGlobalFrozen(view, issue.getIssuer());
206 else
207 return isGlobalFrozen(view, issue);
208 },
209 asset.value());
210}
211
212bool
214 ReadView const& view,
215 AccountID const& account,
216 Currency const& currency,
217 AccountID const& issuer)
218{
219 if (isXRP(currency))
220 return false;
221 if (issuer != account)
222 {
223 // Check if the issuer froze the line
224 auto const sle = view.read(keylet::line(account, issuer, currency));
225 if (sle &&
226 sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
227 return true;
228 }
229 return false;
230}
231
232bool
234 ReadView const& view,
235 AccountID const& account,
236 MPTIssue const& mptIssue)
237{
238 if (auto const sle =
239 view.read(keylet::mptoken(mptIssue.getMptID(), account)))
240 return sle->isFlag(lsfMPTLocked);
241 return false;
242}
243
244// Can the specified account spend the specified currency issued by
245// the specified issuer or does the freeze flag prohibit it?
246bool
248 ReadView const& view,
249 AccountID const& account,
250 Currency const& currency,
251 AccountID const& issuer)
252{
253 if (isXRP(currency))
254 return false;
255 auto sle = view.read(keylet::account(issuer));
256 if (sle && sle->isFlag(lsfGlobalFreeze))
257 return true;
258 if (issuer != account)
259 {
260 // Check if the issuer froze the line
261 sle = view.read(keylet::line(account, issuer, currency));
262 if (sle &&
263 sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
264 return true;
265 }
266 return false;
267}
268
269bool
271 ReadView const& view,
272 AccountID const& account,
273 MPTIssue const& mptIssue,
274 int depth)
275{
276 return isGlobalFrozen(view, mptIssue) ||
277 isIndividualFrozen(view, account, mptIssue) ||
278 isVaultPseudoAccountFrozen(view, account, mptIssue, depth);
279}
280
281[[nodiscard]] bool
283 ReadView const& view,
284 std::initializer_list<AccountID> const& accounts,
285 MPTIssue const& mptIssue,
286 int depth)
287{
288 if (isGlobalFrozen(view, mptIssue))
289 return true;
290
291 for (auto const& account : accounts)
292 {
293 if (isIndividualFrozen(view, account, mptIssue))
294 return true;
295 }
296
297 for (auto const& account : accounts)
298 {
299 if (isVaultPseudoAccountFrozen(view, account, mptIssue, depth))
300 return true;
301 }
302
303 return false;
304}
305
306bool
308 ReadView const& view,
309 AccountID const& account,
310 MPTIssue const& mptShare,
311 int depth)
312{
313 if (!view.rules().enabled(featureSingleAssetVault))
314 return false;
315
316 if (depth >= maxAssetCheckDepth)
317 return true; // LCOV_EXCL_LINE
318
319 auto const mptIssuance =
320 view.read(keylet::mptIssuance(mptShare.getMptID()));
321 if (mptIssuance == nullptr)
322 return false; // zero MPToken won't block deletion of MPTokenIssuance
323
324 auto const issuer = mptIssuance->getAccountID(sfIssuer);
325 auto const mptIssuer = view.read(keylet::account(issuer));
326 if (mptIssuer == nullptr)
327 { // LCOV_EXCL_START
328 UNREACHABLE("ripple::isVaultPseudoAccountFrozen : null MPToken issuer");
329 return false;
330 } // LCOV_EXCL_STOP
331
332 if (!mptIssuer->isFieldPresent(sfVaultID))
333 return false; // not a Vault pseudo-account, common case
334
335 auto const vault =
336 view.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
337 if (vault == nullptr)
338 { // LCOV_EXCL_START
339 UNREACHABLE("ripple::isVaultPseudoAccountFrozen : null vault");
340 return false;
341 } // LCOV_EXCL_STOP
342
343 return isAnyFrozen(view, {issuer, account}, vault->at(sfAsset), depth + 1);
344}
345
346bool
348 ReadView const& view,
349 AccountID const& account,
350 Currency const& currency,
351 AccountID const& issuer)
352{
353 if (isXRP(currency))
354 {
355 return false;
356 }
357
358 if (issuer == account)
359 {
360 return false;
361 }
362
363 auto const sle = view.read(keylet::line(account, issuer, currency));
364 if (!sle)
365 {
366 return false;
367 }
368
369 return sle->isFlag(lsfHighDeepFreeze) || sle->isFlag(lsfLowDeepFreeze);
370}
371
372bool
374 ReadView const& view,
375 AccountID const& account,
376 Issue const& asset,
377 Issue const& asset2)
378{
379 return isFrozen(view, account, asset.currency, asset.account) ||
380 isFrozen(view, account, asset2.currency, asset2.account);
381}
382
383STAmount
385 ReadView const& view,
386 AccountID const& account,
387 Currency const& currency,
388 AccountID const& issuer,
389 FreezeHandling zeroIfFrozen,
391{
392 STAmount amount;
393 if (isXRP(currency))
394 {
395 return {xrpLiquid(view, account, 0, j)};
396 }
397
398 // IOU: Return balance on trust line modulo freeze
399 auto const sle = view.read(keylet::line(account, issuer, currency));
400 auto const allowBalance = [&]() {
401 if (!sle)
402 {
403 return false;
404 }
405
406 if (zeroIfFrozen == fhZERO_IF_FROZEN)
407 {
408 if (isFrozen(view, account, currency, issuer) ||
409 isDeepFrozen(view, account, currency, issuer))
410 {
411 return false;
412 }
413
414 // when fixFrozenLPTokenTransfer is enabled, if currency is lptoken,
415 // we need to check if the associated assets have been frozen
416 if (view.rules().enabled(fixFrozenLPTokenTransfer))
417 {
418 auto const sleIssuer = view.read(keylet::account(issuer));
419 if (!sleIssuer)
420 {
421 return false; // LCOV_EXCL_LINE
422 }
423 else if (sleIssuer->isFieldPresent(sfAMMID))
424 {
425 auto const sleAmm =
426 view.read(keylet::amm((*sleIssuer)[sfAMMID]));
427
428 if (!sleAmm ||
430 view,
431 account,
432 (*sleAmm)[sfAsset].get<Issue>(),
433 (*sleAmm)[sfAsset2].get<Issue>()))
434 {
435 return false;
436 }
437 }
438 }
439 }
440
441 return true;
442 }();
443
444 if (allowBalance)
445 {
446 amount = sle->getFieldAmount(sfBalance);
447 if (account > issuer)
448 {
449 // Put balance in account terms.
450 amount.negate();
451 }
452 amount.setIssuer(issuer);
453 }
454 else
455 {
456 amount.clear(Issue{currency, issuer});
457 }
458
459 JLOG(j.trace()) << "accountHolds:"
460 << " account=" << to_string(account)
461 << " amount=" << amount.getFullText();
462
463 return view.balanceHook(account, issuer, amount);
464}
465
466STAmount
468 ReadView const& view,
469 AccountID const& account,
470 Issue const& issue,
471 FreezeHandling zeroIfFrozen,
473{
474 return accountHolds(
475 view, account, issue.currency, issue.account, zeroIfFrozen, j);
476}
477
478STAmount
480 ReadView const& view,
481 AccountID const& account,
482 MPTIssue const& mptIssue,
483 FreezeHandling zeroIfFrozen,
484 AuthHandling zeroIfUnauthorized,
486{
487 STAmount amount;
488
489 auto const sleMpt =
490 view.read(keylet::mptoken(mptIssue.getMptID(), account));
491
492 if (!sleMpt)
493 amount.clear(mptIssue);
494 else if (
495 zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, mptIssue))
496 amount.clear(mptIssue);
497 else
498 {
499 amount = STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)};
500
501 // Only if auth check is needed, as it needs to do an additional read
502 // operation. Note featureSingleAssetVault will affect error codes.
503 if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
504 view.rules().enabled(featureSingleAssetVault))
505 {
506 if (auto const err =
507 requireAuth(view, mptIssue, account, AuthType::StrongAuth);
508 !isTesSuccess(err))
509 amount.clear(mptIssue);
510 }
511 else if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED)
512 {
513 auto const sleIssuance =
514 view.read(keylet::mptIssuance(mptIssue.getMptID()));
515
516 // if auth is enabled on the issuance and mpt is not authorized,
517 // clear amount
518 if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) &&
519 !sleMpt->isFlag(lsfMPTAuthorized))
520 amount.clear(mptIssue);
521 }
522 }
523
524 return amount;
525}
526
527[[nodiscard]] STAmount
529 ReadView const& view,
530 AccountID const& account,
531 Asset const& asset,
532 FreezeHandling zeroIfFrozen,
533 AuthHandling zeroIfUnauthorized,
535{
536 return std::visit(
537 [&](auto const& value) {
538 if constexpr (std::is_same_v<
539 std::remove_cvref_t<decltype(value)>,
540 Issue>)
541 {
542 return accountHolds(view, account, value, zeroIfFrozen, j);
543 }
544 return accountHolds(
545 view, account, value, zeroIfFrozen, zeroIfUnauthorized, j);
546 },
547 asset.value());
548}
549
550STAmount
552 ReadView const& view,
553 AccountID const& id,
554 STAmount const& saDefault,
555 FreezeHandling freezeHandling,
557{
558 if (!saDefault.native() && saDefault.getIssuer() == id)
559 return saDefault;
560
561 return accountHolds(
562 view,
563 id,
564 saDefault.getCurrency(),
565 saDefault.getIssuer(),
566 freezeHandling,
567 j);
568}
569
570// Prevent ownerCount from wrapping under error conditions.
571//
572// adjustment allows the ownerCount to be adjusted up or down in multiple steps.
573// If id != std::nullopt, then do error reporting.
574//
575// Returns adjusted owner count.
576static std::uint32_t
579 std::int32_t adjustment,
582{
583 std::uint32_t adjusted{current + adjustment};
584 if (adjustment > 0)
585 {
586 // Overflow is well defined on unsigned
587 if (adjusted < current)
588 {
589 if (id)
590 {
591 JLOG(j.fatal())
592 << "Account " << *id << " owner count exceeds max!";
593 }
595 }
596 }
597 else
598 {
599 // Underflow is well defined on unsigned
600 if (adjusted > current)
601 {
602 if (id)
603 {
604 JLOG(j.fatal())
605 << "Account " << *id << " owner count set below 0!";
606 }
607 adjusted = 0;
608 XRPL_ASSERT(!id, "ripple::confineOwnerCount : id is not set");
609 }
610 }
611 return adjusted;
612}
613
614XRPAmount
616 ReadView const& view,
617 AccountID const& id,
618 std::int32_t ownerCountAdj,
620{
621 auto const sle = view.read(keylet::account(id));
622 if (sle == nullptr)
623 return beast::zero;
624
625 // Return balance minus reserve
626 std::uint32_t const ownerCount = confineOwnerCount(
627 view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj);
628
629 // Pseudo-accounts have no reserve requirement
630 auto const reserve = isPseudoAccount(sle)
631 ? XRPAmount{0}
632 : view.fees().accountReserve(ownerCount);
633
634 auto const fullBalance = sle->getFieldAmount(sfBalance);
635
636 auto const balance = view.balanceHook(id, xrpAccount(), fullBalance);
637
638 STAmount const amount =
639 (balance < reserve) ? STAmount{0} : balance - reserve;
640
641 JLOG(j.trace()) << "accountHolds:"
642 << " account=" << to_string(id)
643 << " amount=" << amount.getFullText()
644 << " fullBalance=" << fullBalance.getFullText()
645 << " balance=" << balance.getFullText()
646 << " reserve=" << reserve << " ownerCount=" << ownerCount
647 << " ownerCountAdj=" << ownerCountAdj;
648
649 return amount.xrp();
650}
651
652void
654 ReadView const& view,
655 Keylet const& root,
656 std::function<void(std::shared_ptr<SLE const> const&)> const& f)
657{
658 XRPL_ASSERT(
659 root.type == ltDIR_NODE, "ripple::forEachItem : valid root type");
660
661 if (root.type != ltDIR_NODE)
662 return;
663
664 auto pos = root;
665
666 while (true)
667 {
668 auto sle = view.read(pos);
669 if (!sle)
670 return;
671 for (auto const& key : sle->getFieldV256(sfIndexes))
672 f(view.read(keylet::child(key)));
673 auto const next = sle->getFieldU64(sfIndexNext);
674 if (!next)
675 return;
676 pos = keylet::page(root, next);
677 }
678}
679
680bool
682 ReadView const& view,
683 Keylet const& root,
684 uint256 const& after,
685 std::uint64_t const hint,
686 unsigned int limit,
687 std::function<bool(std::shared_ptr<SLE const> const&)> const& f)
688{
689 XRPL_ASSERT(
690 root.type == ltDIR_NODE, "ripple::forEachItemAfter : valid root type");
691
692 if (root.type != ltDIR_NODE)
693 return false;
694
695 auto currentIndex = root;
696
697 // If startAfter is not zero try jumping to that page using the hint
698 if (after.isNonZero())
699 {
700 auto const hintIndex = keylet::page(root, hint);
701
702 if (auto hintDir = view.read(hintIndex))
703 {
704 for (auto const& key : hintDir->getFieldV256(sfIndexes))
705 {
706 if (key == after)
707 {
708 // We found the hint, we can start here
709 currentIndex = hintIndex;
710 break;
711 }
712 }
713 }
714
715 bool found = false;
716 for (;;)
717 {
718 auto const ownerDir = view.read(currentIndex);
719 if (!ownerDir)
720 return found;
721 for (auto const& key : ownerDir->getFieldV256(sfIndexes))
722 {
723 if (!found)
724 {
725 if (key == after)
726 found = true;
727 }
728 else if (f(view.read(keylet::child(key))) && limit-- <= 1)
729 {
730 return found;
731 }
732 }
733
734 auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
735 if (uNodeNext == 0)
736 return found;
737 currentIndex = keylet::page(root, uNodeNext);
738 }
739 }
740 else
741 {
742 for (;;)
743 {
744 auto const ownerDir = view.read(currentIndex);
745 if (!ownerDir)
746 return true;
747 for (auto const& key : ownerDir->getFieldV256(sfIndexes))
748 if (f(view.read(keylet::child(key))) && limit-- <= 1)
749 return true;
750 auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
751 if (uNodeNext == 0)
752 return true;
753 currentIndex = keylet::page(root, uNodeNext);
754 }
755 }
756}
757
758Rate
759transferRate(ReadView const& view, AccountID const& issuer)
760{
761 auto const sle = view.read(keylet::account(issuer));
762
763 if (sle && sle->isFieldPresent(sfTransferRate))
764 return Rate{sle->getFieldU32(sfTransferRate)};
765
766 return parityRate;
767}
768
769Rate
770transferRate(ReadView const& view, MPTID const& issuanceID)
771{
772 // fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000
773 // For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000
774 // which represents 50% of 1,000,000,000
775 if (auto const sle = view.read(keylet::mptIssuance(issuanceID));
776 sle && sle->isFieldPresent(sfTransferFee))
777 return Rate{1'000'000'000u + 10'000 * sle->getFieldU16(sfTransferFee)};
778
779 return parityRate;
780}
781
782Rate
783transferRate(ReadView const& view, STAmount const& amount)
784{
785 return std::visit(
786 [&]<ValidIssueType TIss>(TIss const& issue) {
787 if constexpr (std::is_same_v<TIss, Issue>)
788 return transferRate(view, issue.getIssuer());
789 else
790 return transferRate(view, issue.getMptID());
791 },
792 amount.asset().value());
793}
794
795bool
797 ReadView const& validLedger,
798 ReadView const& testLedger,
800 char const* reason)
801{
802 bool ret = true;
803
804 if (validLedger.info().seq < testLedger.info().seq)
805 {
806 // valid -> ... -> test
807 auto hash = hashOfSeq(
808 testLedger,
809 validLedger.info().seq,
810 beast::Journal{beast::Journal::getNullSink()});
811 if (hash && (*hash != validLedger.info().hash))
812 {
813 JLOG(s) << reason << " incompatible with valid ledger";
814
815 JLOG(s) << "Hash(VSeq): " << to_string(*hash);
816
817 ret = false;
818 }
819 }
820 else if (validLedger.info().seq > testLedger.info().seq)
821 {
822 // test -> ... -> valid
823 auto hash = hashOfSeq(
824 validLedger,
825 testLedger.info().seq,
826 beast::Journal{beast::Journal::getNullSink()});
827 if (hash && (*hash != testLedger.info().hash))
828 {
829 JLOG(s) << reason << " incompatible preceding ledger";
830
831 JLOG(s) << "Hash(NSeq): " << to_string(*hash);
832
833 ret = false;
834 }
835 }
836 else if (
837 (validLedger.info().seq == testLedger.info().seq) &&
838 (validLedger.info().hash != testLedger.info().hash))
839 {
840 // Same sequence number, different hash
841 JLOG(s) << reason << " incompatible ledger";
842
843 ret = false;
844 }
845
846 if (!ret)
847 {
848 JLOG(s) << "Val: " << validLedger.info().seq << " "
849 << to_string(validLedger.info().hash);
850
851 JLOG(s) << "New: " << testLedger.info().seq << " "
852 << to_string(testLedger.info().hash);
853 }
854
855 return ret;
856}
857
858bool
860 uint256 const& validHash,
861 LedgerIndex validIndex,
862 ReadView const& testLedger,
864 char const* reason)
865{
866 bool ret = true;
867
868 if (testLedger.info().seq > validIndex)
869 {
870 // Ledger we are testing follows last valid ledger
871 auto hash = hashOfSeq(
872 testLedger,
873 validIndex,
875 if (hash && (*hash != validHash))
876 {
877 JLOG(s) << reason << " incompatible following ledger";
878 JLOG(s) << "Hash(VSeq): " << to_string(*hash);
879
880 ret = false;
881 }
882 }
883 else if (
884 (validIndex == testLedger.info().seq) &&
885 (testLedger.info().hash != validHash))
886 {
887 JLOG(s) << reason << " incompatible ledger";
888
889 ret = false;
890 }
891
892 if (!ret)
893 {
894 JLOG(s) << "Val: " << validIndex << " " << to_string(validHash);
895
896 JLOG(s) << "New: " << testLedger.info().seq << " "
897 << to_string(testLedger.info().hash);
898 }
899
900 return ret;
901}
902
903bool
904dirIsEmpty(ReadView const& view, Keylet const& k)
905{
906 auto const sleNode = view.read(k);
907 if (!sleNode)
908 return true;
909 if (!sleNode->getFieldV256(sfIndexes).empty())
910 return false;
911 // The first page of a directory may legitimately be empty even if there
912 // are other pages (the first page is the anchor page) so check to see if
913 // there is another page. If there is, the directory isn't empty.
914 return sleNode->getFieldU64(sfIndexNext) == 0;
915}
916
919{
920 std::set<uint256> amendments;
921
922 if (auto const sle = view.read(keylet::amendments()))
923 {
924 if (sle->isFieldPresent(sfAmendments))
925 {
926 auto const& v = sle->getFieldV256(sfAmendments);
927 amendments.insert(v.begin(), v.end());
928 }
929 }
930
931 return amendments;
932}
933
936{
938
939 if (auto const sle = view.read(keylet::amendments()))
940 {
941 if (sle->isFieldPresent(sfMajorities))
942 {
943 using tp = NetClock::time_point;
944 using d = tp::duration;
945
946 auto const majorities = sle->getFieldArray(sfMajorities);
947
948 for (auto const& m : majorities)
949 ret[m.getFieldH256(sfAmendment)] =
950 tp(d(m.getFieldU32(sfCloseTime)));
951 }
952 }
953
954 return ret;
955}
956
958hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
959{
960 // Easy cases...
961 if (seq > ledger.seq())
962 {
963 JLOG(journal.warn())
964 << "Can't get seq " << seq << " from " << ledger.seq() << " future";
965 return std::nullopt;
966 }
967 if (seq == ledger.seq())
968 return ledger.info().hash;
969 if (seq == (ledger.seq() - 1))
970 return ledger.info().parentHash;
971
972 if (int diff = ledger.seq() - seq; diff <= 256)
973 {
974 // Within 256...
975 auto const hashIndex = ledger.read(keylet::skip());
976 if (hashIndex)
977 {
978 XRPL_ASSERT(
979 hashIndex->getFieldU32(sfLastLedgerSequence) ==
980 (ledger.seq() - 1),
981 "ripple::hashOfSeq : matching ledger sequence");
982 STVector256 vec = hashIndex->getFieldV256(sfHashes);
983 if (vec.size() >= diff)
984 return vec[vec.size() - diff];
985 JLOG(journal.warn())
986 << "Ledger " << ledger.seq() << " missing hash for " << seq
987 << " (" << vec.size() << "," << diff << ")";
988 }
989 else
990 {
991 JLOG(journal.warn())
992 << "Ledger " << ledger.seq() << ":" << ledger.info().hash
993 << " missing normal list";
994 }
995 }
996
997 if ((seq & 0xff) != 0)
998 {
999 JLOG(journal.debug())
1000 << "Can't get seq " << seq << " from " << ledger.seq() << " past";
1001 return std::nullopt;
1002 }
1003
1004 // in skiplist
1005 auto const hashIndex = ledger.read(keylet::skip(seq));
1006 if (hashIndex)
1007 {
1008 auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
1009 XRPL_ASSERT(lastSeq >= seq, "ripple::hashOfSeq : minimum last ledger");
1010 XRPL_ASSERT(
1011 (lastSeq & 0xff) == 0, "ripple::hashOfSeq : valid last ledger");
1012 auto const diff = (lastSeq - seq) >> 8;
1013 STVector256 vec = hashIndex->getFieldV256(sfHashes);
1014 if (vec.size() > diff)
1015 return vec[vec.size() - diff - 1];
1016 }
1017 JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq()
1018 << " error";
1019 return std::nullopt;
1020}
1021
1022//------------------------------------------------------------------------------
1023//
1024// Modifiers
1025//
1026//------------------------------------------------------------------------------
1027
1028void
1030 ApplyView& view,
1031 std::shared_ptr<SLE> const& sle,
1032 std::int32_t amount,
1034{
1035 if (!sle)
1036 return;
1037 XRPL_ASSERT(amount, "ripple::adjustOwnerCount : nonzero amount input");
1038 std::uint32_t const current{sle->getFieldU32(sfOwnerCount)};
1039 AccountID const id = (*sle)[sfAccount];
1040 std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
1041 view.adjustOwnerCountHook(id, current, adjusted);
1042 sle->at(sfOwnerCount) = adjusted;
1043 view.update(sle);
1044}
1045
1048{
1049 return [&account](std::shared_ptr<SLE> const& sle) {
1050 (*sle)[sfOwner] = account;
1051 };
1052}
1053
1054TER
1056{
1057 auto const page = view.dirInsert(
1058 keylet::ownerDir(owner), object->key(), describeOwnerDir(owner));
1059 if (!page)
1060 return tecDIR_FULL; // LCOV_EXCL_LINE
1061 object->setFieldU64(sfOwnerNode, *page);
1062 return tesSUCCESS;
1063}
1064
1066pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey)
1067{
1068 // This number must not be changed without an amendment
1069 constexpr std::uint16_t maxAccountAttempts = 256;
1070 for (std::uint16_t i = 0; i < maxAccountAttempts; ++i)
1071 {
1072 ripesha_hasher rsh;
1073 auto const hash = sha512Half(i, view.info().parentHash, pseudoOwnerKey);
1074 rsh(hash.data(), hash.size());
1075 AccountID const ret{static_cast<ripesha_hasher::result_type>(rsh)};
1076 if (!view.read(keylet::account(ret)))
1077 return ret;
1078 }
1079 return beast::zero;
1080}
1081
1082// Pseudo-account designator fields MUST be maintained by including the
1083// SField::sMD_PseudoAccount flag in the SField definition. (Don't forget to
1084// "| SField::sMD_Default"!) The fields do NOT need to be amendment-gated,
1085// since a non-active amendment will not set any field, by definition.
1086// Specific properties of a pseudo-account are NOT checked here, that's what
1087// InvariantCheck is for.
1088[[nodiscard]] std::vector<SField const*> const&
1090{
1091 static std::vector<SField const*> const pseudoFields = []() {
1092 auto const ar = LedgerFormats::getInstance().findByType(ltACCOUNT_ROOT);
1093 if (!ar)
1094 {
1095 // LCOV_EXCL_START
1096 LogicError(
1097 "ripple::isPseudoAccount : unable to find account root ledger "
1098 "format");
1099 // LCOV_EXCL_STOP
1100 }
1101 auto const& soTemplate = ar->getSOTemplate();
1102
1103 std::vector<SField const*> pseudoFields;
1104 for (auto const& field : soTemplate)
1105 {
1106 if (field.sField().shouldMeta(SField::sMD_PseudoAccount))
1107 pseudoFields.emplace_back(&field.sField());
1108 }
1109 return pseudoFields;
1110 }();
1111 return pseudoFields;
1112}
1113
1114[[nodiscard]] bool
1116{
1117 auto const& fields = getPseudoAccountFields();
1118
1119 // Intentionally use defensive coding here because it's cheap and makes the
1120 // semantics of true return value clean.
1121 return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
1123 fields.begin(), fields.end(), [&sleAcct](SField const* sf) -> bool {
1124 return sleAcct->isFieldPresent(*sf);
1125 }) > 0;
1126}
1127
1128Expected<std::shared_ptr<SLE>, TER>
1130 ApplyView& view,
1131 uint256 const& pseudoOwnerKey,
1132 SField const& ownerField)
1133{
1134 auto const& fields = getPseudoAccountFields();
1135 XRPL_ASSERT(
1137 fields.begin(),
1138 fields.end(),
1139 [&ownerField](SField const* sf) -> bool {
1140 return *sf == ownerField;
1141 }) == 1,
1142 "ripple::createPseudoAccount : valid owner field");
1143
1144 auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey);
1145 if (accountId == beast::zero)
1146 return Unexpected(tecDUPLICATE);
1147
1148 // Create pseudo-account.
1149 auto account = std::make_shared<SLE>(keylet::account(accountId));
1150 account->setAccountID(sfAccount, accountId);
1151 account->setFieldAmount(sfBalance, STAmount{});
1152
1153 // Pseudo-accounts can't submit transactions, so set the sequence number
1154 // to 0 to make them easier to spot and verify, and add an extra level
1155 // of protection.
1156 std::uint32_t const seqno = //
1157 view.rules().enabled(featureSingleAssetVault) //
1158 ? 0 //
1159 : view.seq();
1160 account->setFieldU32(sfSequence, seqno);
1161 // Ignore reserves requirement, disable the master key, allow default
1162 // rippling, and enable deposit authorization to prevent payments into
1163 // pseudo-account.
1164 account->setFieldU32(
1166 // Link the pseudo-account with its owner object.
1167 account->setFieldH256(ownerField, pseudoOwnerKey);
1168
1169 view.insert(account);
1170
1171 return account;
1172}
1173
1174[[nodiscard]] TER
1175canAddHolding(ReadView const& view, Issue const& issue)
1176{
1177 if (issue.native())
1178 return tesSUCCESS; // No special checks for XRP
1179
1180 auto const issuer = view.read(keylet::account(issue.getIssuer()));
1181 if (!issuer)
1182 return terNO_ACCOUNT;
1183 else if (!issuer->isFlag(lsfDefaultRipple))
1184 return terNO_RIPPLE;
1185
1186 return tesSUCCESS;
1187}
1188
1189[[nodiscard]] TER
1190canAddHolding(ReadView const& view, MPTIssue const& mptIssue)
1191{
1192 auto mptID = mptIssue.getMptID();
1193 auto issuance = view.read(keylet::mptIssuance(mptID));
1194 if (!issuance)
1195 return tecOBJECT_NOT_FOUND;
1196 if (!issuance->isFlag(lsfMPTCanTransfer))
1197 return tecNO_AUTH;
1198
1199 return tesSUCCESS;
1200}
1201
1202[[nodiscard]] TER
1203canAddHolding(ReadView const& view, Asset const& asset)
1204{
1205 return std::visit(
1206 [&]<ValidIssueType TIss>(TIss const& issue) -> TER {
1207 return canAddHolding(view, issue);
1208 },
1209 asset.value());
1210}
1211
1212[[nodiscard]] TER
1214 ApplyView& view,
1215 AccountID const& accountID,
1216 XRPAmount priorBalance,
1217 Issue const& issue,
1218 beast::Journal journal)
1219{
1220 // Every account can hold XRP.
1221 if (issue.native())
1222 return tesSUCCESS;
1223
1224 auto const& issuerId = issue.getIssuer();
1225 auto const& currency = issue.currency;
1226 if (isGlobalFrozen(view, issuerId))
1227 return tecFROZEN; // LCOV_EXCL_LINE
1228
1229 auto const& srcId = issuerId;
1230 auto const& dstId = accountID;
1231 auto const high = srcId > dstId;
1232 auto const index = keylet::line(srcId, dstId, currency);
1233 auto const sleSrc = view.peek(keylet::account(srcId));
1234 auto const sleDst = view.peek(keylet::account(dstId));
1235 if (!sleDst || !sleSrc)
1236 return tefINTERNAL; // LCOV_EXCL_LINE
1237 if (!sleSrc->isFlag(lsfDefaultRipple))
1238 return tecINTERNAL; // LCOV_EXCL_LINE
1239 // If the line already exists, don't create it again.
1240 if (view.read(index))
1241 return tecDUPLICATE;
1242 return trustCreate(
1243 view,
1244 high,
1245 srcId,
1246 dstId,
1247 index.key,
1248 sleDst,
1249 /*auth=*/false,
1250 /*noRipple=*/true,
1251 /*freeze=*/false,
1252 /*deepFreeze*/ false,
1253 /*balance=*/STAmount{Issue{currency, noAccount()}},
1254 /*limit=*/STAmount{Issue{currency, dstId}},
1255 /*qualityIn=*/0,
1256 /*qualityOut=*/0,
1257 journal);
1258}
1259
1260[[nodiscard]] TER
1262 ApplyView& view,
1263 AccountID const& accountID,
1264 XRPAmount priorBalance,
1265 MPTIssue const& mptIssue,
1266 beast::Journal journal)
1267{
1268 auto const& mptID = mptIssue.getMptID();
1269 auto const mpt = view.peek(keylet::mptIssuance(mptID));
1270 if (!mpt)
1271 return tefINTERNAL; // LCOV_EXCL_LINE
1272 if (mpt->isFlag(lsfMPTLocked))
1273 return tefINTERNAL; // LCOV_EXCL_LINE
1274 if (view.peek(keylet::mptoken(mptID, accountID)))
1275 return tecDUPLICATE;
1276
1277 return authorizeMPToken(view, priorBalance, mptID, accountID, journal);
1278}
1279
1280[[nodiscard]] TER
1282 ApplyView& view,
1283 XRPAmount const& priorBalance,
1284 MPTID const& mptIssuanceID,
1285 AccountID const& account,
1286 beast::Journal journal,
1287 std::uint32_t flags,
1288 std::optional<AccountID> holderID)
1289{
1290 auto const sleAcct = view.peek(keylet::account(account));
1291 if (!sleAcct)
1292 return tecINTERNAL;
1293
1294 // If the account that submitted the tx is a holder
1295 // Note: `account_` is holder's account
1296 // `holderID` is NOT used
1297 if (!holderID)
1298 {
1299 // When a holder wants to unauthorize/delete a MPT, the ledger must
1300 // - delete mptokenKey from owner directory
1301 // - delete the MPToken
1302 if (flags & tfMPTUnauthorize)
1303 {
1304 auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
1305 auto const sleMpt = view.peek(mptokenKey);
1306 if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0)
1307 return tecINTERNAL; // LCOV_EXCL_LINE
1308
1309 if (!view.dirRemove(
1310 keylet::ownerDir(account),
1311 (*sleMpt)[sfOwnerNode],
1312 sleMpt->key(),
1313 false))
1314 return tecINTERNAL; // LCOV_EXCL_LINE
1315
1316 adjustOwnerCount(view, sleAcct, -1, journal);
1317
1318 view.erase(sleMpt);
1319 return tesSUCCESS;
1320 }
1321
1322 // A potential holder wants to authorize/hold a mpt, the ledger must:
1323 // - add the new mptokenKey to the owner directory
1324 // - create the MPToken object for the holder
1325
1326 // The reserve that is required to create the MPToken. Note
1327 // that although the reserve increases with every item
1328 // an account owns, in the case of MPTokens we only
1329 // *enforce* a reserve if the user owns more than two
1330 // items. This is similar to the reserve requirements of trust lines.
1331 std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount);
1332 XRPAmount const reserveCreate(
1333 (uOwnerCount < 2) ? XRPAmount(beast::zero)
1334 : view.fees().accountReserve(uOwnerCount + 1));
1335
1336 if (priorBalance < reserveCreate)
1338
1339 auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
1340 auto mptoken = std::make_shared<SLE>(mptokenKey);
1341 if (auto ter = dirLink(view, account, mptoken))
1342 return ter; // LCOV_EXCL_LINE
1343
1344 (*mptoken)[sfAccount] = account;
1345 (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
1346 (*mptoken)[sfFlags] = 0;
1347 view.insert(mptoken);
1348
1349 // Update owner count.
1350 adjustOwnerCount(view, sleAcct, 1, journal);
1351
1352 return tesSUCCESS;
1353 }
1354
1355 auto const sleMptIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
1356 if (!sleMptIssuance)
1357 return tecINTERNAL;
1358
1359 // If the account that submitted this tx is the issuer of the MPT
1360 // Note: `account_` is issuer's account
1361 // `holderID` is holder's account
1362 if (account != (*sleMptIssuance)[sfIssuer])
1363 return tecINTERNAL;
1364
1365 auto const sleMpt = view.peek(keylet::mptoken(mptIssuanceID, *holderID));
1366 if (!sleMpt)
1367 return tecINTERNAL;
1368
1369 std::uint32_t const flagsIn = sleMpt->getFieldU32(sfFlags);
1370 std::uint32_t flagsOut = flagsIn;
1371
1372 // Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on
1373 // their MPToken
1374 if (flags & tfMPTUnauthorize)
1375 flagsOut &= ~lsfMPTAuthorized;
1376 // Issuer wants to authorize a holder, set lsfMPTAuthorized on their
1377 // MPToken
1378 else
1379 flagsOut |= lsfMPTAuthorized;
1380
1381 if (flagsIn != flagsOut)
1382 sleMpt->setFieldU32(sfFlags, flagsOut);
1383
1384 view.update(sleMpt);
1385 return tesSUCCESS;
1386}
1387
1388TER
1390 ApplyView& view,
1391 bool const bSrcHigh,
1392 AccountID const& uSrcAccountID,
1393 AccountID const& uDstAccountID,
1394 uint256 const& uIndex, // --> ripple state entry
1395 SLE::ref sleAccount, // --> the account being set.
1396 bool const bAuth, // --> authorize account.
1397 bool const bNoRipple, // --> others cannot ripple through
1398 bool const bFreeze, // --> funds cannot leave
1399 bool bDeepFreeze, // --> can neither receive nor send funds
1400 STAmount const& saBalance, // --> balance of account being set.
1401 // Issuer should be noAccount()
1402 STAmount const& saLimit, // --> limit for account being set.
1403 // Issuer should be the account being set.
1404 std::uint32_t uQualityIn,
1405 std::uint32_t uQualityOut,
1407{
1408 JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", "
1409 << to_string(uDstAccountID) << ", "
1410 << saBalance.getFullText();
1411
1412 auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
1413 auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
1414
1415 auto const sleRippleState = std::make_shared<SLE>(ltRIPPLE_STATE, uIndex);
1416 view.insert(sleRippleState);
1417
1418 auto lowNode = view.dirInsert(
1419 keylet::ownerDir(uLowAccountID),
1420 sleRippleState->key(),
1421 describeOwnerDir(uLowAccountID));
1422
1423 if (!lowNode)
1424 return tecDIR_FULL;
1425
1426 auto highNode = view.dirInsert(
1427 keylet::ownerDir(uHighAccountID),
1428 sleRippleState->key(),
1429 describeOwnerDir(uHighAccountID));
1430
1431 if (!highNode)
1432 return tecDIR_FULL;
1433
1434 bool const bSetDst = saLimit.getIssuer() == uDstAccountID;
1435 bool const bSetHigh = bSrcHigh ^ bSetDst;
1436
1437 XRPL_ASSERT(sleAccount, "ripple::trustCreate : non-null SLE");
1438 if (!sleAccount)
1439 return tefINTERNAL;
1440
1441 XRPL_ASSERT(
1442 sleAccount->getAccountID(sfAccount) ==
1443 (bSetHigh ? uHighAccountID : uLowAccountID),
1444 "ripple::trustCreate : matching account ID");
1445 auto const slePeer =
1446 view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
1447 if (!slePeer)
1448 return tecNO_TARGET;
1449
1450 // Remember deletion hints.
1451 sleRippleState->setFieldU64(sfLowNode, *lowNode);
1452 sleRippleState->setFieldU64(sfHighNode, *highNode);
1453
1454 sleRippleState->setFieldAmount(
1455 bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
1456 sleRippleState->setFieldAmount(
1457 bSetHigh ? sfLowLimit : sfHighLimit,
1459 saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID}));
1460
1461 if (uQualityIn)
1462 sleRippleState->setFieldU32(
1463 bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
1464
1465 if (uQualityOut)
1466 sleRippleState->setFieldU32(
1467 bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
1468
1469 std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve;
1470
1471 if (bAuth)
1472 {
1473 uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth);
1474 }
1475 if (bNoRipple)
1476 {
1477 uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple);
1478 }
1479 if (bFreeze)
1480 {
1481 uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze);
1482 }
1483 if (bDeepFreeze)
1484 {
1485 uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze);
1486 }
1487
1488 if ((slePeer->getFlags() & lsfDefaultRipple) == 0)
1489 {
1490 // The other side's default is no rippling
1491 uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple);
1492 }
1493
1494 sleRippleState->setFieldU32(sfFlags, uFlags);
1495 adjustOwnerCount(view, sleAccount, 1, j);
1496
1497 // ONLY: Create ripple balance.
1498 sleRippleState->setFieldAmount(
1499 sfBalance, bSetHigh ? -saBalance : saBalance);
1500
1501 view.creditHook(
1502 uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
1503
1504 return tesSUCCESS;
1505}
1506
1507[[nodiscard]] TER
1509 ApplyView& view,
1510 AccountID const& accountID,
1511 Issue const& issue,
1512 beast::Journal journal)
1513{
1514 if (issue.native())
1515 {
1516 auto const sle = view.read(keylet::account(accountID));
1517 if (!sle)
1518 return tecINTERNAL;
1519 auto const balance = sle->getFieldAmount(sfBalance);
1520 if (balance.xrp() != 0)
1521 return tecHAS_OBLIGATIONS;
1522 return tesSUCCESS;
1523 }
1524
1525 // `asset` is an IOU.
1526 auto const line = view.peek(keylet::line(accountID, issue));
1527 if (!line)
1528 return tecOBJECT_NOT_FOUND;
1529 if (line->at(sfBalance)->iou() != beast::zero)
1530 return tecHAS_OBLIGATIONS;
1531
1532 // Adjust the owner count(s)
1533 if (line->isFlag(lsfLowReserve))
1534 {
1535 // Clear reserve for low account.
1536 auto sleLowAccount =
1537 view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
1538 if (!sleLowAccount)
1539 return tecINTERNAL;
1540 adjustOwnerCount(view, sleLowAccount, -1, journal);
1541 // It's not really necessary to clear the reserve flag, since the line
1542 // is about to be deleted, but this will make the metadata reflect an
1543 // accurate state at the time of deletion.
1544 line->clearFlag(lsfLowReserve);
1545 }
1546
1547 if (line->isFlag(lsfHighReserve))
1548 {
1549 // Clear reserve for high account.
1550 auto sleHighAccount =
1551 view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
1552 if (!sleHighAccount)
1553 return tecINTERNAL;
1554 adjustOwnerCount(view, sleHighAccount, -1, journal);
1555 // It's not really necessary to clear the reserve flag, since the line
1556 // is about to be deleted, but this will make the metadata reflect an
1557 // accurate state at the time of deletion.
1558 line->clearFlag(lsfHighReserve);
1559 }
1560
1561 return trustDelete(
1562 view,
1563 line,
1564 line->at(sfLowLimit)->getIssuer(),
1565 line->at(sfHighLimit)->getIssuer(),
1566 journal);
1567}
1568
1569[[nodiscard]] TER
1571 ApplyView& view,
1572 AccountID const& accountID,
1573 MPTIssue const& mptIssue,
1574 beast::Journal journal)
1575{
1576 auto const& mptID = mptIssue.getMptID();
1577 auto const mptoken = view.peek(keylet::mptoken(mptID, accountID));
1578 if (!mptoken)
1579 return tecOBJECT_NOT_FOUND;
1580 if (mptoken->at(sfMPTAmount) != 0)
1581 return tecHAS_OBLIGATIONS;
1582
1583 return authorizeMPToken(
1584 view,
1585 {}, // priorBalance
1586 mptID,
1587 accountID,
1588 journal,
1589 tfMPTUnauthorize // flags
1590 );
1591}
1592
1593TER
1595 ApplyView& view,
1596 std::shared_ptr<SLE> const& sleRippleState,
1597 AccountID const& uLowAccountID,
1598 AccountID const& uHighAccountID,
1600{
1601 // Detect legacy dirs.
1602 std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode);
1603 std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode);
1604
1605 JLOG(j.trace()) << "trustDelete: Deleting ripple line: low";
1606
1607 if (!view.dirRemove(
1608 keylet::ownerDir(uLowAccountID),
1609 uLowNode,
1610 sleRippleState->key(),
1611 false))
1612 {
1613 return tefBAD_LEDGER;
1614 }
1615
1616 JLOG(j.trace()) << "trustDelete: Deleting ripple line: high";
1617
1618 if (!view.dirRemove(
1619 keylet::ownerDir(uHighAccountID),
1620 uHighNode,
1621 sleRippleState->key(),
1622 false))
1623 {
1624 return tefBAD_LEDGER;
1625 }
1626
1627 JLOG(j.trace()) << "trustDelete: Deleting ripple line: state";
1628 view.erase(sleRippleState);
1629
1630 return tesSUCCESS;
1631}
1632
1633TER
1635{
1636 if (!sle)
1637 return tesSUCCESS;
1638 auto offerIndex = sle->key();
1639 auto owner = sle->getAccountID(sfAccount);
1640
1641 // Detect legacy directories.
1642 uint256 uDirectory = sle->getFieldH256(sfBookDirectory);
1643
1644 if (!view.dirRemove(
1645 keylet::ownerDir(owner),
1646 sle->getFieldU64(sfOwnerNode),
1647 offerIndex,
1648 false))
1649 {
1650 return tefBAD_LEDGER;
1651 }
1652
1653 if (!view.dirRemove(
1654 keylet::page(uDirectory),
1655 sle->getFieldU64(sfBookNode),
1656 offerIndex,
1657 false))
1658 {
1659 return tefBAD_LEDGER;
1660 }
1661
1662 if (sle->isFieldPresent(sfAdditionalBooks))
1663 {
1664 XRPL_ASSERT(
1665 sle->isFlag(lsfHybrid) && sle->isFieldPresent(sfDomainID),
1666 "ripple::offerDelete : should be a hybrid domain offer");
1667
1668 auto const& additionalBookDirs = sle->getFieldArray(sfAdditionalBooks);
1669
1670 for (auto const& bookDir : additionalBookDirs)
1671 {
1672 auto const& dirIndex = bookDir.getFieldH256(sfBookDirectory);
1673 auto const& dirNode = bookDir.getFieldU64(sfBookNode);
1674
1675 if (!view.dirRemove(
1676 keylet::page(dirIndex), dirNode, offerIndex, false))
1677 {
1678 return tefBAD_LEDGER; // LCOV_EXCL_LINE
1679 }
1680 }
1681 }
1682
1683 adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j);
1684
1685 view.erase(sle);
1686
1687 return tesSUCCESS;
1688}
1689
1690// Direct send w/o fees:
1691// - Redeeming IOUs and/or sending sender's own IOUs.
1692// - Create trust line if needed.
1693// --> bCheckIssuer : normally require issuer to be involved.
1694static TER
1696 ApplyView& view,
1697 AccountID const& uSenderID,
1698 AccountID const& uReceiverID,
1699 STAmount const& saAmount,
1700 bool bCheckIssuer,
1702{
1703 AccountID const& issuer = saAmount.getIssuer();
1704 Currency const& currency = saAmount.getCurrency();
1705
1706 // Make sure issuer is involved.
1707 XRPL_ASSERT(
1708 !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer,
1709 "ripple::rippleCreditIOU : matching issuer or don't care");
1710 (void)issuer;
1711
1712 // Disallow sending to self.
1713 XRPL_ASSERT(
1714 uSenderID != uReceiverID,
1715 "ripple::rippleCreditIOU : sender is not receiver");
1716
1717 bool const bSenderHigh = uSenderID > uReceiverID;
1718 auto const index = keylet::line(uSenderID, uReceiverID, currency);
1719
1720 XRPL_ASSERT(
1721 !isXRP(uSenderID) && uSenderID != noAccount(),
1722 "ripple::rippleCreditIOU : sender is not XRP");
1723 XRPL_ASSERT(
1724 !isXRP(uReceiverID) && uReceiverID != noAccount(),
1725 "ripple::rippleCreditIOU : receiver is not XRP");
1726
1727 // If the line exists, modify it accordingly.
1728 if (auto const sleRippleState = view.peek(index))
1729 {
1730 STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
1731
1732 if (bSenderHigh)
1733 saBalance.negate(); // Put balance in sender terms.
1734
1735 view.creditHook(uSenderID, uReceiverID, saAmount, saBalance);
1736
1737 STAmount const saBefore = saBalance;
1738
1739 saBalance -= saAmount;
1740
1741 JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> "
1742 << to_string(uReceiverID)
1743 << " : before=" << saBefore.getFullText()
1744 << " amount=" << saAmount.getFullText()
1745 << " after=" << saBalance.getFullText();
1746
1747 std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
1748 bool bDelete = false;
1749
1750 // FIXME This NEEDS to be cleaned up and simplified. It's impossible
1751 // for anyone to understand.
1752 if (saBefore > beast::zero
1753 // Sender balance was positive.
1754 && saBalance <= beast::zero
1755 // Sender is zero or negative.
1756 && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
1757 // Sender reserve is set.
1758 &&
1759 static_cast<bool>(
1760 uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
1761 static_cast<bool>(
1762 view.read(keylet::account(uSenderID))->getFlags() &
1764 !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
1765 !sleRippleState->getFieldAmount(
1766 !bSenderHigh ? sfLowLimit : sfHighLimit)
1767 // Sender trust limit is 0.
1768 && !sleRippleState->getFieldU32(
1769 !bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
1770 // Sender quality in is 0.
1771 && !sleRippleState->getFieldU32(
1772 !bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
1773 // Sender quality out is 0.
1774 {
1775 // Clear the reserve of the sender, possibly delete the line!
1777 view, view.peek(keylet::account(uSenderID)), -1, j);
1778
1779 // Clear reserve flag.
1780 sleRippleState->setFieldU32(
1781 sfFlags,
1782 uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
1783
1784 // Balance is zero, receiver reserve is clear.
1785 bDelete = !saBalance // Balance is zero.
1786 && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve));
1787 // Receiver reserve is clear.
1788 }
1789
1790 if (bSenderHigh)
1791 saBalance.negate();
1792
1793 // Want to reflect balance to zero even if we are deleting line.
1794 sleRippleState->setFieldAmount(sfBalance, saBalance);
1795 // ONLY: Adjust ripple balance.
1796
1797 if (bDelete)
1798 {
1799 return trustDelete(
1800 view,
1801 sleRippleState,
1802 bSenderHigh ? uReceiverID : uSenderID,
1803 !bSenderHigh ? uReceiverID : uSenderID,
1804 j);
1805 }
1806
1807 view.update(sleRippleState);
1808 return tesSUCCESS;
1809 }
1810
1811 STAmount const saReceiverLimit(Issue{currency, uReceiverID});
1812 STAmount saBalance{saAmount};
1813
1814 saBalance.setIssuer(noAccount());
1815
1816 JLOG(j.debug()) << "rippleCreditIOU: "
1817 "create line: "
1818 << to_string(uSenderID) << " -> " << to_string(uReceiverID)
1819 << " : " << saAmount.getFullText();
1820
1821 auto const sleAccount = view.peek(keylet::account(uReceiverID));
1822 if (!sleAccount)
1823 return tefINTERNAL;
1824
1825 bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
1826
1827 return trustCreate(
1828 view,
1829 bSenderHigh,
1830 uSenderID,
1831 uReceiverID,
1832 index.key,
1833 sleAccount,
1834 false,
1835 noRipple,
1836 false,
1837 false,
1838 saBalance,
1839 saReceiverLimit,
1840 0,
1841 0,
1842 j);
1843}
1844
1845// Send regardless of limits.
1846// --> saAmount: Amount/currency/issuer to deliver to receiver.
1847// <-- saActual: Amount actually cost. Sender pays fees.
1848static TER
1850 ApplyView& view,
1851 AccountID const& uSenderID,
1852 AccountID const& uReceiverID,
1853 STAmount const& saAmount,
1854 STAmount& saActual,
1856 WaiveTransferFee waiveFee)
1857{
1858 auto const issuer = saAmount.getIssuer();
1859
1860 XRPL_ASSERT(
1861 !isXRP(uSenderID) && !isXRP(uReceiverID),
1862 "ripple::rippleSendIOU : neither sender nor receiver is XRP");
1863 XRPL_ASSERT(
1864 uSenderID != uReceiverID,
1865 "ripple::rippleSendIOU : sender is not receiver");
1866
1867 if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount())
1868 {
1869 // Direct send: redeeming IOUs and/or sending own IOUs.
1870 auto const ter =
1871 rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j);
1872 if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS)
1873 return ter;
1874 saActual = saAmount;
1875 return tesSUCCESS;
1876 }
1877
1878 // Sending 3rd party IOUs: transit.
1879
1880 // Calculate the amount to transfer accounting
1881 // for any transfer fees if the fee is not waived:
1882 saActual = (waiveFee == WaiveTransferFee::Yes)
1883 ? saAmount
1884 : multiply(saAmount, transferRate(view, issuer));
1885
1886 JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > "
1887 << to_string(uReceiverID)
1888 << " : deliver=" << saAmount.getFullText()
1889 << " cost=" << saActual.getFullText();
1890
1891 TER terResult =
1892 rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j);
1893
1894 if (tesSUCCESS == terResult)
1895 terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j);
1896
1897 return terResult;
1898}
1899
1900static TER
1902 ApplyView& view,
1903 AccountID const& uSenderID,
1904 AccountID const& uReceiverID,
1905 STAmount const& saAmount,
1907 WaiveTransferFee waiveFee)
1908{
1909 if (view.rules().enabled(fixAMMv1_1))
1910 {
1911 if (saAmount < beast::zero || saAmount.holds<MPTIssue>())
1912 {
1913 return tecINTERNAL;
1914 }
1915 }
1916 else
1917 {
1918 XRPL_ASSERT(
1919 saAmount >= beast::zero && !saAmount.holds<MPTIssue>(),
1920 "ripple::accountSendIOU : minimum amount and not MPT");
1921 }
1922
1923 /* If we aren't sending anything or if the sender is the same as the
1924 * receiver then we don't need to do anything.
1925 */
1926 if (!saAmount || (uSenderID == uReceiverID))
1927 return tesSUCCESS;
1928
1929 if (!saAmount.native())
1930 {
1931 STAmount saActual;
1932
1933 JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> "
1934 << to_string(uReceiverID) << " : "
1935 << saAmount.getFullText();
1936
1937 return rippleSendIOU(
1938 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
1939 }
1940
1941 /* XRP send which does not check reserve and can do pure adjustment.
1942 * Note that sender or receiver may be null and this not a mistake; this
1943 * setup is used during pathfinding and it is carefully controlled to
1944 * ensure that transfers are balanced.
1945 */
1946 TER terResult(tesSUCCESS);
1947
1948 SLE::pointer sender = uSenderID != beast::zero
1949 ? view.peek(keylet::account(uSenderID))
1950 : SLE::pointer();
1951 SLE::pointer receiver = uReceiverID != beast::zero
1952 ? view.peek(keylet::account(uReceiverID))
1953 : SLE::pointer();
1954
1955 if (auto stream = j.trace())
1956 {
1957 std::string sender_bal("-");
1958 std::string receiver_bal("-");
1959
1960 if (sender)
1961 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
1962
1963 if (receiver)
1964 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
1965
1966 stream << "accountSendIOU> " << to_string(uSenderID) << " ("
1967 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
1968 << receiver_bal << ") : " << saAmount.getFullText();
1969 }
1970
1971 if (sender)
1972 {
1973 if (sender->getFieldAmount(sfBalance) < saAmount)
1974 {
1975 // VFALCO Its laborious to have to mutate the
1976 // TER based on params everywhere
1977 terResult = view.open() ? TER{telFAILED_PROCESSING}
1979 }
1980 else
1981 {
1982 auto const sndBal = sender->getFieldAmount(sfBalance);
1983 view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal);
1984
1985 // Decrement XRP balance.
1986 sender->setFieldAmount(sfBalance, sndBal - saAmount);
1987 view.update(sender);
1988 }
1989 }
1990
1991 if (tesSUCCESS == terResult && receiver)
1992 {
1993 // Increment XRP balance.
1994 auto const rcvBal = receiver->getFieldAmount(sfBalance);
1995 receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
1996 view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal);
1997
1998 view.update(receiver);
1999 }
2000
2001 if (auto stream = j.trace())
2002 {
2003 std::string sender_bal("-");
2004 std::string receiver_bal("-");
2005
2006 if (sender)
2007 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
2008
2009 if (receiver)
2010 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
2011
2012 stream << "accountSendIOU< " << to_string(uSenderID) << " ("
2013 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
2014 << receiver_bal << ") : " << saAmount.getFullText();
2015 }
2016
2017 return terResult;
2018}
2019
2020static TER
2022 ApplyView& view,
2023 AccountID const& uSenderID,
2024 AccountID const& uReceiverID,
2025 STAmount const& saAmount,
2027{
2028 // Do not check MPT authorization here - it must have been checked earlier
2029 auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
2030 auto const issuer = saAmount.getIssuer();
2031 auto sleIssuance = view.peek(mptID);
2032 if (!sleIssuance)
2033 return tecOBJECT_NOT_FOUND;
2034 if (uSenderID == issuer)
2035 {
2036 (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
2037 view.update(sleIssuance);
2038 }
2039 else
2040 {
2041 auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
2042 if (auto sle = view.peek(mptokenID))
2043 {
2044 auto const amt = sle->getFieldU64(sfMPTAmount);
2045 auto const pay = saAmount.mpt().value();
2046 if (amt < pay)
2047 return tecINSUFFICIENT_FUNDS;
2048 (*sle)[sfMPTAmount] = amt - pay;
2049 view.update(sle);
2050 }
2051 else
2052 return tecNO_AUTH;
2053 }
2054
2055 if (uReceiverID == issuer)
2056 {
2057 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
2058 auto const redeem = saAmount.mpt().value();
2059 if (outstanding >= redeem)
2060 {
2061 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
2062 view.update(sleIssuance);
2063 }
2064 else
2065 return tecINTERNAL;
2066 }
2067 else
2068 {
2069 auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
2070 if (auto sle = view.peek(mptokenID))
2071 {
2072 (*sle)[sfMPTAmount] += saAmount.mpt().value();
2073 view.update(sle);
2074 }
2075 else
2076 return tecNO_AUTH;
2077 }
2078
2079 return tesSUCCESS;
2080}
2081
2082static TER
2084 ApplyView& view,
2085 AccountID const& uSenderID,
2086 AccountID const& uReceiverID,
2087 STAmount const& saAmount,
2088 STAmount& saActual,
2090 WaiveTransferFee waiveFee)
2091{
2092 XRPL_ASSERT(
2093 uSenderID != uReceiverID,
2094 "ripple::rippleSendMPT : sender is not receiver");
2095
2096 // Safe to get MPT since rippleSendMPT is only called by accountSendMPT
2097 auto const issuer = saAmount.getIssuer();
2098
2099 auto const sle =
2100 view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
2101 if (!sle)
2102 return tecOBJECT_NOT_FOUND;
2103
2104 if (uSenderID == issuer || uReceiverID == issuer)
2105 {
2106 // if sender is issuer, check that the new OutstandingAmount will not
2107 // exceed MaximumAmount
2108 if (uSenderID == issuer)
2109 {
2110 auto const sendAmount = saAmount.mpt().value();
2111 auto const maximumAmount =
2112 sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
2113 if (sendAmount > maximumAmount ||
2114 sle->getFieldU64(sfOutstandingAmount) >
2115 maximumAmount - sendAmount)
2116 return tecPATH_DRY;
2117 }
2118
2119 // Direct send: redeeming MPTs and/or sending own MPTs.
2120 auto const ter =
2121 rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
2122 if (ter != tesSUCCESS)
2123 return ter;
2124 saActual = saAmount;
2125 return tesSUCCESS;
2126 }
2127
2128 // Sending 3rd party MPTs: transit.
2129 saActual = (waiveFee == WaiveTransferFee::Yes)
2130 ? saAmount
2131 : multiply(
2132 saAmount,
2133 transferRate(view, saAmount.get<MPTIssue>().getMptID()));
2134
2135 JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > "
2136 << to_string(uReceiverID)
2137 << " : deliver=" << saAmount.getFullText()
2138 << " cost=" << saActual.getFullText();
2139
2140 if (auto const terResult =
2141 rippleCreditMPT(view, issuer, uReceiverID, saAmount, j);
2142 terResult != tesSUCCESS)
2143 return terResult;
2144
2145 return rippleCreditMPT(view, uSenderID, issuer, saActual, j);
2146}
2147
2148static TER
2150 ApplyView& view,
2151 AccountID const& uSenderID,
2152 AccountID const& uReceiverID,
2153 STAmount const& saAmount,
2155 WaiveTransferFee waiveFee)
2156{
2157 XRPL_ASSERT(
2158 saAmount >= beast::zero && saAmount.holds<MPTIssue>(),
2159 "ripple::accountSendMPT : minimum amount and MPT");
2160
2161 /* If we aren't sending anything or if the sender is the same as the
2162 * receiver then we don't need to do anything.
2163 */
2164 if (!saAmount || (uSenderID == uReceiverID))
2165 return tesSUCCESS;
2166
2167 STAmount saActual{saAmount.asset()};
2168
2169 return rippleSendMPT(
2170 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
2171}
2172
2173TER
2175 ApplyView& view,
2176 AccountID const& uSenderID,
2177 AccountID const& uReceiverID,
2178 STAmount const& saAmount,
2180 WaiveTransferFee waiveFee)
2181{
2182 return std::visit(
2183 [&]<ValidIssueType TIss>(TIss const& issue) {
2184 if constexpr (std::is_same_v<TIss, Issue>)
2185 return accountSendIOU(
2186 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
2187 else
2188 return accountSendMPT(
2189 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
2190 },
2191 saAmount.asset().value());
2192}
2193
2194static bool
2196 ApplyView& view,
2197 SLE::pointer state,
2198 bool bSenderHigh,
2199 AccountID const& sender,
2200 STAmount const& before,
2201 STAmount const& after,
2203{
2204 if (!state)
2205 return false;
2206 std::uint32_t const flags(state->getFieldU32(sfFlags));
2207
2208 auto sle = view.peek(keylet::account(sender));
2209 if (!sle)
2210 return false;
2211
2212 // YYY Could skip this if rippling in reverse.
2213 if (before > beast::zero
2214 // Sender balance was positive.
2215 && after <= beast::zero
2216 // Sender is zero or negative.
2217 && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
2218 // Sender reserve is set.
2219 && static_cast<bool>(
2220 flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
2221 static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
2222 !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
2223 !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
2224 // Sender trust limit is 0.
2225 && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
2226 // Sender quality in is 0.
2227 &&
2228 !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
2229 // Sender quality out is 0.
2230 {
2231 // VFALCO Where is the line being deleted?
2232 // Clear the reserve of the sender, possibly delete the line!
2233 adjustOwnerCount(view, sle, -1, j);
2234
2235 // Clear reserve flag.
2236 state->setFieldU32(
2237 sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
2238
2239 // Balance is zero, receiver reserve is clear.
2240 if (!after // Balance is zero.
2241 && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)))
2242 return true;
2243 }
2244 return false;
2245}
2246
2247TER
2249 ApplyView& view,
2250 AccountID const& account,
2251 STAmount const& amount,
2252 Issue const& issue,
2254{
2255 XRPL_ASSERT(
2256 !isXRP(account) && !isXRP(issue.account),
2257 "ripple::issueIOU : neither account nor issuer is XRP");
2258
2259 // Consistency check
2260 XRPL_ASSERT(issue == amount.issue(), "ripple::issueIOU : matching issue");
2261
2262 // Can't send to self!
2263 XRPL_ASSERT(
2264 issue.account != account, "ripple::issueIOU : not issuer account");
2265
2266 JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": "
2267 << amount.getFullText();
2268
2269 bool bSenderHigh = issue.account > account;
2270
2271 auto const index = keylet::line(issue.account, account, issue.currency);
2272
2273 if (auto state = view.peek(index))
2274 {
2275 STAmount final_balance = state->getFieldAmount(sfBalance);
2276
2277 if (bSenderHigh)
2278 final_balance.negate(); // Put balance in sender terms.
2279
2280 STAmount const start_balance = final_balance;
2281
2282 final_balance -= amount;
2283
2284 auto const must_delete = updateTrustLine(
2285 view,
2286 state,
2287 bSenderHigh,
2288 issue.account,
2289 start_balance,
2290 final_balance,
2291 j);
2292
2293 view.creditHook(issue.account, account, amount, start_balance);
2294
2295 if (bSenderHigh)
2296 final_balance.negate();
2297
2298 // Adjust the balance on the trust line if necessary. We do this even if
2299 // we are going to delete the line to reflect the correct balance at the
2300 // time of deletion.
2301 state->setFieldAmount(sfBalance, final_balance);
2302 if (must_delete)
2303 return trustDelete(
2304 view,
2305 state,
2306 bSenderHigh ? account : issue.account,
2307 bSenderHigh ? issue.account : account,
2308 j);
2309
2310 view.update(state);
2311
2312 return tesSUCCESS;
2313 }
2314
2315 // NIKB TODO: The limit uses the receiver's account as the issuer and
2316 // this is unnecessarily inefficient as copying which could be avoided
2317 // is now required. Consider available options.
2318 STAmount const limit(Issue{issue.currency, account});
2319 STAmount final_balance = amount;
2320
2321 final_balance.setIssuer(noAccount());
2322
2323 auto const receiverAccount = view.peek(keylet::account(account));
2324 if (!receiverAccount)
2325 return tefINTERNAL;
2326
2327 bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0;
2328
2329 return trustCreate(
2330 view,
2331 bSenderHigh,
2332 issue.account,
2333 account,
2334 index.key,
2335 receiverAccount,
2336 false,
2337 noRipple,
2338 false,
2339 false,
2340 final_balance,
2341 limit,
2342 0,
2343 0,
2344 j);
2345}
2346
2347TER
2349 ApplyView& view,
2350 AccountID const& account,
2351 STAmount const& amount,
2352 Issue const& issue,
2354{
2355 XRPL_ASSERT(
2356 !isXRP(account) && !isXRP(issue.account),
2357 "ripple::redeemIOU : neither account nor issuer is XRP");
2358
2359 // Consistency check
2360 XRPL_ASSERT(issue == amount.issue(), "ripple::redeemIOU : matching issue");
2361
2362 // Can't send to self!
2363 XRPL_ASSERT(
2364 issue.account != account, "ripple::redeemIOU : not issuer account");
2365
2366 JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": "
2367 << amount.getFullText();
2368
2369 bool bSenderHigh = account > issue.account;
2370
2371 if (auto state =
2372 view.peek(keylet::line(account, issue.account, issue.currency)))
2373 {
2374 STAmount final_balance = state->getFieldAmount(sfBalance);
2375
2376 if (bSenderHigh)
2377 final_balance.negate(); // Put balance in sender terms.
2378
2379 STAmount const start_balance = final_balance;
2380
2381 final_balance -= amount;
2382
2383 auto const must_delete = updateTrustLine(
2384 view, state, bSenderHigh, account, start_balance, final_balance, j);
2385
2386 view.creditHook(account, issue.account, amount, start_balance);
2387
2388 if (bSenderHigh)
2389 final_balance.negate();
2390
2391 // Adjust the balance on the trust line if necessary. We do this even if
2392 // we are going to delete the line to reflect the correct balance at the
2393 // time of deletion.
2394 state->setFieldAmount(sfBalance, final_balance);
2395
2396 if (must_delete)
2397 {
2398 return trustDelete(
2399 view,
2400 state,
2401 bSenderHigh ? issue.account : account,
2402 bSenderHigh ? account : issue.account,
2403 j);
2404 }
2405
2406 view.update(state);
2407 return tesSUCCESS;
2408 }
2409
2410 // In order to hold an IOU, a trust line *MUST* exist to track the
2411 // balance. If it doesn't, then something is very wrong. Don't try
2412 // to continue.
2413 JLOG(j.fatal()) << "redeemIOU: " << to_string(account)
2414 << " attempts to redeem " << amount.getFullText()
2415 << " but no trust line exists!";
2416
2417 return tefINTERNAL;
2418}
2419
2420TER
2422 ApplyView& view,
2423 AccountID const& from,
2424 AccountID const& to,
2425 STAmount const& amount,
2427{
2428 XRPL_ASSERT(
2429 from != beast::zero, "ripple::transferXRP : nonzero from account");
2430 XRPL_ASSERT(to != beast::zero, "ripple::transferXRP : nonzero to account");
2431 XRPL_ASSERT(from != to, "ripple::transferXRP : sender is not receiver");
2432 XRPL_ASSERT(amount.native(), "ripple::transferXRP : amount is XRP");
2433
2434 SLE::pointer const sender = view.peek(keylet::account(from));
2435 SLE::pointer const receiver = view.peek(keylet::account(to));
2436 if (!sender || !receiver)
2437 return tefINTERNAL;
2438
2439 JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> "
2440 << to_string(to) << ") : " << amount.getFullText();
2441
2442 if (sender->getFieldAmount(sfBalance) < amount)
2443 {
2444 // VFALCO Its unfortunate we have to keep
2445 // mutating these TER everywhere
2446 // FIXME: this logic should be moved to callers maybe?
2447 return view.open() ? TER{telFAILED_PROCESSING}
2449 }
2450
2451 // Decrement XRP balance.
2452 sender->setFieldAmount(
2453 sfBalance, sender->getFieldAmount(sfBalance) - amount);
2454 view.update(sender);
2455
2456 receiver->setFieldAmount(
2457 sfBalance, receiver->getFieldAmount(sfBalance) + amount);
2458 view.update(receiver);
2459
2460 return tesSUCCESS;
2461}
2462
2463TER
2465 ReadView const& view,
2466 Issue const& issue,
2467 AccountID const& account,
2468 AuthType authType)
2469{
2470 if (isXRP(issue) || issue.account == account)
2471 return tesSUCCESS;
2472
2473 auto const trustLine =
2474 view.read(keylet::line(account, issue.account, issue.currency));
2475 // If account has no line, and this is a strong check, fail
2476 if (!trustLine && authType == AuthType::StrongAuth)
2477 return tecNO_LINE;
2478
2479 // If this is a weak or legacy check, or if the account has a line, fail if
2480 // auth is required and not set on the line
2481 if (auto const issuerAccount = view.read(keylet::account(issue.account));
2482 issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth)
2483 {
2484 if (trustLine)
2485 return ((*trustLine)[sfFlags] &
2486 ((account > issue.account) ? lsfLowAuth : lsfHighAuth))
2487 ? tesSUCCESS
2488 : TER{tecNO_AUTH};
2489 return TER{tecNO_LINE};
2490 }
2491
2492 return tesSUCCESS;
2493}
2494
2495TER
2497 ReadView const& view,
2498 MPTIssue const& mptIssue,
2499 AccountID const& account,
2500 AuthType authType,
2501 int depth)
2502{
2503 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
2504 auto const sleIssuance = view.read(mptID);
2505 if (!sleIssuance)
2506 return tecOBJECT_NOT_FOUND;
2507
2508 auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
2509
2510 // issuer is always "authorized"
2511 if (mptIssuer == account) // Issuer won't have MPToken
2512 return tesSUCCESS;
2513
2514 if (view.rules().enabled(featureSingleAssetVault))
2515 {
2516 if (depth >= maxAssetCheckDepth)
2517 return tecINTERNAL; // LCOV_EXCL_LINE
2518
2519 // requireAuth is recursive if the issuer is a vault pseudo-account
2520 auto const sleIssuer = view.read(keylet::account(mptIssuer));
2521 if (!sleIssuer)
2522 return tefINTERNAL; // LCOV_EXCL_LINE
2523
2524 if (sleIssuer->isFieldPresent(sfVaultID))
2525 {
2526 auto const sleVault =
2527 view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
2528 if (!sleVault)
2529 return tefINTERNAL; // LCOV_EXCL_LINE
2530
2531 auto const asset = sleVault->at(sfAsset);
2532 if (auto const err = std::visit(
2533 [&]<ValidIssueType TIss>(TIss const& issue) {
2534 if constexpr (std::is_same_v<TIss, Issue>)
2535 return requireAuth(view, issue, account, authType);
2536 else
2537 return requireAuth(
2538 view, issue, account, authType, depth + 1);
2539 },
2540 asset.value());
2541 !isTesSuccess(err))
2542 return err;
2543 }
2544 }
2545
2546 auto const mptokenID = keylet::mptoken(mptID.key, account);
2547 auto const sleToken = view.read(mptokenID);
2548
2549 // if account has no MPToken, fail
2550 if (!sleToken &&
2551 (authType == AuthType::StrongAuth || authType == AuthType::Legacy))
2552 return tecNO_AUTH;
2553
2554 // Note, this check is not amendment-gated because DomainID will be always
2555 // empty **unless** writing to it has been enabled by an amendment
2556 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
2557 if (maybeDomainID)
2558 {
2559 XRPL_ASSERT(
2560 sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth,
2561 "ripple::requireAuth : issuance requires authorization");
2562 // ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED
2563 if (auto const ter =
2564 credentials::validDomain(view, *maybeDomainID, account);
2565 isTesSuccess(ter))
2566 return ter; // Note: sleToken might be null
2567 else if (!sleToken)
2568 return ter;
2569 // We ignore error from validDomain if we found sleToken, as it could
2570 // belong to someone who is explicitly authorized e.g. a vault owner.
2571 }
2572
2573 // mptoken must be authorized if issuance enabled requireAuth
2574 if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
2575 (!sleToken || !sleToken->isFlag(lsfMPTAuthorized)))
2576 return tecNO_AUTH;
2577
2578 return tesSUCCESS; // Note: sleToken might be null
2579}
2580
2581[[nodiscard]] TER
2583 ApplyView& view,
2584 MPTID const& mptIssuanceID,
2585 AccountID const& account,
2586 XRPAmount const& priorBalance, // for MPToken authorization
2588{
2589 auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
2590 if (!sleIssuance)
2591 return tefINTERNAL; // LCOV_EXCL_LINE
2592
2593 XRPL_ASSERT(
2594 sleIssuance->isFlag(lsfMPTRequireAuth),
2595 "ripple::enforceMPTokenAuthorization : authorization required");
2596
2597 if (account == sleIssuance->at(sfIssuer))
2598 return tefINTERNAL; // LCOV_EXCL_LINE
2599
2600 auto const keylet = keylet::mptoken(mptIssuanceID, account);
2601 auto const sleToken = view.read(keylet); // NOTE: might be null
2602 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
2603 bool expired = false;
2604 bool const authorizedByDomain = [&]() -> bool {
2605 // NOTE: defensive here, shuld be checked in preclaim
2606 if (!maybeDomainID.has_value())
2607 return false; // LCOV_EXCL_LINE
2608
2609 auto const ter = verifyValidDomain(view, account, *maybeDomainID, j);
2610 if (isTesSuccess(ter))
2611 return true;
2612 if (ter == tecEXPIRED)
2613 expired = true;
2614 return false;
2615 }();
2616
2617 if (!authorizedByDomain && sleToken == nullptr)
2618 {
2619 // Could not find MPToken and won't create one, could be either of:
2620 //
2621 // 1. Field sfDomainID not set in MPTokenIssuance or
2622 // 2. Account has no matching and accepted credentials or
2623 // 3. Account has all expired credentials (deleted in verifyValidDomain)
2624 //
2625 // Either way, return tecNO_AUTH and there is nothing else to do
2626 return expired ? tecEXPIRED : tecNO_AUTH;
2627 }
2628 else if (!authorizedByDomain && maybeDomainID.has_value())
2629 {
2630 // Found an MPToken but the account is not authorized and we expect
2631 // it to have been authorized by the domain. This could be because the
2632 // credentials used to create the MPToken have expired or been deleted.
2633 return expired ? tecEXPIRED : tecNO_AUTH;
2634 }
2635 else if (!authorizedByDomain)
2636 {
2637 // We found an MPToken, but sfDomainID is not set, so this is a classic
2638 // MPToken which requires authorization by the token issuer.
2639 XRPL_ASSERT(
2640 sleToken != nullptr && !maybeDomainID.has_value(),
2641 "ripple::enforceMPTokenAuthorization : found MPToken");
2642 if (sleToken->isFlag(lsfMPTAuthorized))
2643 return tesSUCCESS;
2644
2645 return tecNO_AUTH;
2646 }
2647 else if (authorizedByDomain && sleToken != nullptr)
2648 {
2649 // Found an MPToken, authorized by the domain. Ignore authorization flag
2650 // lsfMPTAuthorized because it is meaningless. Return tesSUCCESS
2651 XRPL_ASSERT(
2652 maybeDomainID.has_value(),
2653 "ripple::enforceMPTokenAuthorization : found MPToken for domain");
2654 return tesSUCCESS;
2655 }
2656 else if (authorizedByDomain)
2657 {
2658 // Could not find MPToken but there should be one because we are
2659 // authorized by domain. Proceed to create it, then return tesSUCCESS
2660 XRPL_ASSERT(
2661 maybeDomainID.has_value() && sleToken == nullptr,
2662 "ripple::enforceMPTokenAuthorization : new MPToken for domain");
2663 if (auto const err = authorizeMPToken(
2664 view,
2665 priorBalance, // priorBalance
2666 mptIssuanceID, // mptIssuanceID
2667 account, // account
2668 j);
2669 !isTesSuccess(err))
2670 return err;
2671
2672 return tesSUCCESS;
2673 }
2674
2675 // LCOV_EXCL_START
2676 UNREACHABLE(
2677 "ripple::enforceMPTokenAuthorization : condition list is incomplete");
2678 return tefINTERNAL;
2679} // LCOV_EXCL_STOP
2680
2681TER
2683 ReadView const& view,
2684 MPTIssue const& mptIssue,
2685 AccountID const& from,
2686 AccountID const& to)
2687{
2688 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
2689 auto const sleIssuance = view.read(mptID);
2690 if (!sleIssuance)
2691 return tecOBJECT_NOT_FOUND;
2692
2693 if (!(sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer))
2694 {
2695 if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
2696 return TER{tecNO_AUTH};
2697 }
2698 return tesSUCCESS;
2699}
2700
2701TER
2703 ApplyView& view,
2704 Keylet const& ownerDirKeylet,
2705 EntryDeleter const& deleter,
2707 std::optional<uint16_t> maxNodesToDelete)
2708{
2709 // Delete all the entries in the account directory.
2710 std::shared_ptr<SLE> sleDirNode{};
2711 unsigned int uDirEntry{0};
2712 uint256 dirEntry{beast::zero};
2713 std::uint32_t deleted = 0;
2714
2715 if (view.exists(ownerDirKeylet) &&
2716 dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
2717 {
2718 do
2719 {
2720 if (maxNodesToDelete && ++deleted > *maxNodesToDelete)
2721 return tecINCOMPLETE;
2722
2723 // Choose the right way to delete each directory node.
2724 auto sleItem = view.peek(keylet::child(dirEntry));
2725 if (!sleItem)
2726 {
2727 // Directory node has an invalid index. Bail out.
2728 JLOG(j.fatal())
2729 << "DeleteAccount: Directory node in ledger " << view.seq()
2730 << " has index to object that is missing: "
2731 << to_string(dirEntry);
2732 return tefBAD_LEDGER;
2733 }
2734
2735 LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
2736 sleItem->getFieldU16(sfLedgerEntryType))};
2737
2738 // Deleter handles the details of specific account-owned object
2739 // deletion
2740 auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem);
2741 if (ter != tesSUCCESS)
2742 return ter;
2743
2744 // dirFirst() and dirNext() are like iterators with exposed
2745 // internal state. We'll take advantage of that exposed state
2746 // to solve a common C++ problem: iterator invalidation while
2747 // deleting elements from a container.
2748 //
2749 // We have just deleted one directory entry, which means our
2750 // "iterator state" is invalid.
2751 //
2752 // 1. During the process of getting an entry from the
2753 // directory uDirEntry was incremented from 'it' to 'it'+1.
2754 //
2755 // 2. We then deleted the entry at index 'it', which means the
2756 // entry that was at 'it'+1 has now moved to 'it'.
2757 //
2758 // 3. So we verify that uDirEntry is indeed 'it'+1. Then we jam it
2759 // back to 'it' to "un-invalidate" the iterator.
2760 XRPL_ASSERT(
2761 uDirEntry >= 1,
2762 "ripple::cleanupOnAccountDelete : minimum dir entries");
2763 if (uDirEntry == 0)
2764 {
2765 JLOG(j.error())
2766 << "DeleteAccount iterator re-validation failed.";
2767 return tefBAD_LEDGER;
2768 }
2769 if (skipEntry == SkipEntry::No)
2770 uDirEntry--;
2771
2772 } while (
2773 dirNext(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
2774 }
2775
2776 return tesSUCCESS;
2777}
2778
2779TER
2781 ApplyView& view,
2782 std::shared_ptr<SLE> sleState,
2783 std::optional<AccountID> const& ammAccountID,
2785{
2786 if (!sleState || sleState->getType() != ltRIPPLE_STATE)
2787 return tecINTERNAL;
2788
2789 auto const& [low, high] = std::minmax(
2790 sleState->getFieldAmount(sfLowLimit).getIssuer(),
2791 sleState->getFieldAmount(sfHighLimit).getIssuer());
2792 auto sleLow = view.peek(keylet::account(low));
2793 auto sleHigh = view.peek(keylet::account(high));
2794 if (!sleLow || !sleHigh)
2795 return tecINTERNAL;
2796 bool const ammLow = sleLow->isFieldPresent(sfAMMID);
2797 bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
2798
2799 // can't both be AMM
2800 if (ammLow && ammHigh)
2801 return tecINTERNAL;
2802
2803 // at least one must be
2804 if (!ammLow && !ammHigh)
2805 return terNO_AMM;
2806
2807 // one must be the target amm
2808 if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
2809 return terNO_AMM;
2810
2811 if (auto const ter = trustDelete(view, sleState, low, high, j);
2812 ter != tesSUCCESS)
2813 {
2814 JLOG(j.error())
2815 << "deleteAMMTrustLine: failed to delete the trustline.";
2816 return ter;
2817 }
2818
2819 auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
2820 if (!(sleState->getFlags() & uFlags))
2821 return tecINTERNAL;
2822
2823 adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
2824
2825 return tesSUCCESS;
2826}
2827
2828TER
2830 ApplyView& view,
2831 AccountID const& uSenderID,
2832 AccountID const& uReceiverID,
2833 STAmount const& saAmount,
2834 bool bCheckIssuer,
2836{
2837 return std::visit(
2838 [&]<ValidIssueType TIss>(TIss const& issue) {
2839 if constexpr (std::is_same_v<TIss, Issue>)
2840 {
2841 return rippleCreditIOU(
2842 view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
2843 }
2844 else
2845 {
2846 XRPL_ASSERT(
2847 !bCheckIssuer,
2848 "ripple::rippleCredit : not checking issuer");
2849 return rippleCreditMPT(
2850 view, uSenderID, uReceiverID, saAmount, j);
2851 }
2852 },
2853 saAmount.asset().value());
2854}
2855
2856[[nodiscard]] std::optional<STAmount>
2858 std::shared_ptr<SLE const> const& vault,
2859 std::shared_ptr<SLE const> const& issuance,
2860 STAmount const& assets)
2861{
2862 XRPL_ASSERT(
2863 !assets.negative(),
2864 "ripple::assetsToSharesDeposit : non-negative assets");
2865 XRPL_ASSERT(
2866 assets.asset() == vault->at(sfAsset),
2867 "ripple::assetsToSharesDeposit : assets and vault match");
2868 if (assets.negative() || assets.asset() != vault->at(sfAsset))
2869 return std::nullopt; // LCOV_EXCL_LINE
2870
2871 Number const assetTotal = vault->at(sfAssetsTotal);
2872 STAmount shares{vault->at(sfShareMPTID)};
2873 if (assetTotal == 0)
2874 return STAmount{
2875 shares.asset(),
2876 Number(assets.mantissa(), assets.exponent() + vault->at(sfScale))
2877 .truncate()};
2878
2879 Number const shareTotal = issuance->at(sfOutstandingAmount);
2880 shares = (shareTotal * (assets / assetTotal)).truncate();
2881 return shares;
2882}
2883
2884[[nodiscard]] std::optional<STAmount>
2886 std::shared_ptr<SLE const> const& vault,
2887 std::shared_ptr<SLE const> const& issuance,
2888 STAmount const& shares)
2889{
2890 XRPL_ASSERT(
2891 !shares.negative(),
2892 "ripple::sharesToAssetsDeposit : non-negative shares");
2893 XRPL_ASSERT(
2894 shares.asset() == vault->at(sfShareMPTID),
2895 "ripple::sharesToAssetsDeposit : shares and vault match");
2896 if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
2897 return std::nullopt; // LCOV_EXCL_LINE
2898
2899 Number const assetTotal = vault->at(sfAssetsTotal);
2900 STAmount assets{vault->at(sfAsset)};
2901 if (assetTotal == 0)
2902 return STAmount{
2903 assets.asset(),
2904 shares.mantissa(),
2905 shares.exponent() - vault->at(sfScale),
2906 false};
2907
2908 Number const shareTotal = issuance->at(sfOutstandingAmount);
2909 assets = assetTotal * (shares / shareTotal);
2910 return assets;
2911}
2912
2913[[nodiscard]] std::optional<STAmount>
2915 std::shared_ptr<SLE const> const& vault,
2916 std::shared_ptr<SLE const> const& issuance,
2917 STAmount const& assets,
2918 TruncateShares truncate)
2919{
2920 XRPL_ASSERT(
2921 !assets.negative(),
2922 "ripple::assetsToSharesDeposit : non-negative assets");
2923 XRPL_ASSERT(
2924 assets.asset() == vault->at(sfAsset),
2925 "ripple::assetsToSharesWithdraw : assets and vault match");
2926 if (assets.negative() || assets.asset() != vault->at(sfAsset))
2927 return std::nullopt; // LCOV_EXCL_LINE
2928
2929 Number assetTotal = vault->at(sfAssetsTotal);
2930 assetTotal -= vault->at(sfLossUnrealized);
2931 STAmount shares{vault->at(sfShareMPTID)};
2932 if (assetTotal == 0)
2933 return shares;
2934 Number const shareTotal = issuance->at(sfOutstandingAmount);
2935 Number result = shareTotal * (assets / assetTotal);
2936 if (truncate == TruncateShares::yes)
2937 result = result.truncate();
2938 shares = result;
2939 return shares;
2940}
2941
2942[[nodiscard]] std::optional<STAmount>
2944 std::shared_ptr<SLE const> const& vault,
2945 std::shared_ptr<SLE const> const& issuance,
2946 STAmount const& shares)
2947{
2948 XRPL_ASSERT(
2949 !shares.negative(),
2950 "ripple::sharesToAssetsDeposit : non-negative shares");
2951 XRPL_ASSERT(
2952 shares.asset() == vault->at(sfShareMPTID),
2953 "ripple::sharesToAssetsWithdraw : shares and vault match");
2954 if (shares.negative() || shares.asset() != vault->at(sfShareMPTID))
2955 return std::nullopt; // LCOV_EXCL_LINE
2956
2957 Number assetTotal = vault->at(sfAssetsTotal);
2958 assetTotal -= vault->at(sfLossUnrealized);
2959 STAmount assets{vault->at(sfAsset)};
2960 if (assetTotal == 0)
2961 return assets;
2962 Number const shareTotal = issuance->at(sfOutstandingAmount);
2963 assets = assetTotal * (shares / shareTotal);
2964 return assets;
2965}
2966
2967TER
2969 ApplyView& view,
2970 AccountID const& sender,
2971 STAmount const& amount,
2973{
2974 auto const mptIssue = amount.get<MPTIssue>();
2975 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
2976 auto sleIssuance = view.peek(mptID);
2977 if (!sleIssuance)
2978 { // LCOV_EXCL_START
2979 JLOG(j.error()) << "rippleLockEscrowMPT: MPT issuance not found for "
2980 << mptIssue.getMptID();
2981 return tecOBJECT_NOT_FOUND;
2982 } // LCOV_EXCL_STOP
2983
2984 if (amount.getIssuer() == sender)
2985 { // LCOV_EXCL_START
2986 JLOG(j.error())
2987 << "rippleLockEscrowMPT: sender is the issuer, cannot lock MPTs.";
2988 return tecINTERNAL;
2989 } // LCOV_EXCL_STOP
2990
2991 // 1. Decrease the MPT Holder MPTAmount
2992 // 2. Increase the MPT Holder EscrowedAmount
2993 {
2994 auto const mptokenID = keylet::mptoken(mptID.key, sender);
2995 auto sle = view.peek(mptokenID);
2996 if (!sle)
2997 { // LCOV_EXCL_START
2998 JLOG(j.error())
2999 << "rippleLockEscrowMPT: MPToken not found for " << sender;
3000 return tecOBJECT_NOT_FOUND;
3001 } // LCOV_EXCL_STOP
3002
3003 auto const amt = sle->getFieldU64(sfMPTAmount);
3004 auto const pay = amount.mpt().value();
3005
3006 // Underflow check for subtraction
3007 if (!canSubtract(STAmount(mptIssue, amt), STAmount(mptIssue, pay)))
3008 { // LCOV_EXCL_START
3009 JLOG(j.error())
3010 << "rippleLockEscrowMPT: insufficient MPTAmount for "
3011 << to_string(sender) << ": " << amt << " < " << pay;
3012 return tecINTERNAL;
3013 } // LCOV_EXCL_STOP
3014
3015 (*sle)[sfMPTAmount] = amt - pay;
3016
3017 // Overflow check for addition
3018 uint64_t const locked = (*sle)[~sfLockedAmount].value_or(0);
3019
3020 if (!canAdd(STAmount(mptIssue, locked), STAmount(mptIssue, pay)))
3021 { // LCOV_EXCL_START
3022 JLOG(j.error())
3023 << "rippleLockEscrowMPT: overflow on locked amount for "
3024 << to_string(sender) << ": " << locked << " + " << pay;
3025 return tecINTERNAL;
3026 } // LCOV_EXCL_STOP
3027
3028 if (sle->isFieldPresent(sfLockedAmount))
3029 (*sle)[sfLockedAmount] += pay;
3030 else
3031 sle->setFieldU64(sfLockedAmount, pay);
3032
3033 view.update(sle);
3034 }
3035
3036 // 1. Increase the Issuance EscrowedAmount
3037 // 2. DO NOT change the Issuance OutstandingAmount
3038 {
3039 uint64_t const issuanceEscrowed =
3040 (*sleIssuance)[~sfLockedAmount].value_or(0);
3041 auto const pay = amount.mpt().value();
3042
3043 // Overflow check for addition
3044 if (!canAdd(
3045 STAmount(mptIssue, issuanceEscrowed), STAmount(mptIssue, pay)))
3046 { // LCOV_EXCL_START
3047 JLOG(j.error()) << "rippleLockEscrowMPT: overflow on issuance "
3048 "locked amount for "
3049 << mptIssue.getMptID() << ": " << issuanceEscrowed
3050 << " + " << pay;
3051 return tecINTERNAL;
3052 } // LCOV_EXCL_STOP
3053
3054 if (sleIssuance->isFieldPresent(sfLockedAmount))
3055 (*sleIssuance)[sfLockedAmount] += pay;
3056 else
3057 sleIssuance->setFieldU64(sfLockedAmount, pay);
3058
3059 view.update(sleIssuance);
3060 }
3061 return tesSUCCESS;
3062}
3063
3064TER
3066 ApplyView& view,
3067 AccountID const& sender,
3068 AccountID const& receiver,
3069 STAmount const& netAmount,
3070 STAmount const& grossAmount,
3072{
3073 if (!view.rules().enabled(fixTokenEscrowV1))
3074 XRPL_ASSERT(
3075 netAmount == grossAmount,
3076 "ripple::rippleUnlockEscrowMPT : netAmount == grossAmount");
3077
3078 auto const& issuer = netAmount.getIssuer();
3079 auto const& mptIssue = netAmount.get<MPTIssue>();
3080 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
3081 auto sleIssuance = view.peek(mptID);
3082 if (!sleIssuance)
3083 { // LCOV_EXCL_START
3084 JLOG(j.error()) << "rippleUnlockEscrowMPT: MPT issuance not found for "
3085 << mptIssue.getMptID();
3086 return tecOBJECT_NOT_FOUND;
3087 } // LCOV_EXCL_STOP
3088
3089 // Decrease the Issuance EscrowedAmount
3090 {
3091 if (!sleIssuance->isFieldPresent(sfLockedAmount))
3092 { // LCOV_EXCL_START
3093 JLOG(j.error())
3094 << "rippleUnlockEscrowMPT: no locked amount in issuance for "
3095 << mptIssue.getMptID();
3096 return tecINTERNAL;
3097 } // LCOV_EXCL_STOP
3098
3099 auto const locked = sleIssuance->getFieldU64(sfLockedAmount);
3100 auto const redeem = grossAmount.mpt().value();
3101
3102 // Underflow check for subtraction
3103 if (!canSubtract(
3104 STAmount(mptIssue, locked), STAmount(mptIssue, redeem)))
3105 { // LCOV_EXCL_START
3106 JLOG(j.error())
3107 << "rippleUnlockEscrowMPT: insufficient locked amount for "
3108 << mptIssue.getMptID() << ": " << locked << " < " << redeem;
3109 return tecINTERNAL;
3110 } // LCOV_EXCL_STOP
3111
3112 auto const newLocked = locked - redeem;
3113 if (newLocked == 0)
3114 sleIssuance->makeFieldAbsent(sfLockedAmount);
3115 else
3116 sleIssuance->setFieldU64(sfLockedAmount, newLocked);
3117 view.update(sleIssuance);
3118 }
3119
3120 if (issuer != receiver)
3121 {
3122 // Increase the MPT Holder MPTAmount
3123 auto const mptokenID = keylet::mptoken(mptID.key, receiver);
3124 auto sle = view.peek(mptokenID);
3125 if (!sle)
3126 { // LCOV_EXCL_START
3127 JLOG(j.error())
3128 << "rippleUnlockEscrowMPT: MPToken not found for " << receiver;
3129 return tecOBJECT_NOT_FOUND; // LCOV_EXCL_LINE
3130 } // LCOV_EXCL_STOP
3131
3132 auto current = sle->getFieldU64(sfMPTAmount);
3133 auto delta = netAmount.mpt().value();
3134
3135 // Overflow check for addition
3136 if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta)))
3137 { // LCOV_EXCL_START
3138 JLOG(j.error())
3139 << "rippleUnlockEscrowMPT: overflow on MPTAmount for "
3140 << to_string(receiver) << ": " << current << " + " << delta;
3141 return tecINTERNAL;
3142 } // LCOV_EXCL_STOP
3143
3144 (*sle)[sfMPTAmount] += delta;
3145 view.update(sle);
3146 }
3147 else
3148 {
3149 // Decrease the Issuance OutstandingAmount
3150 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
3151 auto const redeem = netAmount.mpt().value();
3152
3153 // Underflow check for subtraction
3154 if (!canSubtract(
3155 STAmount(mptIssue, outstanding), STAmount(mptIssue, redeem)))
3156 { // LCOV_EXCL_START
3157 JLOG(j.error())
3158 << "rippleUnlockEscrowMPT: insufficient outstanding amount for "
3159 << mptIssue.getMptID() << ": " << outstanding << " < "
3160 << redeem;
3161 return tecINTERNAL;
3162 } // LCOV_EXCL_STOP
3163
3164 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
3165 view.update(sleIssuance);
3166 }
3167
3168 if (issuer == sender)
3169 { // LCOV_EXCL_START
3170 JLOG(j.error()) << "rippleUnlockEscrowMPT: sender is the issuer, "
3171 "cannot unlock MPTs.";
3172 return tecINTERNAL;
3173 } // LCOV_EXCL_STOP
3174 else
3175 {
3176 // Decrease the MPT Holder EscrowedAmount
3177 auto const mptokenID = keylet::mptoken(mptID.key, sender);
3178 auto sle = view.peek(mptokenID);
3179 if (!sle)
3180 { // LCOV_EXCL_START
3181 JLOG(j.error())
3182 << "rippleUnlockEscrowMPT: MPToken not found for " << sender;
3183 return tecOBJECT_NOT_FOUND;
3184 } // LCOV_EXCL_STOP
3185
3186 if (!sle->isFieldPresent(sfLockedAmount))
3187 { // LCOV_EXCL_START
3188 JLOG(j.error())
3189 << "rippleUnlockEscrowMPT: no locked amount in MPToken for "
3190 << to_string(sender);
3191 return tecINTERNAL;
3192 } // LCOV_EXCL_STOP
3193
3194 auto const locked = sle->getFieldU64(sfLockedAmount);
3195 auto const delta = grossAmount.mpt().value();
3196
3197 // Underflow check for subtraction
3198 if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta)))
3199 { // LCOV_EXCL_START
3200 JLOG(j.error())
3201 << "rippleUnlockEscrowMPT: insufficient locked amount for "
3202 << to_string(sender) << ": " << locked << " < " << delta;
3203 return tecINTERNAL;
3204 } // LCOV_EXCL_STOP
3205
3206 auto const newLocked = locked - delta;
3207 if (newLocked == 0)
3208 sle->makeFieldAbsent(sfLockedAmount);
3209 else
3210 sle->setFieldU64(sfLockedAmount, newLocked);
3211 view.update(sle);
3212 }
3213
3214 // Note: The gross amount is the amount that was locked, the net
3215 // amount is the amount that is being unlocked. The difference is the fee
3216 // that was charged for the transfer. If this difference is greater than
3217 // zero, we need to update the outstanding amount.
3218 auto const diff = grossAmount.mpt().value() - netAmount.mpt().value();
3219 if (diff != 0)
3220 {
3221 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
3222 // Underflow check for subtraction
3223 if (!canSubtract(
3224 STAmount(mptIssue, outstanding), STAmount(mptIssue, diff)))
3225 { // LCOV_EXCL_START
3226 JLOG(j.error())
3227 << "rippleUnlockEscrowMPT: insufficient outstanding amount for "
3228 << mptIssue.getMptID() << ": " << outstanding << " < " << diff;
3229 return tecINTERNAL;
3230 } // LCOV_EXCL_STOP
3231
3232 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff);
3233 view.update(sleIssuance);
3234 }
3235 return tesSUCCESS;
3236}
3237
3238bool
3240{
3241 return now.time_since_epoch().count() > mark;
3242}
3243
3244} // namespace ripple
Provide a light-weight way to check active() before string formatting.
Definition Journal.h:205
A generic endpoint for log messages.
Definition Journal.h:60
Stream fatal() const
Definition Journal.h:352
Stream error() const
Definition Journal.h:346
Stream debug() const
Definition Journal.h:328
static Sink & getNullSink()
Returns a Sink which does nothing.
Stream trace() const
Severity stream access functions.
Definition Journal.h:322
Stream warn() const
Definition Journal.h:340
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:143
virtual void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
Definition ApplyView.h:243
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 adjustOwnerCountHook(AccountID const &account, std::uint32_t cur, std::uint32_t next)
Definition ApplyView.h:254
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
A currency issued by an account.
Definition Issue.h:33
AccountID account
Definition Issue.h:36
Currency currency
Definition Issue.h:35
AccountID const & getIssuer() const
Definition Issue.h:45
bool native() const
Definition Issue.cpp:66
Item const * findByType(KeyType type) const
Retrieve a format based on its type.
static LedgerFormats const & getInstance()
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:133
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:46
std::chrono::time_point< NetClock > time_point
Definition chrono.h:69
std::chrono::duration< rep, period > duration
Definition chrono.h:68
Number truncate() const noexcept
Definition Number.h:154
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.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition ReadView.h:111
virtual std::uint32_t ownerCountHook(AccountID const &account, std::uint32_t count) const
Definition ReadView.h:192
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
Definition ReadView.h:178
virtual bool open() const =0
Returns true if this reflects an open ledger.
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.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:118
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
Identifies fields.
Definition SField.h:146
@ sMD_PseudoAccount
Definition SField.h:156
constexpr bool holds() const noexcept
Definition STAmount.h:465
int exponent() const noexcept
Definition STAmount.h:452
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
XRPAmount xrp() const
Definition STAmount.cpp:306
bool negative() const noexcept
Definition STAmount.h:471
AccountID const & getIssuer() const
Definition STAmount.h:508
MPTAmount mpt() const
Definition STAmount.cpp:337
Issue const & issue() const
Definition STAmount.h:496
std::uint64_t mantissa() const noexcept
Definition STAmount.h:477
std::string getFullText() const override
Definition STAmount.cpp:696
bool native() const noexcept
Definition STAmount.h:458
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
Definition STAmount.h:520
std::shared_ptr< STLedgerEntry > const & ref
std::size_t size() const
T count_if(T... args)
T emplace_back(T... args)
T is_same_v
T max(T... args)
T minmax(T... args)
bool internalDirFirst(V &view, uint256 const &root, std::shared_ptr< N > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:100
bool internalDirNext(V &view, uint256 const &root, std::shared_ptr< N > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:52
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:540
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition Indexes.cpp:190
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:446
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 const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:214
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:526
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:564
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:380
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:374
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:196
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:48
AccountID const & noAccount()
A placeholder for empty accounts.
static bool updateTrustLine(ApplyView &view, SLE::pointer state, bool bSenderHigh, AccountID const &sender, STAmount const &before, STAmount const &after, beast::Journal j)
Definition View.cpp:2195
std::uint8_t constexpr maxAssetCheckDepth
Maximum recursion depth for vault shares being put as an asset inside another vault; counted from 0.
Definition Protocol.h:133
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition View.cpp:551
FreezeHandling
Controls the treatment of frozen account balances.
Definition View.h:77
@ fhZERO_IF_FROZEN
Definition View.h:77
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, char const *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
Definition View.cpp:796
bool isXRP(AccountID const &c)
Definition AccountID.h:90
AccountID const & xrpAccount()
Compute AccountID from public key.
TER rippleLockEscrowMPT(ApplyView &view, AccountID const &uGrantorID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2968
@ telFAILED_PROCESSING
Definition TER.h:56
std::optional< STAmount > sharesToAssetsDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2885
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:213
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:608
static TER rippleSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:2083
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:123
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition View.cpp:134
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
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:347
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition View.cpp:958
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
Definition Protocol.h:116
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2348
@ lsfHighDeepFreeze
@ lsfMPTCanTransfer
@ lsfDefaultRipple
@ lsfHighNoRipple
@ lsfDisableMaster
@ lsfMPTRequireAuth
@ lsfMPTAuthorized
@ lsfGlobalFreeze
@ lsfLowDeepFreeze
AuthType
Definition View.h:786
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
std::optional< STAmount > assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition View.cpp:2857
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
AuthHandling
Controls the treatment of unauthorized MPT balances.
Definition View.h:80
@ ahZERO_IF_UNAUTHORIZED
Definition View.h:80
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition View.cpp:1047
std::optional< STAmount > assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets, TruncateShares truncate=TruncateShares::no)
Definition View.cpp:2914
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition View.cpp:2421
@ current
This was a new validation and was added.
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2174
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition View.cpp:904
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< std::uint16_t > maxNodesToDelete=std::nullopt)
Cleanup owner directory entries on account delete.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:247
@ expired
List is expired, but has the largest non-pending sequence seen so far.
std::set< uint256 > getEnabledAmendments(ReadView const &view)
Definition View.cpp:918
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
@ tefBAD_LEDGER
Definition TER.h:170
@ tefINTERNAL
Definition TER.h:173
std::optional< STAmount > sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition View.cpp:2943
void forEachItem(ReadView const &view, Keylet const &root, std::function< void(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items in the given directory.
Definition View.cpp:653
static std::uint32_t confineOwnerCount(std::uint32_t current, std::int32_t adjustment, std::optional< AccountID > const &id=std::nullopt, beast::Journal j=beast::Journal{beast::Journal::getNullSink()})
Definition View.cpp:577
static TER rippleCreditIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Definition View.cpp:1695
constexpr std::uint32_t const tfMPTUnauthorize
Definition TxFlags.h:172
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
Expected< std::shared_ptr< SLE >, TER > createPseudoAccount(ApplyView &view, uint256 const &pseudoOwnerKey, SField const &ownerField)
Create pseudo-account, storing pseudoOwnerKey into ownerField.
Definition View.cpp:1129
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:173
bool isVaultPseudoAccountFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptShare, int depth)
Definition View.cpp:307
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition View.cpp:2248
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:1901
std::map< uint256, NetClock::time_point > majorityAmendments_t
Definition View.h:400
bool forEachItemAfter(ReadView const &view, Keylet const &root, uint256 const &after, std::uint64_t const hint, unsigned int limit, std::function< bool(std::shared_ptr< SLE const > const &)> const &f)
Iterate all items after an item in the given directory.
Definition View.cpp:681
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:759
WaiveTransferFee
Definition View.h:43
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition View.cpp:1594
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecNO_TARGET
Definition TER.h:304
@ tecDIR_FULL
Definition TER.h:287
@ tecINCOMPLETE
Definition TER.h:335
@ tecFROZEN
Definition TER.h:303
@ tecDUPLICATE
Definition TER.h:315
@ tecINSUFFICIENT_FUNDS
Definition TER.h:325
@ tecINTERNAL
Definition TER.h:310
@ tecHAS_OBLIGATIONS
Definition TER.h:317
@ tecNO_LINE
Definition TER.h:301
@ tecPATH_DRY
Definition TER.h:294
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tecFAILED_PROCESSING
Definition TER.h:286
@ tecEXPIRED
Definition TER.h:314
@ tecNO_AUTH
Definition TER.h:300
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
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, Issue const &issue, beast::Journal journal)
Any transactors that call addEmptyHolding() in doApply must call canAddHolding() in preflight with th...
Definition View.cpp:1213
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition View.cpp:1066
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:384
bool isLPTokenFrozen(ReadView const &view, AccountID const &account, Issue const &asset, Issue const &asset2)
Definition View.cpp:373
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition View.cpp:935
STLedgerEntry SLE
bool cdirFirst(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the first entry in the directory, advancing the index.
Definition View.cpp:145
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
LedgerEntryType
Identifiers for on-ledger objects.
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
TruncateShares
Definition View.h:949
Number root(Number f, unsigned d)
Definition Number.cpp:636
TER verifyValidDomain(ApplyView &view, AccountID const &account, uint256 domainID, beast::Journal j)
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2780
TER enforceMPTokenAuthorization(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, XRPAmount const &priorBalance, beast::Journal j)
Enforce account has MPToken to match its authorization.
Definition View.cpp:2582
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1203
bool cdirNext(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the next entry in the directory, advancing the index.
Definition View.cpp:156
static TER rippleSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:1849
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
Definition View.cpp:282
std::vector< SField const * > const & getPseudoAccountFields()
Definition View.cpp:1089
@ terNO_ACCOUNT
Definition TER.h:217
@ terNO_RIPPLE
Definition TER.h:224
@ terNO_AMM
Definition TER.h:227
TER authorizeMPToken(ApplyView &view, XRPAmount const &priorBalance, MPTID const &mptIssuanceID, AccountID const &account, beast::Journal journal, std::uint32_t flags=0, std::optional< AccountID > holderID=std::nullopt)
Definition View.cpp:1281
TERSubset< CanCvtToTER > TER
Definition TER.h:645
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:224
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition View.cpp:1508
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
Definition View.cpp:2021
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition View.cpp:1634
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1115
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition View.cpp:2149
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object)
Definition View.cpp:1055
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition View.cpp:615
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Definition View.cpp:182
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
Represents a transfer rate.
Definition Rate.h:40
Returns the RIPEMD-160 digest of the SHA256 hash of the message.
Definition digest.h:136
T time_since_epoch(T... args)
T visit(T... args)