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