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