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 <xrpld/app/misc/CredentialHelpers.h>
21#include <xrpld/app/tx/detail/MPTokenAuthorize.h>
22#include <xrpld/ledger/ReadView.h>
23#include <xrpld/ledger/View.h>
24
25#include <xrpl/basics/Expected.h>
26#include <xrpl/basics/Log.h>
27#include <xrpl/basics/chrono.h>
28#include <xrpl/beast/utility/instrumentation.h>
29#include <xrpl/protocol/Feature.h>
30#include <xrpl/protocol/Indexes.h>
31#include <xrpl/protocol/LedgerFormats.h>
32#include <xrpl/protocol/MPTIssue.h>
33#include <xrpl/protocol/Protocol.h>
34#include <xrpl/protocol/Quality.h>
35#include <xrpl/protocol/TER.h>
36#include <xrpl/protocol/TxFlags.h>
37#include <xrpl/protocol/digest.h>
38#include <xrpl/protocol/st.h>
39
40#include <optional>
41#include <type_traits>
42#include <variant>
43
44namespace ripple {
45
46namespace detail {
47
48template <
49 class V,
50 class N,
51 class = std::enable_if_t<
52 std::is_same_v<std::remove_cv_t<N>, SLE> &&
53 std::is_base_of_v<ReadView, V>>>
54bool
56 V& view,
57 uint256 const& root,
59 unsigned int& index,
60 uint256& entry)
61{
62 auto const& svIndexes = page->getFieldV256(sfIndexes);
63 XRPL_ASSERT(
64 index <= svIndexes.size(),
65 "ripple::detail::internalDirNext : index inside range");
66
67 if (index >= svIndexes.size())
68 {
69 auto const next = page->getFieldU64(sfIndexNext);
70
71 if (!next)
72 {
73 entry.zero();
74 return false;
75 }
76
77 if constexpr (std::is_const_v<N>)
78 page = view.read(keylet::page(root, next));
79 else
80 page = view.peek(keylet::page(root, next));
81
82 XRPL_ASSERT(page, "ripple::detail::internalDirNext : non-null root");
83
84 if (!page)
85 return false;
86
87 index = 0;
88
89 return internalDirNext(view, root, page, index, entry);
90 }
91
92 entry = svIndexes[index++];
93 return true;
94}
95
96template <
97 class V,
98 class N,
99 class = std::enable_if_t<
100 std::is_same_v<std::remove_cv_t<N>, SLE> &&
101 std::is_base_of_v<ReadView, V>>>
102bool
104 V& view,
105 uint256 const& root,
106 std::shared_ptr<N>& page,
107 unsigned int& index,
108 uint256& entry)
109{
110 if constexpr (std::is_const_v<N>)
111 page = view.read(keylet::page(root));
112 else
113 page = view.peek(keylet::page(root));
114
115 if (!page)
116 return false;
117
118 index = 0;
119
120 return internalDirNext(view, root, page, index, entry);
121}
122
123} // namespace detail
124
125bool
127 ApplyView& view,
128 uint256 const& root,
130 unsigned int& index,
131 uint256& entry)
132{
133 return detail::internalDirFirst(view, root, page, index, entry);
134}
135
136bool
138 ApplyView& view,
139 uint256 const& root,
141 unsigned int& index,
142 uint256& entry)
143{
144 return detail::internalDirNext(view, root, page, index, entry);
145}
146
147bool
149 ReadView const& view,
150 uint256 const& root,
152 unsigned int& index,
153 uint256& entry)
154{
155 return detail::internalDirFirst(view, root, page, index, entry);
156}
157
158bool
160 ReadView const& view,
161 uint256 const& root,
163 unsigned int& index,
164 uint256& entry)
165{
166 return detail::internalDirNext(view, root, page, index, entry);
167}
168
169//------------------------------------------------------------------------------
170//
171// Observers
172//
173//------------------------------------------------------------------------------
174
175bool
177{
178 using d = NetClock::duration;
179 using tp = NetClock::time_point;
180
181 return exp && (view.parentCloseTime() >= tp{d{*exp}});
182}
183
184bool
185isGlobalFrozen(ReadView const& view, AccountID const& issuer)
186{
187 if (isXRP(issuer))
188 return false;
189 if (auto const sle = view.read(keylet::account(issuer)))
190 return sle->isFlag(lsfGlobalFreeze);
191 return false;
192}
193
194bool
195isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
196{
197 if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())))
198 return sle->isFlag(lsfMPTLocked);
199 return false;
200}
201
202bool
203isGlobalFrozen(ReadView const& view, Asset const& asset)
204{
205 return std::visit(
206 [&]<ValidIssueType TIss>(TIss const& issue) {
207 if constexpr (std::is_same_v<TIss, Issue>)
208 return isGlobalFrozen(view, issue.getIssuer());
209 else
210 return isGlobalFrozen(view, issue);
211 },
212 asset.value());
213}
214
215bool
217 ReadView const& view,
218 AccountID const& account,
219 Currency const& currency,
220 AccountID const& issuer)
221{
222 if (isXRP(currency))
223 return false;
224 if (issuer != account)
225 {
226 // Check if the issuer froze the line
227 auto const sle = view.read(keylet::line(account, issuer, currency));
228 if (sle &&
229 sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
230 return true;
231 }
232 return false;
233}
234
235bool
237 ReadView const& view,
238 AccountID const& account,
239 MPTIssue const& mptIssue)
240{
241 if (auto const sle =
242 view.read(keylet::mptoken(mptIssue.getMptID(), account)))
243 return sle->isFlag(lsfMPTLocked);
244 return false;
245}
246
247// Can the specified account spend the specified currency issued by
248// the specified issuer or does the freeze flag prohibit it?
249bool
251 ReadView const& view,
252 AccountID const& account,
253 Currency const& currency,
254 AccountID const& issuer)
255{
256 if (isXRP(currency))
257 return false;
258 auto sle = view.read(keylet::account(issuer));
259 if (sle && sle->isFlag(lsfGlobalFreeze))
260 return true;
261 if (issuer != account)
262 {
263 // Check if the issuer froze the line
264 sle = view.read(keylet::line(account, issuer, currency));
265 if (sle &&
266 sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
267 return true;
268 }
269 return false;
270}
271
272bool
274 ReadView const& view,
275 AccountID const& account,
276 MPTIssue const& mptIssue,
277 int depth)
278{
279 return isGlobalFrozen(view, mptIssue) ||
280 isIndividualFrozen(view, account, mptIssue) ||
281 isVaultPseudoAccountFrozen(view, account, mptIssue, depth);
282}
283
284[[nodiscard]] bool
286 ReadView const& view,
287 std::initializer_list<AccountID> const& accounts,
288 MPTIssue const& mptIssue,
289 int depth)
290{
291 if (isGlobalFrozen(view, mptIssue))
292 return true;
293
294 for (auto const& account : accounts)
295 {
296 if (isIndividualFrozen(view, account, mptIssue))
297 return true;
298 }
299
300 for (auto const& account : accounts)
301 {
302 if (isVaultPseudoAccountFrozen(view, account, mptIssue, depth))
303 return true;
304 }
305
306 return false;
307}
308
309bool
311 ReadView const& view,
312 AccountID const& account,
313 MPTIssue const& mptShare,
314 int depth)
315{
316 if (!view.rules().enabled(featureSingleAssetVault))
317 return false;
318
319 if (depth >= maxAssetCheckDepth)
320 return true; // LCOV_EXCL_LINE
321
322 auto const mptIssuance =
323 view.read(keylet::mptIssuance(mptShare.getMptID()));
324 if (mptIssuance == nullptr)
325 return false; // zero MPToken won't block deletion of MPTokenIssuance
326
327 auto const issuer = mptIssuance->getAccountID(sfIssuer);
328 auto const mptIssuer = view.read(keylet::account(issuer));
329 if (mptIssuer == nullptr)
330 { // LCOV_EXCL_START
331 UNREACHABLE("ripple::isVaultPseudoAccountFrozen : null MPToken issuer");
332 return false;
333 } // LCOV_EXCL_STOP
334
335 if (!mptIssuer->isFieldPresent(sfVaultID))
336 return false; // not a Vault pseudo-account, common case
337
338 auto const vault =
339 view.read(keylet::vault(mptIssuer->getFieldH256(sfVaultID)));
340 if (vault == nullptr)
341 { // LCOV_EXCL_START
342 UNREACHABLE("ripple::isVaultPseudoAccountFrozen : null vault");
343 return false;
344 } // LCOV_EXCL_STOP
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 = requireAuth(
510 view, mptIssue, account, MPTAuthType::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,
583 std::optional<AccountID> const& id = std::nullopt,
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
630 view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj);
631
632 // AMMs have no reserve requirement
633 auto const reserve = sle->isFieldPresent(sfAMMID)
634 ? XRPAmount{0}
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
785bool
787 ReadView const& validLedger,
788 ReadView const& testLedger,
790 char const* reason)
791{
792 bool ret = true;
793
794 if (validLedger.info().seq < testLedger.info().seq)
795 {
796 // valid -> ... -> test
797 auto hash = hashOfSeq(
798 testLedger,
799 validLedger.info().seq,
800 beast::Journal{beast::Journal::getNullSink()});
801 if (hash && (*hash != validLedger.info().hash))
802 {
803 JLOG(s) << reason << " incompatible with valid ledger";
804
805 JLOG(s) << "Hash(VSeq): " << to_string(*hash);
806
807 ret = false;
808 }
809 }
810 else if (validLedger.info().seq > testLedger.info().seq)
811 {
812 // test -> ... -> valid
813 auto hash = hashOfSeq(
814 validLedger,
815 testLedger.info().seq,
816 beast::Journal{beast::Journal::getNullSink()});
817 if (hash && (*hash != testLedger.info().hash))
818 {
819 JLOG(s) << reason << " incompatible preceding ledger";
820
821 JLOG(s) << "Hash(NSeq): " << to_string(*hash);
822
823 ret = false;
824 }
825 }
826 else if (
827 (validLedger.info().seq == testLedger.info().seq) &&
828 (validLedger.info().hash != testLedger.info().hash))
829 {
830 // Same sequence number, different hash
831 JLOG(s) << reason << " incompatible ledger";
832
833 ret = false;
834 }
835
836 if (!ret)
837 {
838 JLOG(s) << "Val: " << validLedger.info().seq << " "
839 << to_string(validLedger.info().hash);
840
841 JLOG(s) << "New: " << testLedger.info().seq << " "
842 << to_string(testLedger.info().hash);
843 }
844
845 return ret;
846}
847
848bool
850 uint256 const& validHash,
851 LedgerIndex validIndex,
852 ReadView const& testLedger,
854 char const* reason)
855{
856 bool ret = true;
857
858 if (testLedger.info().seq > validIndex)
859 {
860 // Ledger we are testing follows last valid ledger
861 auto hash = hashOfSeq(
862 testLedger,
863 validIndex,
865 if (hash && (*hash != validHash))
866 {
867 JLOG(s) << reason << " incompatible following ledger";
868 JLOG(s) << "Hash(VSeq): " << to_string(*hash);
869
870 ret = false;
871 }
872 }
873 else if (
874 (validIndex == testLedger.info().seq) &&
875 (testLedger.info().hash != validHash))
876 {
877 JLOG(s) << reason << " incompatible ledger";
878
879 ret = false;
880 }
881
882 if (!ret)
883 {
884 JLOG(s) << "Val: " << validIndex << " " << to_string(validHash);
885
886 JLOG(s) << "New: " << testLedger.info().seq << " "
887 << to_string(testLedger.info().hash);
888 }
889
890 return ret;
891}
892
893bool
894dirIsEmpty(ReadView const& view, Keylet const& k)
895{
896 auto const sleNode = view.read(k);
897 if (!sleNode)
898 return true;
899 if (!sleNode->getFieldV256(sfIndexes).empty())
900 return false;
901 // The first page of a directory may legitimately be empty even if there
902 // are other pages (the first page is the anchor page) so check to see if
903 // there is another page. If there is, the directory isn't empty.
904 return sleNode->getFieldU64(sfIndexNext) == 0;
905}
906
909{
910 std::set<uint256> amendments;
911
912 if (auto const sle = view.read(keylet::amendments()))
913 {
914 if (sle->isFieldPresent(sfAmendments))
915 {
916 auto const& v = sle->getFieldV256(sfAmendments);
917 amendments.insert(v.begin(), v.end());
918 }
919 }
920
921 return amendments;
922}
923
926{
928
929 if (auto const sle = view.read(keylet::amendments()))
930 {
931 if (sle->isFieldPresent(sfMajorities))
932 {
933 using tp = NetClock::time_point;
934 using d = tp::duration;
935
936 auto const majorities = sle->getFieldArray(sfMajorities);
937
938 for (auto const& m : majorities)
939 ret[m.getFieldH256(sfAmendment)] =
940 tp(d(m.getFieldU32(sfCloseTime)));
941 }
942 }
943
944 return ret;
945}
946
949{
950 // Easy cases...
951 if (seq > ledger.seq())
952 {
953 JLOG(journal.warn())
954 << "Can't get seq " << seq << " from " << ledger.seq() << " future";
955 return std::nullopt;
956 }
957 if (seq == ledger.seq())
958 return ledger.info().hash;
959 if (seq == (ledger.seq() - 1))
960 return ledger.info().parentHash;
961
962 if (int diff = ledger.seq() - seq; diff <= 256)
963 {
964 // Within 256...
965 auto const hashIndex = ledger.read(keylet::skip());
966 if (hashIndex)
967 {
968 XRPL_ASSERT(
969 hashIndex->getFieldU32(sfLastLedgerSequence) ==
970 (ledger.seq() - 1),
971 "ripple::hashOfSeq : matching ledger sequence");
972 STVector256 vec = hashIndex->getFieldV256(sfHashes);
973 if (vec.size() >= diff)
974 return vec[vec.size() - diff];
975 JLOG(journal.warn())
976 << "Ledger " << ledger.seq() << " missing hash for " << seq
977 << " (" << vec.size() << "," << diff << ")";
978 }
979 else
980 {
981 JLOG(journal.warn())
982 << "Ledger " << ledger.seq() << ":" << ledger.info().hash
983 << " missing normal list";
984 }
985 }
986
987 if ((seq & 0xff) != 0)
988 {
989 JLOG(journal.debug())
990 << "Can't get seq " << seq << " from " << ledger.seq() << " past";
991 return std::nullopt;
992 }
993
994 // in skiplist
995 auto const hashIndex = ledger.read(keylet::skip(seq));
996 if (hashIndex)
997 {
998 auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
999 XRPL_ASSERT(lastSeq >= seq, "ripple::hashOfSeq : minimum last ledger");
1000 XRPL_ASSERT(
1001 (lastSeq & 0xff) == 0, "ripple::hashOfSeq : valid last ledger");
1002 auto const diff = (lastSeq - seq) >> 8;
1003 STVector256 vec = hashIndex->getFieldV256(sfHashes);
1004 if (vec.size() > diff)
1005 return vec[vec.size() - diff - 1];
1006 }
1007 JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq()
1008 << " error";
1009 return std::nullopt;
1010}
1011
1012//------------------------------------------------------------------------------
1013//
1014// Modifiers
1015//
1016//------------------------------------------------------------------------------
1017
1018void
1020 ApplyView& view,
1021 std::shared_ptr<SLE> const& sle,
1022 std::int32_t amount,
1024{
1025 if (!sle)
1026 return;
1027 XRPL_ASSERT(amount, "ripple::adjustOwnerCount : nonzero amount input");
1028 std::uint32_t const current{sle->getFieldU32(sfOwnerCount)};
1029 AccountID const id = (*sle)[sfAccount];
1030 std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
1031 view.adjustOwnerCountHook(id, current, adjusted);
1032 sle->setFieldU32(sfOwnerCount, adjusted);
1033 view.update(sle);
1034}
1035
1038{
1039 return [&account](std::shared_ptr<SLE> const& sle) {
1040 (*sle)[sfOwner] = account;
1041 };
1042}
1043
1044TER
1046{
1047 auto const page = view.dirInsert(
1048 keylet::ownerDir(owner), object->key(), describeOwnerDir(owner));
1049 if (!page)
1050 return tecDIR_FULL; // LCOV_EXCL_LINE
1051 object->setFieldU64(sfOwnerNode, *page);
1052 return tesSUCCESS;
1053}
1054
1056pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey)
1057{
1058 // This number must not be changed without an amendment
1059 constexpr std::uint16_t maxAccountAttempts = 256;
1060 for (std::uint16_t i = 0; i < maxAccountAttempts; ++i)
1061 {
1062 ripesha_hasher rsh;
1063 auto const hash = sha512Half(i, view.info().parentHash, pseudoOwnerKey);
1064 rsh(hash.data(), hash.size());
1065 AccountID const ret{static_cast<ripesha_hasher::result_type>(rsh)};
1066 if (!view.read(keylet::account(ret)))
1067 return ret;
1068 }
1069 return beast::zero;
1070}
1071
1072// Note, the list of the pseudo-account designator fields below MUST be
1073// maintained but it does NOT need to be amendment-gated, since a
1074// non-active amendment will not set any field, by definition. Specific
1075// properties of a pseudo-account are NOT checked here, that's what
1076// InvariantCheck is for.
1078 &sfAMMID, //
1079 &sfVaultID, //
1080};
1081
1082Expected<std::shared_ptr<SLE>, TER>
1084 ApplyView& view,
1085 uint256 const& pseudoOwnerKey,
1086 SField const& ownerField)
1087{
1088 XRPL_ASSERT(
1092 [&ownerField](SField const* sf) -> bool {
1093 return *sf == ownerField;
1094 }) == 1,
1095 "ripple::createPseudoAccount : valid owner field");
1096
1097 auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey);
1098 if (accountId == beast::zero)
1099 return Unexpected(tecDUPLICATE);
1100
1101 // Create pseudo-account.
1102 auto account = std::make_shared<SLE>(keylet::account(accountId));
1103 account->setAccountID(sfAccount, accountId);
1104 account->setFieldAmount(sfBalance, STAmount{});
1105
1106 // Pseudo-accounts can't submit transactions, so set the sequence number
1107 // to 0 to make them easier to spot and verify, and add an extra level
1108 // of protection.
1109 std::uint32_t const seqno = //
1110 view.rules().enabled(featureSingleAssetVault) //
1111 ? 0 //
1112 : view.seq();
1113 account->setFieldU32(sfSequence, seqno);
1114 // Ignore reserves requirement, disable the master key, allow default
1115 // rippling, and enable deposit authorization to prevent payments into
1116 // pseudo-account.
1117 account->setFieldU32(
1119 // Link the pseudo-account with its owner object.
1120 account->setFieldH256(ownerField, pseudoOwnerKey);
1121
1122 view.insert(account);
1123
1124 return account;
1125}
1126
1127[[nodiscard]] bool
1129{
1130 // Intentionally use defensive coding here because it's cheap and makes the
1131 // semantics of true return value clean.
1132 return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
1136 [&sleAcct](SField const* sf) -> bool {
1137 return sleAcct->isFieldPresent(*sf);
1138 }) > 0;
1139}
1140
1141[[nodiscard]] TER
1143 ApplyView& view,
1144 AccountID const& accountID,
1145 XRPAmount priorBalance,
1146 Issue const& issue,
1147 beast::Journal journal)
1148{
1149 // Every account can hold XRP.
1150 if (issue.native())
1151 return tesSUCCESS;
1152
1153 auto const& issuerId = issue.getIssuer();
1154 auto const& currency = issue.currency;
1155 if (isGlobalFrozen(view, issuerId))
1156 return tecFROZEN; // LCOV_EXCL_LINE
1157
1158 auto const& srcId = issuerId;
1159 auto const& dstId = accountID;
1160 auto const high = srcId > dstId;
1161 auto const index = keylet::line(srcId, dstId, currency);
1162 auto const sleSrc = view.peek(keylet::account(srcId));
1163 auto const sleDst = view.peek(keylet::account(dstId));
1164 if (!sleDst || !sleSrc)
1165 return tefINTERNAL; // LCOV_EXCL_LINE
1166 if (!sleSrc->isFlag(lsfDefaultRipple))
1167 return tecINTERNAL; // LCOV_EXCL_LINE
1168 // If the line already exists, don't create it again.
1169 if (view.read(index))
1170 return tecDUPLICATE;
1171 return trustCreate(
1172 view,
1173 high,
1174 srcId,
1175 dstId,
1176 index.key,
1177 sleDst,
1178 /*auth=*/false,
1179 /*noRipple=*/true,
1180 /*freeze=*/false,
1181 /*deepFreeze*/ false,
1182 /*balance=*/STAmount{Issue{currency, noAccount()}},
1183 /*limit=*/STAmount{Issue{currency, dstId}},
1184 /*qualityIn=*/0,
1185 /*qualityOut=*/0,
1186 journal);
1187}
1188
1189[[nodiscard]] TER
1191 ApplyView& view,
1192 AccountID const& accountID,
1193 XRPAmount priorBalance,
1194 MPTIssue const& mptIssue,
1195 beast::Journal journal)
1196{
1197 auto const& mptID = mptIssue.getMptID();
1198 auto const mpt = view.peek(keylet::mptIssuance(mptID));
1199 if (!mpt)
1200 return tefINTERNAL; // LCOV_EXCL_LINE
1201 if (mpt->isFlag(lsfMPTLocked))
1202 return tefINTERNAL; // LCOV_EXCL_LINE
1203 if (view.peek(keylet::mptoken(mptID, accountID)))
1204 return tecDUPLICATE;
1205
1206 return MPTokenAuthorize::authorize(
1207 view,
1208 journal,
1209 {.priorBalance = priorBalance,
1210 .mptIssuanceID = mptID,
1211 .account = accountID});
1212}
1213
1214TER
1216 ApplyView& view,
1217 bool const bSrcHigh,
1218 AccountID const& uSrcAccountID,
1219 AccountID const& uDstAccountID,
1220 uint256 const& uIndex, // --> ripple state entry
1221 SLE::ref sleAccount, // --> the account being set.
1222 bool const bAuth, // --> authorize account.
1223 bool const bNoRipple, // --> others cannot ripple through
1224 bool const bFreeze, // --> funds cannot leave
1225 bool bDeepFreeze, // --> can neither receive nor send funds
1226 STAmount const& saBalance, // --> balance of account being set.
1227 // Issuer should be noAccount()
1228 STAmount const& saLimit, // --> limit for account being set.
1229 // Issuer should be the account being set.
1230 std::uint32_t uQualityIn,
1231 std::uint32_t uQualityOut,
1233{
1234 JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", "
1235 << to_string(uDstAccountID) << ", "
1236 << saBalance.getFullText();
1237
1238 auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
1239 auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
1240
1241 auto const sleRippleState = std::make_shared<SLE>(ltRIPPLE_STATE, uIndex);
1242 view.insert(sleRippleState);
1243
1244 auto lowNode = view.dirInsert(
1245 keylet::ownerDir(uLowAccountID),
1246 sleRippleState->key(),
1247 describeOwnerDir(uLowAccountID));
1248
1249 if (!lowNode)
1250 return tecDIR_FULL;
1251
1252 auto highNode = view.dirInsert(
1253 keylet::ownerDir(uHighAccountID),
1254 sleRippleState->key(),
1255 describeOwnerDir(uHighAccountID));
1256
1257 if (!highNode)
1258 return tecDIR_FULL;
1259
1260 bool const bSetDst = saLimit.getIssuer() == uDstAccountID;
1261 bool const bSetHigh = bSrcHigh ^ bSetDst;
1262
1263 XRPL_ASSERT(sleAccount, "ripple::trustCreate : non-null SLE");
1264 if (!sleAccount)
1265 return tefINTERNAL;
1266
1267 XRPL_ASSERT(
1268 sleAccount->getAccountID(sfAccount) ==
1269 (bSetHigh ? uHighAccountID : uLowAccountID),
1270 "ripple::trustCreate : matching account ID");
1271 auto const slePeer =
1272 view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
1273 if (!slePeer)
1274 return tecNO_TARGET;
1275
1276 // Remember deletion hints.
1277 sleRippleState->setFieldU64(sfLowNode, *lowNode);
1278 sleRippleState->setFieldU64(sfHighNode, *highNode);
1279
1280 sleRippleState->setFieldAmount(
1281 bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
1282 sleRippleState->setFieldAmount(
1283 bSetHigh ? sfLowLimit : sfHighLimit,
1285 saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID}));
1286
1287 if (uQualityIn)
1288 sleRippleState->setFieldU32(
1289 bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
1290
1291 if (uQualityOut)
1292 sleRippleState->setFieldU32(
1293 bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
1294
1295 std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve;
1296
1297 if (bAuth)
1298 {
1299 uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth);
1300 }
1301 if (bNoRipple)
1302 {
1303 uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple);
1304 }
1305 if (bFreeze)
1306 {
1307 uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze);
1308 }
1309 if (bDeepFreeze)
1310 {
1311 uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze);
1312 }
1313
1314 if ((slePeer->getFlags() & lsfDefaultRipple) == 0)
1315 {
1316 // The other side's default is no rippling
1317 uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple);
1318 }
1319
1320 sleRippleState->setFieldU32(sfFlags, uFlags);
1321 adjustOwnerCount(view, sleAccount, 1, j);
1322
1323 // ONLY: Create ripple balance.
1324 sleRippleState->setFieldAmount(
1325 sfBalance, bSetHigh ? -saBalance : saBalance);
1326
1327 view.creditHook(
1328 uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
1329
1330 return tesSUCCESS;
1331}
1332
1333[[nodiscard]] TER
1335 ApplyView& view,
1336 AccountID const& accountID,
1337 Issue const& issue,
1338 beast::Journal journal)
1339{
1340 if (issue.native())
1341 {
1342 auto const sle = view.read(keylet::account(accountID));
1343 if (!sle)
1344 return tecINTERNAL;
1345 auto const balance = sle->getFieldAmount(sfBalance);
1346 if (balance.xrp() != 0)
1347 return tecHAS_OBLIGATIONS;
1348 return tesSUCCESS;
1349 }
1350
1351 // `asset` is an IOU.
1352 auto const line = view.peek(keylet::line(accountID, issue));
1353 if (!line)
1354 return tecOBJECT_NOT_FOUND;
1355 if (line->at(sfBalance)->iou() != beast::zero)
1356 return tecHAS_OBLIGATIONS;
1357
1358 // Adjust the owner count(s)
1359 if (line->isFlag(lsfLowReserve))
1360 {
1361 // Clear reserve for low account.
1362 auto sleLowAccount =
1363 view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
1364 if (!sleLowAccount)
1365 return tecINTERNAL;
1366 adjustOwnerCount(view, sleLowAccount, -1, journal);
1367 // It's not really necessary to clear the reserve flag, since the line
1368 // is about to be deleted, but this will make the metadata reflect an
1369 // accurate state at the time of deletion.
1370 line->clearFlag(lsfLowReserve);
1371 }
1372
1373 if (line->isFlag(lsfHighReserve))
1374 {
1375 // Clear reserve for high account.
1376 auto sleHighAccount =
1377 view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
1378 if (!sleHighAccount)
1379 return tecINTERNAL;
1380 adjustOwnerCount(view, sleHighAccount, -1, journal);
1381 // It's not really necessary to clear the reserve flag, since the line
1382 // is about to be deleted, but this will make the metadata reflect an
1383 // accurate state at the time of deletion.
1384 line->clearFlag(lsfHighReserve);
1385 }
1386
1387 return trustDelete(
1388 view,
1389 line,
1390 line->at(sfLowLimit)->getIssuer(),
1391 line->at(sfHighLimit)->getIssuer(),
1392 journal);
1393}
1394
1395[[nodiscard]] TER
1397 ApplyView& view,
1398 AccountID const& accountID,
1399 MPTIssue const& mptIssue,
1400 beast::Journal journal)
1401{
1402 auto const& mptID = mptIssue.getMptID();
1403 auto const mptoken = view.peek(keylet::mptoken(mptID, accountID));
1404 if (!mptoken)
1405 return tecOBJECT_NOT_FOUND;
1406 if (mptoken->at(sfMPTAmount) != 0)
1407 return tecHAS_OBLIGATIONS;
1408
1409 return MPTokenAuthorize::authorize(
1410 view,
1411 journal,
1412 {.priorBalance = {},
1413 .mptIssuanceID = mptID,
1414 .account = accountID,
1415 .flags = tfMPTUnauthorize});
1416}
1417
1418TER
1420 ApplyView& view,
1421 std::shared_ptr<SLE> const& sleRippleState,
1422 AccountID const& uLowAccountID,
1423 AccountID const& uHighAccountID,
1425{
1426 // Detect legacy dirs.
1427 std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode);
1428 std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode);
1429
1430 JLOG(j.trace()) << "trustDelete: Deleting ripple line: low";
1431
1432 if (!view.dirRemove(
1433 keylet::ownerDir(uLowAccountID),
1434 uLowNode,
1435 sleRippleState->key(),
1436 false))
1437 {
1438 return tefBAD_LEDGER;
1439 }
1440
1441 JLOG(j.trace()) << "trustDelete: Deleting ripple line: high";
1442
1443 if (!view.dirRemove(
1444 keylet::ownerDir(uHighAccountID),
1445 uHighNode,
1446 sleRippleState->key(),
1447 false))
1448 {
1449 return tefBAD_LEDGER;
1450 }
1451
1452 JLOG(j.trace()) << "trustDelete: Deleting ripple line: state";
1453 view.erase(sleRippleState);
1454
1455 return tesSUCCESS;
1456}
1457
1458TER
1460{
1461 if (!sle)
1462 return tesSUCCESS;
1463 auto offerIndex = sle->key();
1464 auto owner = sle->getAccountID(sfAccount);
1465
1466 // Detect legacy directories.
1467 uint256 uDirectory = sle->getFieldH256(sfBookDirectory);
1468
1469 if (!view.dirRemove(
1470 keylet::ownerDir(owner),
1471 sle->getFieldU64(sfOwnerNode),
1472 offerIndex,
1473 false))
1474 {
1475 return tefBAD_LEDGER;
1476 }
1477
1478 if (!view.dirRemove(
1479 keylet::page(uDirectory),
1480 sle->getFieldU64(sfBookNode),
1481 offerIndex,
1482 false))
1483 {
1484 return tefBAD_LEDGER;
1485 }
1486
1487 adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j);
1488
1489 view.erase(sle);
1490
1491 return tesSUCCESS;
1492}
1493
1494// Direct send w/o fees:
1495// - Redeeming IOUs and/or sending sender's own IOUs.
1496// - Create trust line if needed.
1497// --> bCheckIssuer : normally require issuer to be involved.
1498static TER
1500 ApplyView& view,
1501 AccountID const& uSenderID,
1502 AccountID const& uReceiverID,
1503 STAmount const& saAmount,
1504 bool bCheckIssuer,
1506{
1507 AccountID const& issuer = saAmount.getIssuer();
1508 Currency const& currency = saAmount.getCurrency();
1509
1510 // Make sure issuer is involved.
1511 XRPL_ASSERT(
1512 !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer,
1513 "ripple::rippleCreditIOU : matching issuer or don't care");
1514 (void)issuer;
1515
1516 // Disallow sending to self.
1517 XRPL_ASSERT(
1518 uSenderID != uReceiverID,
1519 "ripple::rippleCreditIOU : sender is not receiver");
1520
1521 bool const bSenderHigh = uSenderID > uReceiverID;
1522 auto const index = keylet::line(uSenderID, uReceiverID, currency);
1523
1524 XRPL_ASSERT(
1525 !isXRP(uSenderID) && uSenderID != noAccount(),
1526 "ripple::rippleCreditIOU : sender is not XRP");
1527 XRPL_ASSERT(
1528 !isXRP(uReceiverID) && uReceiverID != noAccount(),
1529 "ripple::rippleCreditIOU : receiver is not XRP");
1530
1531 // If the line exists, modify it accordingly.
1532 if (auto const sleRippleState = view.peek(index))
1533 {
1534 STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
1535
1536 if (bSenderHigh)
1537 saBalance.negate(); // Put balance in sender terms.
1538
1539 view.creditHook(uSenderID, uReceiverID, saAmount, saBalance);
1540
1541 STAmount const saBefore = saBalance;
1542
1543 saBalance -= saAmount;
1544
1545 JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> "
1546 << to_string(uReceiverID)
1547 << " : before=" << saBefore.getFullText()
1548 << " amount=" << saAmount.getFullText()
1549 << " after=" << saBalance.getFullText();
1550
1551 std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
1552 bool bDelete = false;
1553
1554 // FIXME This NEEDS to be cleaned up and simplified. It's impossible
1555 // for anyone to understand.
1556 if (saBefore > beast::zero
1557 // Sender balance was positive.
1558 && saBalance <= beast::zero
1559 // Sender is zero or negative.
1560 && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
1561 // Sender reserve is set.
1562 &&
1563 static_cast<bool>(
1564 uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
1565 static_cast<bool>(
1566 view.read(keylet::account(uSenderID))->getFlags() &
1568 !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
1569 !sleRippleState->getFieldAmount(
1570 !bSenderHigh ? sfLowLimit : sfHighLimit)
1571 // Sender trust limit is 0.
1572 && !sleRippleState->getFieldU32(
1573 !bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
1574 // Sender quality in is 0.
1575 && !sleRippleState->getFieldU32(
1576 !bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
1577 // Sender quality out is 0.
1578 {
1579 // Clear the reserve of the sender, possibly delete the line!
1581 view, view.peek(keylet::account(uSenderID)), -1, j);
1582
1583 // Clear reserve flag.
1584 sleRippleState->setFieldU32(
1585 sfFlags,
1586 uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
1587
1588 // Balance is zero, receiver reserve is clear.
1589 bDelete = !saBalance // Balance is zero.
1590 && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve));
1591 // Receiver reserve is clear.
1592 }
1593
1594 if (bSenderHigh)
1595 saBalance.negate();
1596
1597 // Want to reflect balance to zero even if we are deleting line.
1598 sleRippleState->setFieldAmount(sfBalance, saBalance);
1599 // ONLY: Adjust ripple balance.
1600
1601 if (bDelete)
1602 {
1603 return trustDelete(
1604 view,
1605 sleRippleState,
1606 bSenderHigh ? uReceiverID : uSenderID,
1607 !bSenderHigh ? uReceiverID : uSenderID,
1608 j);
1609 }
1610
1611 view.update(sleRippleState);
1612 return tesSUCCESS;
1613 }
1614
1615 STAmount const saReceiverLimit(Issue{currency, uReceiverID});
1616 STAmount saBalance{saAmount};
1617
1618 saBalance.setIssuer(noAccount());
1619
1620 JLOG(j.debug()) << "rippleCreditIOU: "
1621 "create line: "
1622 << to_string(uSenderID) << " -> " << to_string(uReceiverID)
1623 << " : " << saAmount.getFullText();
1624
1625 auto const sleAccount = view.peek(keylet::account(uReceiverID));
1626 if (!sleAccount)
1627 return tefINTERNAL;
1628
1629 bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
1630
1631 return trustCreate(
1632 view,
1633 bSenderHigh,
1634 uSenderID,
1635 uReceiverID,
1636 index.key,
1637 sleAccount,
1638 false,
1639 noRipple,
1640 false,
1641 false,
1642 saBalance,
1643 saReceiverLimit,
1644 0,
1645 0,
1646 j);
1647}
1648
1649// Send regardless of limits.
1650// --> saAmount: Amount/currency/issuer to deliver to receiver.
1651// <-- saActual: Amount actually cost. Sender pays fees.
1652static TER
1654 ApplyView& view,
1655 AccountID const& uSenderID,
1656 AccountID const& uReceiverID,
1657 STAmount const& saAmount,
1658 STAmount& saActual,
1660 WaiveTransferFee waiveFee)
1661{
1662 auto const issuer = saAmount.getIssuer();
1663
1664 XRPL_ASSERT(
1665 !isXRP(uSenderID) && !isXRP(uReceiverID),
1666 "ripple::rippleSendIOU : neither sender nor receiver is XRP");
1667 XRPL_ASSERT(
1668 uSenderID != uReceiverID,
1669 "ripple::rippleSendIOU : sender is not receiver");
1670
1671 if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount())
1672 {
1673 // Direct send: redeeming IOUs and/or sending own IOUs.
1674 auto const ter =
1675 rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j);
1676 if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS)
1677 return ter;
1678 saActual = saAmount;
1679 return tesSUCCESS;
1680 }
1681
1682 // Sending 3rd party IOUs: transit.
1683
1684 // Calculate the amount to transfer accounting
1685 // for any transfer fees if the fee is not waived:
1686 saActual = (waiveFee == WaiveTransferFee::Yes)
1687 ? saAmount
1688 : multiply(saAmount, transferRate(view, issuer));
1689
1690 JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > "
1691 << to_string(uReceiverID)
1692 << " : deliver=" << saAmount.getFullText()
1693 << " cost=" << saActual.getFullText();
1694
1695 TER terResult =
1696 rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j);
1697
1698 if (tesSUCCESS == terResult)
1699 terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j);
1700
1701 return terResult;
1702}
1703
1704static TER
1706 ApplyView& view,
1707 AccountID const& uSenderID,
1708 AccountID const& uReceiverID,
1709 STAmount const& saAmount,
1711 WaiveTransferFee waiveFee)
1712{
1713 if (view.rules().enabled(fixAMMv1_1))
1714 {
1715 if (saAmount < beast::zero || saAmount.holds<MPTIssue>())
1716 {
1717 return tecINTERNAL;
1718 }
1719 }
1720 else
1721 {
1722 XRPL_ASSERT(
1723 saAmount >= beast::zero && !saAmount.holds<MPTIssue>(),
1724 "ripple::accountSendIOU : minimum amount and not MPT");
1725 }
1726
1727 /* If we aren't sending anything or if the sender is the same as the
1728 * receiver then we don't need to do anything.
1729 */
1730 if (!saAmount || (uSenderID == uReceiverID))
1731 return tesSUCCESS;
1732
1733 if (!saAmount.native())
1734 {
1735 STAmount saActual;
1736
1737 JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> "
1738 << to_string(uReceiverID) << " : "
1739 << saAmount.getFullText();
1740
1741 return rippleSendIOU(
1742 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
1743 }
1744
1745 /* XRP send which does not check reserve and can do pure adjustment.
1746 * Note that sender or receiver may be null and this not a mistake; this
1747 * setup is used during pathfinding and it is carefully controlled to
1748 * ensure that transfers are balanced.
1749 */
1750 TER terResult(tesSUCCESS);
1751
1752 SLE::pointer sender = uSenderID != beast::zero
1753 ? view.peek(keylet::account(uSenderID))
1754 : SLE::pointer();
1755 SLE::pointer receiver = uReceiverID != beast::zero
1756 ? view.peek(keylet::account(uReceiverID))
1757 : SLE::pointer();
1758
1759 if (auto stream = j.trace())
1760 {
1761 std::string sender_bal("-");
1762 std::string receiver_bal("-");
1763
1764 if (sender)
1765 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
1766
1767 if (receiver)
1768 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
1769
1770 stream << "accountSendIOU> " << to_string(uSenderID) << " ("
1771 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
1772 << receiver_bal << ") : " << saAmount.getFullText();
1773 }
1774
1775 if (sender)
1776 {
1777 if (sender->getFieldAmount(sfBalance) < saAmount)
1778 {
1779 // VFALCO Its laborious to have to mutate the
1780 // TER based on params everywhere
1781 terResult = view.open() ? TER{telFAILED_PROCESSING}
1783 }
1784 else
1785 {
1786 auto const sndBal = sender->getFieldAmount(sfBalance);
1787 view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal);
1788
1789 // Decrement XRP balance.
1790 sender->setFieldAmount(sfBalance, sndBal - saAmount);
1791 view.update(sender);
1792 }
1793 }
1794
1795 if (tesSUCCESS == terResult && receiver)
1796 {
1797 // Increment XRP balance.
1798 auto const rcvBal = receiver->getFieldAmount(sfBalance);
1799 receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
1800 view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal);
1801
1802 view.update(receiver);
1803 }
1804
1805 if (auto stream = j.trace())
1806 {
1807 std::string sender_bal("-");
1808 std::string receiver_bal("-");
1809
1810 if (sender)
1811 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
1812
1813 if (receiver)
1814 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
1815
1816 stream << "accountSendIOU< " << to_string(uSenderID) << " ("
1817 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
1818 << receiver_bal << ") : " << saAmount.getFullText();
1819 }
1820
1821 return terResult;
1822}
1823
1824static TER
1826 ApplyView& view,
1827 AccountID const& uSenderID,
1828 AccountID const& uReceiverID,
1829 STAmount const& saAmount,
1831{
1832 // Do not check MPT authorization here - it must have been checked earlier
1833 auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
1834 auto const issuer = saAmount.getIssuer();
1835 auto sleIssuance = view.peek(mptID);
1836 if (!sleIssuance)
1837 return tecOBJECT_NOT_FOUND;
1838 if (uSenderID == issuer)
1839 {
1840 (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
1841 view.update(sleIssuance);
1842 }
1843 else
1844 {
1845 auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
1846 if (auto sle = view.peek(mptokenID))
1847 {
1848 auto const amt = sle->getFieldU64(sfMPTAmount);
1849 auto const pay = saAmount.mpt().value();
1850 if (amt < pay)
1851 return tecINSUFFICIENT_FUNDS;
1852 (*sle)[sfMPTAmount] = amt - pay;
1853 view.update(sle);
1854 }
1855 else
1856 return tecNO_AUTH;
1857 }
1858
1859 if (uReceiverID == issuer)
1860 {
1861 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
1862 auto const redeem = saAmount.mpt().value();
1863 if (outstanding >= redeem)
1864 {
1865 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
1866 view.update(sleIssuance);
1867 }
1868 else
1869 return tecINTERNAL;
1870 }
1871 else
1872 {
1873 auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
1874 if (auto sle = view.peek(mptokenID))
1875 {
1876 (*sle)[sfMPTAmount] += saAmount.mpt().value();
1877 view.update(sle);
1878 }
1879 else
1880 return tecNO_AUTH;
1881 }
1882
1883 return tesSUCCESS;
1884}
1885
1886static TER
1888 ApplyView& view,
1889 AccountID const& uSenderID,
1890 AccountID const& uReceiverID,
1891 STAmount const& saAmount,
1892 STAmount& saActual,
1894 WaiveTransferFee waiveFee)
1895{
1896 XRPL_ASSERT(
1897 uSenderID != uReceiverID,
1898 "ripple::rippleSendMPT : sender is not receiver");
1899
1900 // Safe to get MPT since rippleSendMPT is only called by accountSendMPT
1901 auto const issuer = saAmount.getIssuer();
1902
1903 auto const sle =
1904 view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
1905 if (!sle)
1906 return tecOBJECT_NOT_FOUND;
1907
1908 if (uSenderID == issuer || uReceiverID == issuer)
1909 {
1910 // if sender is issuer, check that the new OutstandingAmount will not
1911 // exceed MaximumAmount
1912 if (uSenderID == issuer)
1913 {
1914 auto const sendAmount = saAmount.mpt().value();
1915 auto const maximumAmount =
1916 sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
1917 if (sendAmount > maximumAmount ||
1918 sle->getFieldU64(sfOutstandingAmount) >
1919 maximumAmount - sendAmount)
1920 return tecPATH_DRY;
1921 }
1922
1923 // Direct send: redeeming MPTs and/or sending own MPTs.
1924 auto const ter =
1925 rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
1926 if (ter != tesSUCCESS)
1927 return ter;
1928 saActual = saAmount;
1929 return tesSUCCESS;
1930 }
1931
1932 // Sending 3rd party MPTs: transit.
1933 saActual = (waiveFee == WaiveTransferFee::Yes)
1934 ? saAmount
1935 : multiply(
1936 saAmount,
1937 transferRate(view, saAmount.get<MPTIssue>().getMptID()));
1938
1939 JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > "
1940 << to_string(uReceiverID)
1941 << " : deliver=" << saAmount.getFullText()
1942 << " cost=" << saActual.getFullText();
1943
1944 if (auto const terResult =
1945 rippleCreditMPT(view, issuer, uReceiverID, saAmount, j);
1946 terResult != tesSUCCESS)
1947 return terResult;
1948
1949 return rippleCreditMPT(view, uSenderID, issuer, saActual, j);
1950}
1951
1952static TER
1954 ApplyView& view,
1955 AccountID const& uSenderID,
1956 AccountID const& uReceiverID,
1957 STAmount const& saAmount,
1959 WaiveTransferFee waiveFee)
1960{
1961 XRPL_ASSERT(
1962 saAmount >= beast::zero && saAmount.holds<MPTIssue>(),
1963 "ripple::accountSendMPT : minimum amount and MPT");
1964
1965 /* If we aren't sending anything or if the sender is the same as the
1966 * receiver then we don't need to do anything.
1967 */
1968 if (!saAmount || (uSenderID == uReceiverID))
1969 return tesSUCCESS;
1970
1971 STAmount saActual{saAmount.asset()};
1972
1973 return rippleSendMPT(
1974 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
1975}
1976
1977TER
1979 ApplyView& view,
1980 AccountID const& uSenderID,
1981 AccountID const& uReceiverID,
1982 STAmount const& saAmount,
1984 WaiveTransferFee waiveFee)
1985{
1986 return std::visit(
1987 [&]<ValidIssueType TIss>(TIss const& issue) {
1988 if constexpr (std::is_same_v<TIss, Issue>)
1989 return accountSendIOU(
1990 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
1991 else
1992 return accountSendMPT(
1993 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
1994 },
1995 saAmount.asset().value());
1996}
1997
1998static bool
2000 ApplyView& view,
2001 SLE::pointer state,
2002 bool bSenderHigh,
2003 AccountID const& sender,
2004 STAmount const& before,
2005 STAmount const& after,
2007{
2008 if (!state)
2009 return false;
2010 std::uint32_t const flags(state->getFieldU32(sfFlags));
2011
2012 auto sle = view.peek(keylet::account(sender));
2013 if (!sle)
2014 return false;
2015
2016 // YYY Could skip this if rippling in reverse.
2017 if (before > beast::zero
2018 // Sender balance was positive.
2019 && after <= beast::zero
2020 // Sender is zero or negative.
2021 && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
2022 // Sender reserve is set.
2023 && static_cast<bool>(
2024 flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
2025 static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
2026 !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
2027 !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
2028 // Sender trust limit is 0.
2029 && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
2030 // Sender quality in is 0.
2031 &&
2032 !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
2033 // Sender quality out is 0.
2034 {
2035 // VFALCO Where is the line being deleted?
2036 // Clear the reserve of the sender, possibly delete the line!
2037 adjustOwnerCount(view, sle, -1, j);
2038
2039 // Clear reserve flag.
2040 state->setFieldU32(
2041 sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
2042
2043 // Balance is zero, receiver reserve is clear.
2044 if (!after // Balance is zero.
2045 && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)))
2046 return true;
2047 }
2048 return false;
2049}
2050
2051TER
2053 ApplyView& view,
2054 AccountID const& account,
2055 STAmount const& amount,
2056 Issue const& issue,
2058{
2059 XRPL_ASSERT(
2060 !isXRP(account) && !isXRP(issue.account),
2061 "ripple::issueIOU : neither account nor issuer is XRP");
2062
2063 // Consistency check
2064 XRPL_ASSERT(issue == amount.issue(), "ripple::issueIOU : matching issue");
2065
2066 // Can't send to self!
2067 XRPL_ASSERT(
2068 issue.account != account, "ripple::issueIOU : not issuer account");
2069
2070 JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": "
2071 << amount.getFullText();
2072
2073 bool bSenderHigh = issue.account > account;
2074
2075 auto const index = keylet::line(issue.account, account, issue.currency);
2076
2077 if (auto state = view.peek(index))
2078 {
2079 STAmount final_balance = state->getFieldAmount(sfBalance);
2080
2081 if (bSenderHigh)
2082 final_balance.negate(); // Put balance in sender terms.
2083
2084 STAmount const start_balance = final_balance;
2085
2086 final_balance -= amount;
2087
2088 auto const must_delete = updateTrustLine(
2089 view,
2090 state,
2091 bSenderHigh,
2092 issue.account,
2093 start_balance,
2094 final_balance,
2095 j);
2096
2097 view.creditHook(issue.account, account, amount, start_balance);
2098
2099 if (bSenderHigh)
2100 final_balance.negate();
2101
2102 // Adjust the balance on the trust line if necessary. We do this even if
2103 // we are going to delete the line to reflect the correct balance at the
2104 // time of deletion.
2105 state->setFieldAmount(sfBalance, final_balance);
2106 if (must_delete)
2107 return trustDelete(
2108 view,
2109 state,
2110 bSenderHigh ? account : issue.account,
2111 bSenderHigh ? issue.account : account,
2112 j);
2113
2114 view.update(state);
2115
2116 return tesSUCCESS;
2117 }
2118
2119 // NIKB TODO: The limit uses the receiver's account as the issuer and
2120 // this is unnecessarily inefficient as copying which could be avoided
2121 // is now required. Consider available options.
2122 STAmount const limit(Issue{issue.currency, account});
2123 STAmount final_balance = amount;
2124
2125 final_balance.setIssuer(noAccount());
2126
2127 auto const receiverAccount = view.peek(keylet::account(account));
2128 if (!receiverAccount)
2129 return tefINTERNAL;
2130
2131 bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0;
2132
2133 return trustCreate(
2134 view,
2135 bSenderHigh,
2136 issue.account,
2137 account,
2138 index.key,
2139 receiverAccount,
2140 false,
2141 noRipple,
2142 false,
2143 false,
2144 final_balance,
2145 limit,
2146 0,
2147 0,
2148 j);
2149}
2150
2151TER
2153 ApplyView& view,
2154 AccountID const& account,
2155 STAmount const& amount,
2156 Issue const& issue,
2158{
2159 XRPL_ASSERT(
2160 !isXRP(account) && !isXRP(issue.account),
2161 "ripple::redeemIOU : neither account nor issuer is XRP");
2162
2163 // Consistency check
2164 XRPL_ASSERT(issue == amount.issue(), "ripple::redeemIOU : matching issue");
2165
2166 // Can't send to self!
2167 XRPL_ASSERT(
2168 issue.account != account, "ripple::redeemIOU : not issuer account");
2169
2170 JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": "
2171 << amount.getFullText();
2172
2173 bool bSenderHigh = account > issue.account;
2174
2175 if (auto state =
2176 view.peek(keylet::line(account, issue.account, issue.currency)))
2177 {
2178 STAmount final_balance = state->getFieldAmount(sfBalance);
2179
2180 if (bSenderHigh)
2181 final_balance.negate(); // Put balance in sender terms.
2182
2183 STAmount const start_balance = final_balance;
2184
2185 final_balance -= amount;
2186
2187 auto const must_delete = updateTrustLine(
2188 view, state, bSenderHigh, account, start_balance, final_balance, j);
2189
2190 view.creditHook(account, issue.account, amount, start_balance);
2191
2192 if (bSenderHigh)
2193 final_balance.negate();
2194
2195 // Adjust the balance on the trust line if necessary. We do this even if
2196 // we are going to delete the line to reflect the correct balance at the
2197 // time of deletion.
2198 state->setFieldAmount(sfBalance, final_balance);
2199
2200 if (must_delete)
2201 {
2202 return trustDelete(
2203 view,
2204 state,
2205 bSenderHigh ? issue.account : account,
2206 bSenderHigh ? account : issue.account,
2207 j);
2208 }
2209
2210 view.update(state);
2211 return tesSUCCESS;
2212 }
2213
2214 // In order to hold an IOU, a trust line *MUST* exist to track the
2215 // balance. If it doesn't, then something is very wrong. Don't try
2216 // to continue.
2217 JLOG(j.fatal()) << "redeemIOU: " << to_string(account)
2218 << " attempts to redeem " << amount.getFullText()
2219 << " but no trust line exists!";
2220
2221 return tefINTERNAL;
2222}
2223
2224TER
2226 ApplyView& view,
2227 AccountID const& from,
2228 AccountID const& to,
2229 STAmount const& amount,
2231{
2232 XRPL_ASSERT(
2233 from != beast::zero, "ripple::transferXRP : nonzero from account");
2234 XRPL_ASSERT(to != beast::zero, "ripple::transferXRP : nonzero to account");
2235 XRPL_ASSERT(from != to, "ripple::transferXRP : sender is not receiver");
2236 XRPL_ASSERT(amount.native(), "ripple::transferXRP : amount is XRP");
2237
2238 SLE::pointer const sender = view.peek(keylet::account(from));
2239 SLE::pointer const receiver = view.peek(keylet::account(to));
2240 if (!sender || !receiver)
2241 return tefINTERNAL;
2242
2243 JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> "
2244 << to_string(to) << ") : " << amount.getFullText();
2245
2246 if (sender->getFieldAmount(sfBalance) < amount)
2247 {
2248 // VFALCO Its unfortunate we have to keep
2249 // mutating these TER everywhere
2250 // FIXME: this logic should be moved to callers maybe?
2251 return view.open() ? TER{telFAILED_PROCESSING}
2253 }
2254
2255 // Decrement XRP balance.
2256 sender->setFieldAmount(
2257 sfBalance, sender->getFieldAmount(sfBalance) - amount);
2258 view.update(sender);
2259
2260 receiver->setFieldAmount(
2261 sfBalance, receiver->getFieldAmount(sfBalance) + amount);
2262 view.update(receiver);
2263
2264 return tesSUCCESS;
2265}
2266
2267TER
2268requireAuth(ReadView const& view, Issue const& issue, AccountID const& account)
2269{
2270 if (isXRP(issue) || issue.account == account)
2271 return tesSUCCESS;
2272 if (auto const issuerAccount = view.read(keylet::account(issue.account));
2273 issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth)
2274 {
2275 if (auto const trustLine =
2276 view.read(keylet::line(account, issue.account, issue.currency)))
2277 return ((*trustLine)[sfFlags] &
2278 ((account > issue.account) ? lsfLowAuth : lsfHighAuth))
2279 ? tesSUCCESS
2280 : TER{tecNO_AUTH};
2281 return TER{tecNO_LINE};
2282 }
2283
2284 return tesSUCCESS;
2285}
2286
2287TER
2289 ReadView const& view,
2290 MPTIssue const& mptIssue,
2291 AccountID const& account,
2292 MPTAuthType authType,
2293 int depth)
2294{
2295 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
2296 auto const sleIssuance = view.read(mptID);
2297 if (!sleIssuance)
2298 return tecOBJECT_NOT_FOUND;
2299
2300 auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
2301
2302 // issuer is always "authorized"
2303 if (mptIssuer == account) // Issuer won't have MPToken
2304 return tesSUCCESS;
2305
2306 if (view.rules().enabled(featureSingleAssetVault))
2307 {
2308 if (depth >= maxAssetCheckDepth)
2309 return tecINTERNAL; // LCOV_EXCL_LINE
2310
2311 // requireAuth is recursive if the issuer is a vault pseudo-account
2312 auto const sleIssuer = view.read(keylet::account(mptIssuer));
2313 if (!sleIssuer)
2314 return tefINTERNAL; // LCOV_EXCL_LINE
2315
2316 if (sleIssuer->isFieldPresent(sfVaultID))
2317 {
2318 auto const sleVault =
2319 view.read(keylet::vault(sleIssuer->getFieldH256(sfVaultID)));
2320 if (!sleVault)
2321 return tefINTERNAL; // LCOV_EXCL_LINE
2322
2323 auto const asset = sleVault->at(sfAsset);
2324 if (auto const err = std::visit(
2325 [&]<ValidIssueType TIss>(TIss const& issue) {
2326 if constexpr (std::is_same_v<TIss, Issue>)
2327 return requireAuth(view, issue, account);
2328 else
2329 return requireAuth(
2330 view, issue, account, authType, depth + 1);
2331 },
2332 asset.value());
2333 !isTesSuccess(err))
2334 return err;
2335 }
2336 }
2337
2338 auto const mptokenID = keylet::mptoken(mptID.key, account);
2339 auto const sleToken = view.read(mptokenID);
2340
2341 // if account has no MPToken, fail
2342 if (!sleToken && authType == MPTAuthType::StrongAuth)
2343 return tecNO_AUTH;
2344
2345 // Note, this check is not amendment-gated because DomainID will be always
2346 // empty **unless** writing to it has been enabled by an amendment
2347 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
2348 if (maybeDomainID)
2349 {
2350 XRPL_ASSERT(
2351 sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth,
2352 "ripple::requireAuth : issuance requires authorization");
2353 // ter = tefINTERNAL | tecOBJECT_NOT_FOUND | tecNO_AUTH | tecEXPIRED
2354 if (auto const ter =
2355 credentials::validDomain(view, *maybeDomainID, account);
2357 return ter; // Note: sleToken might be null
2358 else if (!sleToken)
2359 return ter;
2360 // We ignore error from validDomain if we found sleToken, as it could
2361 // belong to someone who is explicitly authorized e.g. a vault owner.
2362 }
2363
2364 // mptoken must be authorized if issuance enabled requireAuth
2365 if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
2366 (!sleToken || !sleToken->isFlag(lsfMPTAuthorized)))
2367 return tecNO_AUTH;
2368
2369 return tesSUCCESS; // Note: sleToken might be null
2370}
2371
2372[[nodiscard]] TER
2374 ApplyView& view,
2375 MPTID const& mptIssuanceID,
2376 AccountID const& account,
2377 XRPAmount const& priorBalance, // for MPToken authorization
2379{
2380 auto const sleIssuance = view.read(keylet::mptIssuance(mptIssuanceID));
2381 if (!sleIssuance)
2382 return tefINTERNAL; // LCOV_EXCL_LINE
2383
2384 XRPL_ASSERT(
2385 sleIssuance->isFlag(lsfMPTRequireAuth),
2386 "ripple::enforceMPTokenAuthorization : authorization required");
2387
2388 if (account == sleIssuance->at(sfIssuer))
2389 return tefINTERNAL; // LCOV_EXCL_LINE
2390
2391 auto const keylet = keylet::mptoken(mptIssuanceID, account);
2392 auto const sleToken = view.read(keylet); // NOTE: might be null
2393 auto const maybeDomainID = sleIssuance->at(~sfDomainID);
2394 bool const authorizedByDomain = maybeDomainID.has_value() &&
2395 verifyValidDomain(view, account, *maybeDomainID, j) == tesSUCCESS;
2396
2397 if (!authorizedByDomain && sleToken == nullptr)
2398 {
2399 // Could not find MPToken and won't create one, could be either of:
2400 //
2401 // 1. Field sfDomainID not set in MPTokenIssuance or
2402 // 2. Account has no matching and accepted credentials or
2403 // 3. Account has all expired credentials (deleted in verifyValidDomain)
2404 //
2405 // Either way, return tecNO_AUTH and there is nothing else to do
2406 return tecNO_AUTH;
2407 }
2408 else if (!authorizedByDomain && maybeDomainID.has_value())
2409 {
2410 // Found an MPToken but the account is not authorized and we expect
2411 // it to have been authorized by the domain. This could be because the
2412 // credentials used to create the MPToken have expired or been deleted.
2413 return tecNO_AUTH;
2414 }
2415 else if (!authorizedByDomain)
2416 {
2417 // We found an MPToken, but sfDomainID is not set, so this is a classic
2418 // MPToken which requires authorization by the token issuer.
2419 XRPL_ASSERT(
2420 sleToken != nullptr && !maybeDomainID.has_value(),
2421 "ripple::enforceMPTokenAuthorization : found MPToken");
2422 if (sleToken->isFlag(lsfMPTAuthorized))
2423 return tesSUCCESS;
2424
2425 return tecNO_AUTH;
2426 }
2427 else if (authorizedByDomain && sleToken != nullptr)
2428 {
2429 // Found an MPToken, authorized by the domain. Ignore authorization flag
2430 // lsfMPTAuthorized because it is meaningless. Return tesSUCCESS
2431 XRPL_ASSERT(
2432 maybeDomainID.has_value(),
2433 "ripple::enforceMPTokenAuthorization : found MPToken for domain");
2434 return tesSUCCESS;
2435 }
2436 else if (authorizedByDomain)
2437 {
2438 // Could not find MPToken but there should be one because we are
2439 // authorized by domain. Proceed to create it, then return tesSUCCESS
2440 XRPL_ASSERT(
2441 maybeDomainID.has_value() && sleToken == nullptr,
2442 "ripple::enforceMPTokenAuthorization : new MPToken for domain");
2443 if (auto const err = MPTokenAuthorize::authorize(
2444 view,
2445 j,
2446 {
2447 .priorBalance = priorBalance,
2448 .mptIssuanceID = mptIssuanceID,
2449 .account = account,
2450 .flags = 0,
2451 });
2452 !isTesSuccess(err))
2453 return err;
2454
2455 return tesSUCCESS;
2456 }
2457
2458 // LCOV_EXCL_START
2459 UNREACHABLE(
2460 "ripple::enforceMPTokenAuthorization : condition list is incomplete");
2461 return tefINTERNAL;
2462} // LCOV_EXCL_STOP
2463
2464TER
2466 ReadView const& view,
2467 MPTIssue const& mptIssue,
2468 AccountID const& from,
2469 AccountID const& to)
2470{
2471 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
2472 auto const sleIssuance = view.read(mptID);
2473 if (!sleIssuance)
2474 return tecOBJECT_NOT_FOUND;
2475
2476 if (!(sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer))
2477 {
2478 if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
2479 return TER{tecNO_AUTH};
2480 }
2481 return tesSUCCESS;
2482}
2483
2484TER
2486 ApplyView& view,
2487 Keylet const& ownerDirKeylet,
2488 EntryDeleter const& deleter,
2490 std::optional<uint16_t> maxNodesToDelete)
2491{
2492 // Delete all the entries in the account directory.
2493 std::shared_ptr<SLE> sleDirNode{};
2494 unsigned int uDirEntry{0};
2495 uint256 dirEntry{beast::zero};
2496 std::uint32_t deleted = 0;
2497
2498 if (view.exists(ownerDirKeylet) &&
2499 dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
2500 {
2501 do
2502 {
2503 if (maxNodesToDelete && ++deleted > *maxNodesToDelete)
2504 return tecINCOMPLETE;
2505
2506 // Choose the right way to delete each directory node.
2507 auto sleItem = view.peek(keylet::child(dirEntry));
2508 if (!sleItem)
2509 {
2510 // Directory node has an invalid index. Bail out.
2511 JLOG(j.fatal())
2512 << "DeleteAccount: Directory node in ledger " << view.seq()
2513 << " has index to object that is missing: "
2514 << to_string(dirEntry);
2515 return tefBAD_LEDGER;
2516 }
2517
2518 LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
2519 sleItem->getFieldU16(sfLedgerEntryType))};
2520
2521 // Deleter handles the details of specific account-owned object
2522 // deletion
2523 auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem);
2524 if (ter != tesSUCCESS)
2525 return ter;
2526
2527 // dirFirst() and dirNext() are like iterators with exposed
2528 // internal state. We'll take advantage of that exposed state
2529 // to solve a common C++ problem: iterator invalidation while
2530 // deleting elements from a container.
2531 //
2532 // We have just deleted one directory entry, which means our
2533 // "iterator state" is invalid.
2534 //
2535 // 1. During the process of getting an entry from the
2536 // directory uDirEntry was incremented from 'it' to 'it'+1.
2537 //
2538 // 2. We then deleted the entry at index 'it', which means the
2539 // entry that was at 'it'+1 has now moved to 'it'.
2540 //
2541 // 3. So we verify that uDirEntry is indeed 'it'+1. Then we jam it
2542 // back to 'it' to "un-invalidate" the iterator.
2543 XRPL_ASSERT(
2544 uDirEntry >= 1,
2545 "ripple::cleanupOnAccountDelete : minimum dir entries");
2546 if (uDirEntry == 0)
2547 {
2548 JLOG(j.error())
2549 << "DeleteAccount iterator re-validation failed.";
2550 return tefBAD_LEDGER;
2551 }
2552 if (skipEntry == SkipEntry::No)
2553 uDirEntry--;
2554
2555 } while (
2556 dirNext(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
2557 }
2558
2559 return tesSUCCESS;
2560}
2561
2562TER
2564 ApplyView& view,
2565 std::shared_ptr<SLE> sleState,
2566 std::optional<AccountID> const& ammAccountID,
2568{
2569 if (!sleState || sleState->getType() != ltRIPPLE_STATE)
2570 return tecINTERNAL;
2571
2572 auto const& [low, high] = std::minmax(
2573 sleState->getFieldAmount(sfLowLimit).getIssuer(),
2574 sleState->getFieldAmount(sfHighLimit).getIssuer());
2575 auto sleLow = view.peek(keylet::account(low));
2576 auto sleHigh = view.peek(keylet::account(high));
2577 if (!sleLow || !sleHigh)
2578 return tecINTERNAL;
2579 bool const ammLow = sleLow->isFieldPresent(sfAMMID);
2580 bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
2581
2582 // can't both be AMM
2583 if (ammLow && ammHigh)
2584 return tecINTERNAL;
2585
2586 // at least one must be
2587 if (!ammLow && !ammHigh)
2588 return terNO_AMM;
2589
2590 // one must be the target amm
2591 if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
2592 return terNO_AMM;
2593
2594 if (auto const ter = trustDelete(view, sleState, low, high, j);
2595 ter != tesSUCCESS)
2596 {
2597 JLOG(j.error())
2598 << "deleteAMMTrustLine: failed to delete the trustline.";
2599 return ter;
2600 }
2601
2602 auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
2603 if (!(sleState->getFlags() & uFlags))
2604 return tecINTERNAL;
2605
2606 adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
2607
2608 return tesSUCCESS;
2609}
2610
2611TER
2613 ApplyView& view,
2614 AccountID const& uSenderID,
2615 AccountID const& uReceiverID,
2616 STAmount const& saAmount,
2617 bool bCheckIssuer,
2619{
2620 return std::visit(
2621 [&]<ValidIssueType TIss>(TIss const& issue) {
2622 if constexpr (std::is_same_v<TIss, Issue>)
2623 {
2624 return rippleCreditIOU(
2625 view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
2626 }
2627 else
2628 {
2629 XRPL_ASSERT(
2630 !bCheckIssuer,
2631 "ripple::rippleCredit : not checking issuer");
2632 return rippleCreditMPT(
2633 view, uSenderID, uReceiverID, saAmount, j);
2634 }
2635 },
2636 saAmount.asset().value());
2637}
2638
2639[[nodiscard]] STAmount
2641 std::shared_ptr<SLE const> const& vault,
2642 std::shared_ptr<SLE const> const& issuance,
2643 STAmount const& assets)
2644{
2645 XRPL_ASSERT(
2646 assets.asset() == vault->at(sfAsset),
2647 "ripple::assetsToSharesDeposit : assets and vault match");
2648 Number assetTotal = vault->at(sfAssetsTotal);
2649 STAmount shares{vault->at(sfShareMPTID), static_cast<Number>(assets)};
2650 if (assetTotal == 0)
2651 return shares;
2652 Number shareTotal = issuance->at(sfOutstandingAmount);
2653 shares = shareTotal * (assets / assetTotal);
2654 return shares;
2655}
2656
2657[[nodiscard]] STAmount
2659 std::shared_ptr<SLE const> const& vault,
2660 std::shared_ptr<SLE const> const& issuance,
2661 STAmount const& assets)
2662{
2663 XRPL_ASSERT(
2664 assets.asset() == vault->at(sfAsset),
2665 "ripple::assetsToSharesWithdraw : assets and vault match");
2666 Number assetTotal = vault->at(sfAssetsTotal);
2667 assetTotal -= vault->at(sfLossUnrealized);
2668 STAmount shares{vault->at(sfShareMPTID)};
2669 if (assetTotal == 0)
2670 return shares;
2671 Number shareTotal = issuance->at(sfOutstandingAmount);
2672 shares = shareTotal * (assets / assetTotal);
2673 return shares;
2674}
2675
2676[[nodiscard]] STAmount
2678 std::shared_ptr<SLE const> const& vault,
2679 std::shared_ptr<SLE const> const& issuance,
2680 STAmount const& shares)
2681{
2682 XRPL_ASSERT(
2683 shares.asset() == vault->at(sfShareMPTID),
2684 "ripple::sharesToAssetsWithdraw : shares and vault match");
2685 Number assetTotal = vault->at(sfAssetsTotal);
2686 assetTotal -= vault->at(sfLossUnrealized);
2687 STAmount assets{vault->at(sfAsset)};
2688 if (assetTotal == 0)
2689 return assets;
2690 Number shareTotal = issuance->at(sfOutstandingAmount);
2691 assets = assetTotal * (shares / shareTotal);
2692 return assets;
2693}
2694
2695bool
2697{
2698 return now.time_since_epoch().count() > mark;
2699}
2700
2701} // 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:144
virtual void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
Definition: ApplyView.h:244
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
Definition: ApplyView.cpp:190
virtual void adjustOwnerCountHook(AccountID const &account, std::uint32_t cur, std::uint32_t next)
Definition: ApplyView.h:255
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
std::optional< std::uint64_t > dirInsert(Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Insert an entry to a directory.
Definition: ApplyView.h:318
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
constexpr value_type const & value() const
Definition: Asset.h:156
A currency issued by an account.
Definition: Issue.h:36
AccountID account
Definition: Issue.h:39
Currency currency
Definition: Issue.h:38
AccountID const & getIssuer() const
Definition: Issue.h:48
bool native() const
Definition: Issue.cpp:66
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
A view into a ledger.
Definition: ReadView.h:52
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition: ReadView.h:112
virtual std::uint32_t ownerCountHook(AccountID const &account, std::uint32_t count) const
Definition: ReadView.h:193
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
Definition: ReadView.h:179
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:119
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:143
constexpr bool holds() const noexcept
Definition: STAmount.h:465
Asset const & asset() const
Definition: STAmount.h:483
constexpr TIss const & get() const
void setIssuer(AccountID const &uIssuer)
Definition: STAmount.h:588
Currency const & getCurrency() const
Definition: STAmount.h:502
XRPAmount xrp() const
Definition: STAmount.cpp:306
AccountID const & getIssuer() const
Definition: STAmount.h:508
MPTAmount mpt() const
Definition: STAmount.cpp:337
Issue const & issue() const
Definition: STAmount.h:496
std::string getFullText() const override
Definition: STAmount.cpp:540
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
Definition: STLedgerEntry.h:38
std::size_t size() const
Definition: STVector256.h:168
A balance matches.
Definition: balance.h:39
Match set account flags.
Definition: flags.h:125
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
T count_if(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:103
bool internalDirNext(V &view, uint256 const &root, std::shared_ptr< N > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:55
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition: Indexes.cpp:533
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition: Indexes.cpp:183
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:439
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:237
Keylet const & amendments() noexcept
The index of the amendment table.
Definition: Indexes.cpp:207
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:519
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition: Indexes.cpp:557
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:177
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition: Indexes.cpp:373
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:367
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition: Indexes.cpp:189
std::uint32_t ownerCount(Env const &env, Account const &account)
Definition: TestHelpers.cpp:54
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition: AccountID.h:49
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:185
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:1083
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:1999
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:127
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:78
@ fhZERO_IF_FROZEN
Definition: View.h:78
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:148
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:178
STAmount sharesToAssetsWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &shares)
Definition: View.cpp:2677
@ telFAILED_PROCESSING
Definition: TER.h:56
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:216
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition: View.cpp:2563
static TER rippleSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1887
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:126
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:137
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account)
Check if the account lacks required authorization.
Definition: View.cpp:2268
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:948
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition: View.cpp:762
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
Definition: Protocol.h:117
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:2152
@ lsfHighDeepFreeze
@ lsfMPTCanTransfer
@ lsfDefaultRipple
@ lsfHighNoRipple
@ lsfRequireAuth
@ lsfHighFreeze
@ lsfLowNoRipple
@ lsfDisableMaster
@ lsfHighReserve
@ lsfDepositAuth
@ lsfMPTRequireAuth
@ lsfMPTAuthorized
@ lsfGlobalFreeze
@ lsfLowReserve
@ lsfLowFreeze
@ lsfMPTLocked
@ lsfLowDeepFreeze
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:53
AuthHandling
Controls the treatment of unauthorized MPT balances.
Definition: View.h:81
@ ahZERO_IF_UNAUTHORIZED
Definition: View.h:81
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition: View.cpp:1037
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition: View.cpp:2225
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:2373
static std::array< SField const *, 2 > const pseudoAccountOwnerFields
Definition: View.cpp:1077
@ current
This was a new validation and was added.
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
Definition: View.cpp:1459
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:250
std::set< uint256 > getEnabledAmendments(ReadView const &view)
Definition: View.cpp:908
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:2465
@ tefBAD_LEDGER
Definition: TER.h:170
@ tefINTERNAL
Definition: TER.h:173
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:1019
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:1499
static bool adjustOwnerCount(ApplyContext &ctx, int count)
Definition: SetOracle.cpp:186
constexpr std::uint32_t const tfMPTUnauthorize
Definition: TxFlags.h:160
bool isVaultPseudoAccountFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptShare, int depth)
Definition: View.cpp:310
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:2052
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1705
std::map< uint256, NetClock::time_point > majorityAmendments_t
Definition: View.h:371
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:159
WaiveTransferFee
Definition: View.h:44
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uQualityIn, std::uint32_t uQualityOut, beast::Journal j)
Create a trust line.
Definition: View.cpp:1215
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition: View.cpp:1419
@ 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
@ tecFAILED_PROCESSING
Definition: TER.h:286
@ tecNO_AUTH
Definition: TER.h:300
@ tesSUCCESS
Definition: TER.h:244
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, Issue const &issue, beast::Journal journal)
Definition: View.cpp:1142
AccountID pseudoAccountAddress(ReadView const &view, uint256 const &pseudoOwnerKey)
Definition: View.cpp:1056
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:672
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition: View.cpp:925
STLedgerEntry SLE
Definition: STLedgerEntry.h:97
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:54
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
MPTAuthType
Definition: View.h:710
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition: View.cpp:2696
STAmount assetsToSharesWithdraw(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition: View.cpp:2658
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< uint16_t > maxNodesToDelete)
Definition: View.cpp:2485
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
Number root(Number f, unsigned d)
Definition: Number.cpp:636
TER verifyValidDomain(ApplyView &view, AccountID const &account, uint256 domainID, beast::Journal j)
STAmount assetsToSharesDeposit(std::shared_ptr< SLE const > const &vault, std::shared_ptr< SLE const > const &issuance, STAmount const &assets)
Definition: View.cpp:2640
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition: View.cpp:176
static TER rippleSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1653
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:2612
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth)
Definition: View.cpp:285
@ terNO_AMM
Definition: TER.h:227
TERSubset< CanCvtToTER > TER
Definition: TER.h:643
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:225
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, Issue const &issue, beast::Journal journal)
Definition: View.cpp:1334
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:786
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
Definition: View.cpp:1825
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition: View.cpp:894
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition: View.cpp:1128
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Calls static accountSendIOU if saAmount represents Issue.
Definition: View.cpp:1978
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1953
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object)
Definition: View.cpp:1045
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:185
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
Definition: protocol/Fees.h:49
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:39
uint256 key
Definition: Keylet.h:40
Represents a transfer rate.
Definition: Rate.h:40
Returns the RIPEMD-160 digest of the SHA256 hash of the message.
Definition: digest.h:137
Set the sequence number on a JTx.
Definition: seq.h:34
T time_since_epoch(T... args)
T visit(T... args)