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/ledger/ReadView.h>
21#include <xrpld/ledger/View.h>
22#include <xrpl/basics/Log.h>
23#include <xrpl/basics/chrono.h>
24#include <xrpl/basics/contract.h>
25#include <xrpl/beast/utility/instrumentation.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Protocol.h>
28#include <xrpl/protocol/Quality.h>
29#include <xrpl/protocol/st.h>
30#include <optional>
31
32namespace ripple {
33
34namespace detail {
35
36template <
37 class V,
38 class N,
39 class = std::enable_if_t<
40 std::is_same_v<std::remove_cv_t<N>, SLE> &&
41 std::is_base_of_v<ReadView, V>>>
42bool
44 V& view,
45 uint256 const& root,
47 unsigned int& index,
48 uint256& entry)
49{
50 auto const& svIndexes = page->getFieldV256(sfIndexes);
51 XRPL_ASSERT(
52 index <= svIndexes.size(),
53 "ripple::detail::internalDirNext : index inside range");
54
55 if (index >= svIndexes.size())
56 {
57 auto const next = page->getFieldU64(sfIndexNext);
58
59 if (!next)
60 {
61 entry.zero();
62 return false;
63 }
64
65 if constexpr (std::is_const_v<N>)
66 page = view.read(keylet::page(root, next));
67 else
68 page = view.peek(keylet::page(root, next));
69
70 XRPL_ASSERT(page, "ripple::detail::internalDirNext : non-null root");
71
72 if (!page)
73 return false;
74
75 index = 0;
76
77 return internalDirNext(view, root, page, index, entry);
78 }
79
80 entry = svIndexes[index++];
81 return true;
82}
83
84template <
85 class V,
86 class N,
87 class = std::enable_if_t<
88 std::is_same_v<std::remove_cv_t<N>, SLE> &&
89 std::is_base_of_v<ReadView, V>>>
90bool
92 V& view,
93 uint256 const& root,
95 unsigned int& index,
96 uint256& entry)
97{
98 if constexpr (std::is_const_v<N>)
99 page = view.read(keylet::page(root));
100 else
101 page = view.peek(keylet::page(root));
102
103 if (!page)
104 return false;
105
106 index = 0;
107
108 return internalDirNext(view, root, page, index, entry);
109}
110
111} // namespace detail
112
113bool
115 ApplyView& view,
116 uint256 const& root,
118 unsigned int& index,
119 uint256& entry)
120{
121 return detail::internalDirFirst(view, root, page, index, entry);
122}
123
124bool
126 ApplyView& view,
127 uint256 const& root,
129 unsigned int& index,
130 uint256& entry)
131{
132 return detail::internalDirNext(view, root, page, index, entry);
133}
134
135bool
137 ReadView const& view,
138 uint256 const& root,
140 unsigned int& index,
141 uint256& entry)
142{
143 return detail::internalDirFirst(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::internalDirNext(view, root, page, index, entry);
155}
156
157//------------------------------------------------------------------------------
158//
159// Observers
160//
161//------------------------------------------------------------------------------
162
163bool
165{
166 using d = NetClock::duration;
167 using tp = NetClock::time_point;
168
169 return exp && (view.parentCloseTime() >= tp{d{*exp}});
170}
171
172bool
173isGlobalFrozen(ReadView const& view, AccountID const& issuer)
174{
175 if (isXRP(issuer))
176 return false;
177 if (auto const sle = view.read(keylet::account(issuer)))
178 return sle->isFlag(lsfGlobalFreeze);
179 return false;
180}
181
182bool
183isGlobalFrozen(ReadView const& view, MPTIssue const& mptIssue)
184{
185 if (auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID())))
186 return sle->getFlags() & lsfMPTLocked;
187 return false;
188}
189
190bool
191isGlobalFrozen(ReadView const& view, Asset const& asset)
192{
193 return std::visit(
194 [&]<ValidIssueType TIss>(TIss const& issue) {
195 if constexpr (std::is_same_v<TIss, Issue>)
196 return isGlobalFrozen(view, issue.getIssuer());
197 else
198 return isGlobalFrozen(view, issue);
199 },
200 asset.value());
201}
202
203bool
205 ReadView const& view,
206 AccountID const& account,
207 Currency const& currency,
208 AccountID const& issuer)
209{
210 if (isXRP(currency))
211 return false;
212 if (issuer != account)
213 {
214 // Check if the issuer froze the line
215 auto const sle = view.read(keylet::line(account, issuer, currency));
216 if (sle &&
217 sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
218 return true;
219 }
220 return false;
221}
222
223bool
225 ReadView const& view,
226 AccountID const& account,
227 MPTIssue const& mptIssue)
228{
229 if (auto const sle =
230 view.read(keylet::mptoken(mptIssue.getMptID(), account)))
231 return sle->getFlags() & lsfMPTLocked;
232 return false;
233}
234
235// Can the specified account spend the specified currency issued by
236// the specified issuer or does the freeze flag prohibit it?
237bool
239 ReadView const& view,
240 AccountID const& account,
241 Currency const& currency,
242 AccountID const& issuer)
243{
244 if (isXRP(currency))
245 return false;
246 auto sle = view.read(keylet::account(issuer));
247 if (sle && sle->isFlag(lsfGlobalFreeze))
248 return true;
249 if (issuer != account)
250 {
251 // Check if the issuer froze the line
252 sle = view.read(keylet::line(account, issuer, currency));
253 if (sle &&
254 sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
255 return true;
256 }
257 return false;
258}
259
260bool
262 ReadView const& view,
263 AccountID const& account,
264 MPTIssue const& mptIssue)
265{
266 return isGlobalFrozen(view, mptIssue) ||
267 isIndividualFrozen(view, account, mptIssue);
268}
269
270STAmount
272 ReadView const& view,
273 AccountID const& account,
274 Currency const& currency,
275 AccountID const& issuer,
276 FreezeHandling zeroIfFrozen,
278{
279 STAmount amount;
280 if (isXRP(currency))
281 {
282 return {xrpLiquid(view, account, 0, j)};
283 }
284
285 // IOU: Return balance on trust line modulo freeze
286 auto const sle = view.read(keylet::line(account, issuer, currency));
287 if (!sle)
288 {
289 amount.clear(Issue{currency, issuer});
290 }
291 else if (
292 (zeroIfFrozen == fhZERO_IF_FROZEN) &&
293 isFrozen(view, account, currency, issuer))
294 {
295 amount.clear(Issue{currency, issuer});
296 }
297 else
298 {
299 amount = sle->getFieldAmount(sfBalance);
300 if (account > issuer)
301 {
302 // Put balance in account terms.
303 amount.negate();
304 }
305 amount.setIssuer(issuer);
306 }
307 JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(account)
308 << " amount=" << amount.getFullText();
309
310 return view.balanceHook(account, issuer, amount);
311}
312
313STAmount
315 ReadView const& view,
316 AccountID const& account,
317 Issue const& issue,
318 FreezeHandling zeroIfFrozen,
320{
321 return accountHolds(
322 view, account, issue.currency, issue.account, zeroIfFrozen, j);
323}
324
325STAmount
327 ReadView const& view,
328 AccountID const& account,
329 MPTIssue const& mptIssue,
330 FreezeHandling zeroIfFrozen,
331 AuthHandling zeroIfUnauthorized,
333{
334 STAmount amount;
335
336 auto const sleMpt =
337 view.read(keylet::mptoken(mptIssue.getMptID(), account));
338 if (!sleMpt)
339 amount.clear(mptIssue);
340 else if (
341 zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, mptIssue))
342 amount.clear(mptIssue);
343 else
344 {
345 amount = STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)};
346
347 // only if auth check is needed, as it needs to do an additional read
348 // operation
349 if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED)
350 {
351 auto const sleIssuance =
352 view.read(keylet::mptIssuance(mptIssue.getMptID()));
353
354 // if auth is enabled on the issuance and mpt is not authorized,
355 // clear amount
356 if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) &&
357 !sleMpt->isFlag(lsfMPTAuthorized))
358 amount.clear(mptIssue);
359 }
360 }
361
362 return amount;
363}
364
365STAmount
367 ReadView const& view,
368 AccountID const& id,
369 STAmount const& saDefault,
370 FreezeHandling freezeHandling,
372{
373 if (!saDefault.native() && saDefault.getIssuer() == id)
374 return saDefault;
375
376 return accountHolds(
377 view,
378 id,
379 saDefault.getCurrency(),
380 saDefault.getIssuer(),
381 freezeHandling,
382 j);
383}
384
385// Prevent ownerCount from wrapping under error conditions.
386//
387// adjustment allows the ownerCount to be adjusted up or down in multiple steps.
388// If id != std::nullopt, then do error reporting.
389//
390// Returns adjusted owner count.
391static std::uint32_t
394 std::int32_t adjustment,
395 std::optional<AccountID> const& id = std::nullopt,
397{
398 std::uint32_t adjusted{current + adjustment};
399 if (adjustment > 0)
400 {
401 // Overflow is well defined on unsigned
402 if (adjusted < current)
403 {
404 if (id)
405 {
406 JLOG(j.fatal())
407 << "Account " << *id << " owner count exceeds max!";
408 }
410 }
411 }
412 else
413 {
414 // Underflow is well defined on unsigned
415 if (adjusted > current)
416 {
417 if (id)
418 {
419 JLOG(j.fatal())
420 << "Account " << *id << " owner count set below 0!";
421 }
422 adjusted = 0;
423 XRPL_ASSERT(!id, "ripple::confineOwnerCount : id is not set");
424 }
425 }
426 return adjusted;
427}
428
429XRPAmount
431 ReadView const& view,
432 AccountID const& id,
433 std::int32_t ownerCountAdj,
435{
436 auto const sle = view.read(keylet::account(id));
437 if (sle == nullptr)
438 return beast::zero;
439
440 // Return balance minus reserve
441 std::uint32_t const ownerCount = confineOwnerCount(
442 view.ownerCountHook(id, sle->getFieldU32(sfOwnerCount)), ownerCountAdj);
443
444 // AMMs have no reserve requirement
445 auto const reserve = sle->isFieldPresent(sfAMMID)
446 ? XRPAmount{0}
447 : view.fees().accountReserve(ownerCount);
448
449 auto const fullBalance = sle->getFieldAmount(sfBalance);
450
451 auto const balance = view.balanceHook(id, xrpAccount(), fullBalance);
452
453 STAmount const amount =
454 (balance < reserve) ? STAmount{0} : balance - reserve;
455
456 JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id)
457 << " amount=" << amount.getFullText()
458 << " fullBalance=" << fullBalance.getFullText()
459 << " balance=" << balance.getFullText()
460 << " reserve=" << reserve << " ownerCount=" << ownerCount
461 << " ownerCountAdj=" << ownerCountAdj;
462
463 return amount.xrp();
464}
465
466void
468 ReadView const& view,
469 Keylet const& root,
470 std::function<void(std::shared_ptr<SLE const> const&)> const& f)
471{
472 XRPL_ASSERT(
473 root.type == ltDIR_NODE, "ripple::forEachItem : valid root type");
474
475 if (root.type != ltDIR_NODE)
476 return;
477
478 auto pos = root;
479
480 while (true)
481 {
482 auto sle = view.read(pos);
483 if (!sle)
484 return;
485 for (auto const& key : sle->getFieldV256(sfIndexes))
486 f(view.read(keylet::child(key)));
487 auto const next = sle->getFieldU64(sfIndexNext);
488 if (!next)
489 return;
490 pos = keylet::page(root, next);
491 }
492}
493
494bool
496 ReadView const& view,
497 Keylet const& root,
498 uint256 const& after,
499 std::uint64_t const hint,
500 unsigned int limit,
501 std::function<bool(std::shared_ptr<SLE const> const&)> const& f)
502{
503 XRPL_ASSERT(
504 root.type == ltDIR_NODE, "ripple::forEachItemAfter : valid root type");
505
506 if (root.type != ltDIR_NODE)
507 return false;
508
509 auto currentIndex = root;
510
511 // If startAfter is not zero try jumping to that page using the hint
512 if (after.isNonZero())
513 {
514 auto const hintIndex = keylet::page(root, hint);
515
516 if (auto hintDir = view.read(hintIndex))
517 {
518 for (auto const& key : hintDir->getFieldV256(sfIndexes))
519 {
520 if (key == after)
521 {
522 // We found the hint, we can start here
523 currentIndex = hintIndex;
524 break;
525 }
526 }
527 }
528
529 bool found = false;
530 for (;;)
531 {
532 auto const ownerDir = view.read(currentIndex);
533 if (!ownerDir)
534 return found;
535 for (auto const& key : ownerDir->getFieldV256(sfIndexes))
536 {
537 if (!found)
538 {
539 if (key == after)
540 found = true;
541 }
542 else if (f(view.read(keylet::child(key))) && limit-- <= 1)
543 {
544 return found;
545 }
546 }
547
548 auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
549 if (uNodeNext == 0)
550 return found;
551 currentIndex = keylet::page(root, uNodeNext);
552 }
553 }
554 else
555 {
556 for (;;)
557 {
558 auto const ownerDir = view.read(currentIndex);
559 if (!ownerDir)
560 return true;
561 for (auto const& key : ownerDir->getFieldV256(sfIndexes))
562 if (f(view.read(keylet::child(key))) && limit-- <= 1)
563 return true;
564 auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
565 if (uNodeNext == 0)
566 return true;
567 currentIndex = keylet::page(root, uNodeNext);
568 }
569 }
570}
571
572Rate
573transferRate(ReadView const& view, AccountID const& issuer)
574{
575 auto const sle = view.read(keylet::account(issuer));
576
577 if (sle && sle->isFieldPresent(sfTransferRate))
578 return Rate{sle->getFieldU32(sfTransferRate)};
579
580 return parityRate;
581}
582
583Rate
584transferRate(ReadView const& view, MPTID const& issuanceID)
585{
586 // fee is 0-50,000 (0-50%), rate is 1,000,000,000-2,000,000,000
587 // For example, if transfer fee is 50% then 10,000 * 50,000 = 500,000
588 // which represents 50% of 1,000,000,000
589 if (auto const sle = view.read(keylet::mptIssuance(issuanceID));
590 sle && sle->isFieldPresent(sfTransferFee))
591 return Rate{1'000'000'000u + 10'000 * sle->getFieldU16(sfTransferFee)};
592
593 return parityRate;
594}
595
596bool
598 ReadView const& validLedger,
599 ReadView const& testLedger,
601 const char* reason)
602{
603 bool ret = true;
604
605 if (validLedger.info().seq < testLedger.info().seq)
606 {
607 // valid -> ... -> test
608 auto hash = hashOfSeq(
609 testLedger,
610 validLedger.info().seq,
611 beast::Journal{beast::Journal::getNullSink()});
612 if (hash && (*hash != validLedger.info().hash))
613 {
614 JLOG(s) << reason << " incompatible with valid ledger";
615
616 JLOG(s) << "Hash(VSeq): " << to_string(*hash);
617
618 ret = false;
619 }
620 }
621 else if (validLedger.info().seq > testLedger.info().seq)
622 {
623 // test -> ... -> valid
624 auto hash = hashOfSeq(
625 validLedger,
626 testLedger.info().seq,
627 beast::Journal{beast::Journal::getNullSink()});
628 if (hash && (*hash != testLedger.info().hash))
629 {
630 JLOG(s) << reason << " incompatible preceding ledger";
631
632 JLOG(s) << "Hash(NSeq): " << to_string(*hash);
633
634 ret = false;
635 }
636 }
637 else if (
638 (validLedger.info().seq == testLedger.info().seq) &&
639 (validLedger.info().hash != testLedger.info().hash))
640 {
641 // Same sequence number, different hash
642 JLOG(s) << reason << " incompatible ledger";
643
644 ret = false;
645 }
646
647 if (!ret)
648 {
649 JLOG(s) << "Val: " << validLedger.info().seq << " "
650 << to_string(validLedger.info().hash);
651
652 JLOG(s) << "New: " << testLedger.info().seq << " "
653 << to_string(testLedger.info().hash);
654 }
655
656 return ret;
657}
658
659bool
661 uint256 const& validHash,
662 LedgerIndex validIndex,
663 ReadView const& testLedger,
665 const char* reason)
666{
667 bool ret = true;
668
669 if (testLedger.info().seq > validIndex)
670 {
671 // Ledger we are testing follows last valid ledger
672 auto hash = hashOfSeq(
673 testLedger,
674 validIndex,
676 if (hash && (*hash != validHash))
677 {
678 JLOG(s) << reason << " incompatible following ledger";
679 JLOG(s) << "Hash(VSeq): " << to_string(*hash);
680
681 ret = false;
682 }
683 }
684 else if (
685 (validIndex == testLedger.info().seq) &&
686 (testLedger.info().hash != validHash))
687 {
688 JLOG(s) << reason << " incompatible ledger";
689
690 ret = false;
691 }
692
693 if (!ret)
694 {
695 JLOG(s) << "Val: " << validIndex << " " << to_string(validHash);
696
697 JLOG(s) << "New: " << testLedger.info().seq << " "
698 << to_string(testLedger.info().hash);
699 }
700
701 return ret;
702}
703
704bool
705dirIsEmpty(ReadView const& view, Keylet const& k)
706{
707 auto const sleNode = view.read(k);
708 if (!sleNode)
709 return true;
710 if (!sleNode->getFieldV256(sfIndexes).empty())
711 return false;
712 // The first page of a directory may legitimately be empty even if there
713 // are other pages (the first page is the anchor page) so check to see if
714 // there is another page. If there is, the directory isn't empty.
715 return sleNode->getFieldU64(sfIndexNext) == 0;
716}
717
720{
721 std::set<uint256> amendments;
722
723 if (auto const sle = view.read(keylet::amendments()))
724 {
725 if (sle->isFieldPresent(sfAmendments))
726 {
727 auto const& v = sle->getFieldV256(sfAmendments);
728 amendments.insert(v.begin(), v.end());
729 }
730 }
731
732 return amendments;
733}
734
737{
739
740 if (auto const sle = view.read(keylet::amendments()))
741 {
742 if (sle->isFieldPresent(sfMajorities))
743 {
744 using tp = NetClock::time_point;
745 using d = tp::duration;
746
747 auto const majorities = sle->getFieldArray(sfMajorities);
748
749 for (auto const& m : majorities)
750 ret[m.getFieldH256(sfAmendment)] =
751 tp(d(m.getFieldU32(sfCloseTime)));
752 }
753 }
754
755 return ret;
756}
757
759hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
760{
761 // Easy cases...
762 if (seq > ledger.seq())
763 {
764 JLOG(journal.warn())
765 << "Can't get seq " << seq << " from " << ledger.seq() << " future";
766 return std::nullopt;
767 }
768 if (seq == ledger.seq())
769 return ledger.info().hash;
770 if (seq == (ledger.seq() - 1))
771 return ledger.info().parentHash;
772
773 if (int diff = ledger.seq() - seq; diff <= 256)
774 {
775 // Within 256...
776 auto const hashIndex = ledger.read(keylet::skip());
777 if (hashIndex)
778 {
779 XRPL_ASSERT(
780 hashIndex->getFieldU32(sfLastLedgerSequence) ==
781 (ledger.seq() - 1),
782 "ripple::hashOfSeq : matching ledger sequence");
783 STVector256 vec = hashIndex->getFieldV256(sfHashes);
784 if (vec.size() >= diff)
785 return vec[vec.size() - diff];
786 JLOG(journal.warn())
787 << "Ledger " << ledger.seq() << " missing hash for " << seq
788 << " (" << vec.size() << "," << diff << ")";
789 }
790 else
791 {
792 JLOG(journal.warn())
793 << "Ledger " << ledger.seq() << ":" << ledger.info().hash
794 << " missing normal list";
795 }
796 }
797
798 if ((seq & 0xff) != 0)
799 {
800 JLOG(journal.debug())
801 << "Can't get seq " << seq << " from " << ledger.seq() << " past";
802 return std::nullopt;
803 }
804
805 // in skiplist
806 auto const hashIndex = ledger.read(keylet::skip(seq));
807 if (hashIndex)
808 {
809 auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
810 XRPL_ASSERT(lastSeq >= seq, "ripple::hashOfSeq : minimum last ledger");
811 XRPL_ASSERT(
812 (lastSeq & 0xff) == 0, "ripple::hashOfSeq : valid last ledger");
813 auto const diff = (lastSeq - seq) >> 8;
814 STVector256 vec = hashIndex->getFieldV256(sfHashes);
815 if (vec.size() > diff)
816 return vec[vec.size() - diff - 1];
817 }
818 JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq()
819 << " error";
820 return std::nullopt;
821}
822
823//------------------------------------------------------------------------------
824//
825// Modifiers
826//
827//------------------------------------------------------------------------------
828
829void
831 ApplyView& view,
832 std::shared_ptr<SLE> const& sle,
833 std::int32_t amount,
835{
836 if (!sle)
837 return;
838 XRPL_ASSERT(amount, "ripple::adjustOwnerCount : nonzero amount input");
839 std::uint32_t const current{sle->getFieldU32(sfOwnerCount)};
840 AccountID const id = (*sle)[sfAccount];
841 std::uint32_t const adjusted = confineOwnerCount(current, amount, id, j);
842 view.adjustOwnerCountHook(id, current, adjusted);
843 sle->setFieldU32(sfOwnerCount, adjusted);
844 view.update(sle);
845}
846
849{
850 return [&account](std::shared_ptr<SLE> const& sle) {
851 (*sle)[sfOwner] = account;
852 };
853}
854
855TER
857 ApplyView& view,
858 const bool bSrcHigh,
859 AccountID const& uSrcAccountID,
860 AccountID const& uDstAccountID,
861 uint256 const& uIndex, // --> ripple state entry
862 SLE::ref sleAccount, // --> the account being set.
863 const bool bAuth, // --> authorize account.
864 const bool bNoRipple, // --> others cannot ripple through
865 const bool bFreeze, // --> funds cannot leave
866 STAmount const& saBalance, // --> balance of account being set.
867 // Issuer should be noAccount()
868 STAmount const& saLimit, // --> limit for account being set.
869 // Issuer should be the account being set.
870 std::uint32_t uQualityIn,
871 std::uint32_t uQualityOut,
873{
874 JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", "
875 << to_string(uDstAccountID) << ", "
876 << saBalance.getFullText();
877
878 auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
879 auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
880
881 auto const sleRippleState = std::make_shared<SLE>(ltRIPPLE_STATE, uIndex);
882 view.insert(sleRippleState);
883
884 auto lowNode = view.dirInsert(
885 keylet::ownerDir(uLowAccountID),
886 sleRippleState->key(),
887 describeOwnerDir(uLowAccountID));
888
889 if (!lowNode)
890 return tecDIR_FULL;
891
892 auto highNode = view.dirInsert(
893 keylet::ownerDir(uHighAccountID),
894 sleRippleState->key(),
895 describeOwnerDir(uHighAccountID));
896
897 if (!highNode)
898 return tecDIR_FULL;
899
900 const bool bSetDst = saLimit.getIssuer() == uDstAccountID;
901 const bool bSetHigh = bSrcHigh ^ bSetDst;
902
903 XRPL_ASSERT(sleAccount, "ripple::trustCreate : non-null SLE");
904 if (!sleAccount)
905 return tefINTERNAL;
906
907 XRPL_ASSERT(
908 sleAccount->getAccountID(sfAccount) ==
909 (bSetHigh ? uHighAccountID : uLowAccountID),
910 "ripple::trustCreate : matching account ID");
911 auto const slePeer =
912 view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
913 if (!slePeer)
914 return tecNO_TARGET;
915
916 // Remember deletion hints.
917 sleRippleState->setFieldU64(sfLowNode, *lowNode);
918 sleRippleState->setFieldU64(sfHighNode, *highNode);
919
920 sleRippleState->setFieldAmount(
921 bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
922 sleRippleState->setFieldAmount(
923 bSetHigh ? sfLowLimit : sfHighLimit,
925 saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID}));
926
927 if (uQualityIn)
928 sleRippleState->setFieldU32(
929 bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
930
931 if (uQualityOut)
932 sleRippleState->setFieldU32(
933 bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
934
935 std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve;
936
937 if (bAuth)
938 {
939 uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth);
940 }
941 if (bNoRipple)
942 {
943 uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple);
944 }
945 if (bFreeze)
946 {
947 uFlags |= (!bSetHigh ? lsfLowFreeze : lsfHighFreeze);
948 }
949
950 if ((slePeer->getFlags() & lsfDefaultRipple) == 0)
951 {
952 // The other side's default is no rippling
953 uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple);
954 }
955
956 sleRippleState->setFieldU32(sfFlags, uFlags);
957 adjustOwnerCount(view, sleAccount, 1, j);
958
959 // ONLY: Create ripple balance.
960 sleRippleState->setFieldAmount(
961 sfBalance, bSetHigh ? -saBalance : saBalance);
962
963 view.creditHook(
964 uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
965
966 return tesSUCCESS;
967}
968
969TER
971 ApplyView& view,
972 std::shared_ptr<SLE> const& sleRippleState,
973 AccountID const& uLowAccountID,
974 AccountID const& uHighAccountID,
976{
977 // Detect legacy dirs.
978 std::uint64_t uLowNode = sleRippleState->getFieldU64(sfLowNode);
979 std::uint64_t uHighNode = sleRippleState->getFieldU64(sfHighNode);
980
981 JLOG(j.trace()) << "trustDelete: Deleting ripple line: low";
982
983 if (!view.dirRemove(
984 keylet::ownerDir(uLowAccountID),
985 uLowNode,
986 sleRippleState->key(),
987 false))
988 {
989 return tefBAD_LEDGER;
990 }
991
992 JLOG(j.trace()) << "trustDelete: Deleting ripple line: high";
993
994 if (!view.dirRemove(
995 keylet::ownerDir(uHighAccountID),
996 uHighNode,
997 sleRippleState->key(),
998 false))
999 {
1000 return tefBAD_LEDGER;
1001 }
1002
1003 JLOG(j.trace()) << "trustDelete: Deleting ripple line: state";
1004 view.erase(sleRippleState);
1005
1006 return tesSUCCESS;
1007}
1008
1009TER
1011{
1012 if (!sle)
1013 return tesSUCCESS;
1014 auto offerIndex = sle->key();
1015 auto owner = sle->getAccountID(sfAccount);
1016
1017 // Detect legacy directories.
1018 uint256 uDirectory = sle->getFieldH256(sfBookDirectory);
1019
1020 if (!view.dirRemove(
1021 keylet::ownerDir(owner),
1022 sle->getFieldU64(sfOwnerNode),
1023 offerIndex,
1024 false))
1025 {
1026 return tefBAD_LEDGER;
1027 }
1028
1029 if (!view.dirRemove(
1030 keylet::page(uDirectory),
1031 sle->getFieldU64(sfBookNode),
1032 offerIndex,
1033 false))
1034 {
1035 return tefBAD_LEDGER;
1036 }
1037
1038 adjustOwnerCount(view, view.peek(keylet::account(owner)), -1, j);
1039
1040 view.erase(sle);
1041
1042 return tesSUCCESS;
1043}
1044
1045// Direct send w/o fees:
1046// - Redeeming IOUs and/or sending sender's own IOUs.
1047// - Create trust line if needed.
1048// --> bCheckIssuer : normally require issuer to be involved.
1049static TER
1051 ApplyView& view,
1052 AccountID const& uSenderID,
1053 AccountID const& uReceiverID,
1054 STAmount const& saAmount,
1055 bool bCheckIssuer,
1057{
1058 AccountID const& issuer = saAmount.getIssuer();
1059 Currency const& currency = saAmount.getCurrency();
1060
1061 // Make sure issuer is involved.
1062 XRPL_ASSERT(
1063 !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer,
1064 "ripple::rippleCreditIOU : matching issuer or don't care");
1065 (void)issuer;
1066
1067 // Disallow sending to self.
1068 XRPL_ASSERT(
1069 uSenderID != uReceiverID,
1070 "ripple::rippleCreditIOU : sender is not receiver");
1071
1072 bool const bSenderHigh = uSenderID > uReceiverID;
1073 auto const index = keylet::line(uSenderID, uReceiverID, currency);
1074
1075 XRPL_ASSERT(
1076 !isXRP(uSenderID) && uSenderID != noAccount(),
1077 "ripple::rippleCreditIOU : sender is not XRP");
1078 XRPL_ASSERT(
1079 !isXRP(uReceiverID) && uReceiverID != noAccount(),
1080 "ripple::rippleCreditIOU : receiver is not XRP");
1081
1082 // If the line exists, modify it accordingly.
1083 if (auto const sleRippleState = view.peek(index))
1084 {
1085 STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
1086
1087 if (bSenderHigh)
1088 saBalance.negate(); // Put balance in sender terms.
1089
1090 view.creditHook(uSenderID, uReceiverID, saAmount, saBalance);
1091
1092 STAmount const saBefore = saBalance;
1093
1094 saBalance -= saAmount;
1095
1096 JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> "
1097 << to_string(uReceiverID)
1098 << " : before=" << saBefore.getFullText()
1099 << " amount=" << saAmount.getFullText()
1100 << " after=" << saBalance.getFullText();
1101
1102 std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
1103 bool bDelete = false;
1104
1105 // FIXME This NEEDS to be cleaned up and simplified. It's impossible
1106 // for anyone to understand.
1107 if (saBefore > beast::zero
1108 // Sender balance was positive.
1109 && saBalance <= beast::zero
1110 // Sender is zero or negative.
1111 && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
1112 // Sender reserve is set.
1113 &&
1114 static_cast<bool>(
1115 uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
1116 static_cast<bool>(
1117 view.read(keylet::account(uSenderID))->getFlags() &
1119 !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
1120 !sleRippleState->getFieldAmount(
1121 !bSenderHigh ? sfLowLimit : sfHighLimit)
1122 // Sender trust limit is 0.
1123 && !sleRippleState->getFieldU32(
1124 !bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
1125 // Sender quality in is 0.
1126 && !sleRippleState->getFieldU32(
1127 !bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
1128 // Sender quality out is 0.
1129 {
1130 // Clear the reserve of the sender, possibly delete the line!
1132 view, view.peek(keylet::account(uSenderID)), -1, j);
1133
1134 // Clear reserve flag.
1135 sleRippleState->setFieldU32(
1136 sfFlags,
1137 uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
1138
1139 // Balance is zero, receiver reserve is clear.
1140 bDelete = !saBalance // Balance is zero.
1141 && !(uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve));
1142 // Receiver reserve is clear.
1143 }
1144
1145 if (bSenderHigh)
1146 saBalance.negate();
1147
1148 // Want to reflect balance to zero even if we are deleting line.
1149 sleRippleState->setFieldAmount(sfBalance, saBalance);
1150 // ONLY: Adjust ripple balance.
1151
1152 if (bDelete)
1153 {
1154 return trustDelete(
1155 view,
1156 sleRippleState,
1157 bSenderHigh ? uReceiverID : uSenderID,
1158 !bSenderHigh ? uReceiverID : uSenderID,
1159 j);
1160 }
1161
1162 view.update(sleRippleState);
1163 return tesSUCCESS;
1164 }
1165
1166 STAmount const saReceiverLimit(Issue{currency, uReceiverID});
1167 STAmount saBalance{saAmount};
1168
1169 saBalance.setIssuer(noAccount());
1170
1171 JLOG(j.debug()) << "rippleCreditIOU: "
1172 "create line: "
1173 << to_string(uSenderID) << " -> " << to_string(uReceiverID)
1174 << " : " << saAmount.getFullText();
1175
1176 auto const sleAccount = view.peek(keylet::account(uReceiverID));
1177 if (!sleAccount)
1178 return tefINTERNAL;
1179
1180 bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
1181
1182 return trustCreate(
1183 view,
1184 bSenderHigh,
1185 uSenderID,
1186 uReceiverID,
1187 index.key,
1188 sleAccount,
1189 false,
1190 noRipple,
1191 false,
1192 saBalance,
1193 saReceiverLimit,
1194 0,
1195 0,
1196 j);
1197}
1198
1199// Send regardless of limits.
1200// --> saAmount: Amount/currency/issuer to deliver to receiver.
1201// <-- saActual: Amount actually cost. Sender pays fees.
1202static TER
1204 ApplyView& view,
1205 AccountID const& uSenderID,
1206 AccountID const& uReceiverID,
1207 STAmount const& saAmount,
1208 STAmount& saActual,
1210 WaiveTransferFee waiveFee)
1211{
1212 auto const issuer = saAmount.getIssuer();
1213
1214 XRPL_ASSERT(
1215 !isXRP(uSenderID) && !isXRP(uReceiverID),
1216 "ripple::rippleSendIOU : neither sender nor receiver is XRP");
1217 XRPL_ASSERT(
1218 uSenderID != uReceiverID,
1219 "ripple::rippleSendIOU : sender is not receiver");
1220
1221 if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount())
1222 {
1223 // Direct send: redeeming IOUs and/or sending own IOUs.
1224 auto const ter =
1225 rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j);
1226 if (view.rules().enabled(featureDeletableAccounts) && ter != tesSUCCESS)
1227 return ter;
1228 saActual = saAmount;
1229 return tesSUCCESS;
1230 }
1231
1232 // Sending 3rd party IOUs: transit.
1233
1234 // Calculate the amount to transfer accounting
1235 // for any transfer fees if the fee is not waived:
1236 saActual = (waiveFee == WaiveTransferFee::Yes)
1237 ? saAmount
1238 : multiply(saAmount, transferRate(view, issuer));
1239
1240 JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > "
1241 << to_string(uReceiverID)
1242 << " : deliver=" << saAmount.getFullText()
1243 << " cost=" << saActual.getFullText();
1244
1245 TER terResult =
1246 rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j);
1247
1248 if (tesSUCCESS == terResult)
1249 terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j);
1250
1251 return terResult;
1252}
1253
1254static TER
1256 ApplyView& view,
1257 AccountID const& uSenderID,
1258 AccountID const& uReceiverID,
1259 STAmount const& saAmount,
1261 WaiveTransferFee waiveFee)
1262{
1263 if (view.rules().enabled(fixAMMv1_1))
1264 {
1265 if (saAmount < beast::zero || saAmount.holds<MPTIssue>())
1266 {
1267 return tecINTERNAL;
1268 }
1269 }
1270 else
1271 {
1272 XRPL_ASSERT(
1273 saAmount >= beast::zero && !saAmount.holds<MPTIssue>(),
1274 "ripple::accountSendIOU : minimum amount and not MPT");
1275 }
1276
1277 /* If we aren't sending anything or if the sender is the same as the
1278 * receiver then we don't need to do anything.
1279 */
1280 if (!saAmount || (uSenderID == uReceiverID))
1281 return tesSUCCESS;
1282
1283 if (!saAmount.native())
1284 {
1285 STAmount saActual;
1286
1287 JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> "
1288 << to_string(uReceiverID) << " : "
1289 << saAmount.getFullText();
1290
1291 return rippleSendIOU(
1292 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
1293 }
1294
1295 /* XRP send which does not check reserve and can do pure adjustment.
1296 * Note that sender or receiver may be null and this not a mistake; this
1297 * setup is used during pathfinding and it is carefully controlled to
1298 * ensure that transfers are balanced.
1299 */
1300 TER terResult(tesSUCCESS);
1301
1302 SLE::pointer sender = uSenderID != beast::zero
1303 ? view.peek(keylet::account(uSenderID))
1304 : SLE::pointer();
1305 SLE::pointer receiver = uReceiverID != beast::zero
1306 ? view.peek(keylet::account(uReceiverID))
1307 : SLE::pointer();
1308
1309 if (auto stream = j.trace())
1310 {
1311 std::string sender_bal("-");
1312 std::string receiver_bal("-");
1313
1314 if (sender)
1315 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
1316
1317 if (receiver)
1318 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
1319
1320 stream << "accountSendIOU> " << to_string(uSenderID) << " ("
1321 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
1322 << receiver_bal << ") : " << saAmount.getFullText();
1323 }
1324
1325 if (sender)
1326 {
1327 if (sender->getFieldAmount(sfBalance) < saAmount)
1328 {
1329 // VFALCO Its laborious to have to mutate the
1330 // TER based on params everywhere
1331 terResult = view.open() ? TER{telFAILED_PROCESSING}
1333 }
1334 else
1335 {
1336 auto const sndBal = sender->getFieldAmount(sfBalance);
1337 view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal);
1338
1339 // Decrement XRP balance.
1340 sender->setFieldAmount(sfBalance, sndBal - saAmount);
1341 view.update(sender);
1342 }
1343 }
1344
1345 if (tesSUCCESS == terResult && receiver)
1346 {
1347 // Increment XRP balance.
1348 auto const rcvBal = receiver->getFieldAmount(sfBalance);
1349 receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
1350 view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal);
1351
1352 view.update(receiver);
1353 }
1354
1355 if (auto stream = j.trace())
1356 {
1357 std::string sender_bal("-");
1358 std::string receiver_bal("-");
1359
1360 if (sender)
1361 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
1362
1363 if (receiver)
1364 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
1365
1366 stream << "accountSendIOU< " << to_string(uSenderID) << " ("
1367 << sender_bal << ") -> " << to_string(uReceiverID) << " ("
1368 << receiver_bal << ") : " << saAmount.getFullText();
1369 }
1370
1371 return terResult;
1372}
1373
1374static TER
1376 ApplyView& view,
1377 AccountID const& uSenderID,
1378 AccountID const& uReceiverID,
1379 STAmount const& saAmount,
1381{
1382 auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
1383 auto const issuer = saAmount.getIssuer();
1384 auto sleIssuance = view.peek(mptID);
1385 if (!sleIssuance)
1386 return tecOBJECT_NOT_FOUND;
1387 if (uSenderID == issuer)
1388 {
1389 (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
1390 view.update(sleIssuance);
1391 }
1392 else
1393 {
1394 auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
1395 if (auto sle = view.peek(mptokenID))
1396 {
1397 auto const amt = sle->getFieldU64(sfMPTAmount);
1398 auto const pay = saAmount.mpt().value();
1399 if (amt < pay)
1400 return tecINSUFFICIENT_FUNDS;
1401 (*sle)[sfMPTAmount] = amt - pay;
1402 view.update(sle);
1403 }
1404 else
1405 return tecNO_AUTH;
1406 }
1407
1408 if (uReceiverID == issuer)
1409 {
1410 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
1411 auto const redeem = saAmount.mpt().value();
1412 if (outstanding >= redeem)
1413 {
1414 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
1415 view.update(sleIssuance);
1416 }
1417 else
1418 return tecINTERNAL;
1419 }
1420 else
1421 {
1422 auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
1423 if (auto sle = view.peek(mptokenID))
1424 {
1425 (*sle)[sfMPTAmount] += saAmount.mpt().value();
1426 view.update(sle);
1427 }
1428 else
1429 return tecNO_AUTH;
1430 }
1431 return tesSUCCESS;
1432}
1433
1434static TER
1436 ApplyView& view,
1437 AccountID const& uSenderID,
1438 AccountID const& uReceiverID,
1439 STAmount const& saAmount,
1440 STAmount& saActual,
1442 WaiveTransferFee waiveFee)
1443{
1444 XRPL_ASSERT(
1445 uSenderID != uReceiverID,
1446 "ripple::rippleSendMPT : sender is not receiver");
1447
1448 // Safe to get MPT since rippleSendMPT is only called by accountSendMPT
1449 auto const issuer = saAmount.getIssuer();
1450
1451 auto const sle =
1452 view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
1453 if (!sle)
1454 return tecOBJECT_NOT_FOUND;
1455
1456 if (uSenderID == issuer || uReceiverID == issuer)
1457 {
1458 // if sender is issuer, check that the new OutstandingAmount will not
1459 // exceed MaximumAmount
1460 if (uSenderID == issuer)
1461 {
1462 auto const sendAmount = saAmount.mpt().value();
1463 auto const maximumAmount =
1464 sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
1465 if (sendAmount > maximumAmount ||
1466 sle->getFieldU64(sfOutstandingAmount) >
1467 maximumAmount - sendAmount)
1468 return tecPATH_DRY;
1469 }
1470
1471 // Direct send: redeeming MPTs and/or sending own MPTs.
1472 auto const ter =
1473 rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
1474 if (ter != tesSUCCESS)
1475 return ter;
1476 saActual = saAmount;
1477 return tesSUCCESS;
1478 }
1479
1480 // Sending 3rd party MPTs: transit.
1481 saActual = (waiveFee == WaiveTransferFee::Yes)
1482 ? saAmount
1483 : multiply(
1484 saAmount,
1485 transferRate(view, saAmount.get<MPTIssue>().getMptID()));
1486
1487 JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > "
1488 << to_string(uReceiverID)
1489 << " : deliver=" << saAmount.getFullText()
1490 << " cost=" << saActual.getFullText();
1491
1492 if (auto const terResult =
1493 rippleCreditMPT(view, issuer, uReceiverID, saAmount, j);
1494 terResult != tesSUCCESS)
1495 return terResult;
1496
1497 return rippleCreditMPT(view, uSenderID, issuer, saActual, j);
1498}
1499
1500static TER
1502 ApplyView& view,
1503 AccountID const& uSenderID,
1504 AccountID const& uReceiverID,
1505 STAmount const& saAmount,
1507 WaiveTransferFee waiveFee)
1508{
1509 XRPL_ASSERT(
1510 saAmount >= beast::zero && saAmount.holds<MPTIssue>(),
1511 "ripple::accountSendMPT : minimum amount and MPT");
1512
1513 /* If we aren't sending anything or if the sender is the same as the
1514 * receiver then we don't need to do anything.
1515 */
1516 if (!saAmount || (uSenderID == uReceiverID))
1517 return tesSUCCESS;
1518
1519 STAmount saActual{saAmount.asset()};
1520
1521 return rippleSendMPT(
1522 view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
1523}
1524
1525TER
1527 ApplyView& view,
1528 AccountID const& uSenderID,
1529 AccountID const& uReceiverID,
1530 STAmount const& saAmount,
1532 WaiveTransferFee waiveFee)
1533{
1534 return std::visit(
1535 [&]<ValidIssueType TIss>(TIss const& issue) {
1536 if constexpr (std::is_same_v<TIss, Issue>)
1537 return accountSendIOU(
1538 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
1539 else
1540 return accountSendMPT(
1541 view, uSenderID, uReceiverID, saAmount, j, waiveFee);
1542 },
1543 saAmount.asset().value());
1544}
1545
1546static bool
1548 ApplyView& view,
1549 SLE::pointer state,
1550 bool bSenderHigh,
1551 AccountID const& sender,
1552 STAmount const& before,
1553 STAmount const& after,
1555{
1556 if (!state)
1557 return false;
1558 std::uint32_t const flags(state->getFieldU32(sfFlags));
1559
1560 auto sle = view.peek(keylet::account(sender));
1561 if (!sle)
1562 return false;
1563
1564 // YYY Could skip this if rippling in reverse.
1565 if (before > beast::zero
1566 // Sender balance was positive.
1567 && after <= beast::zero
1568 // Sender is zero or negative.
1569 && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve))
1570 // Sender reserve is set.
1571 && static_cast<bool>(
1572 flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
1573 static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
1574 !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) &&
1575 !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
1576 // Sender trust limit is 0.
1577 && !state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn)
1578 // Sender quality in is 0.
1579 &&
1580 !state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut))
1581 // Sender quality out is 0.
1582 {
1583 // VFALCO Where is the line being deleted?
1584 // Clear the reserve of the sender, possibly delete the line!
1585 adjustOwnerCount(view, sle, -1, j);
1586
1587 // Clear reserve flag.
1588 state->setFieldU32(
1589 sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
1590
1591 // Balance is zero, receiver reserve is clear.
1592 if (!after // Balance is zero.
1593 && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)))
1594 return true;
1595 }
1596 return false;
1597}
1598
1599TER
1601 ApplyView& view,
1602 AccountID const& account,
1603 STAmount const& amount,
1604 Issue const& issue,
1606{
1607 XRPL_ASSERT(
1608 !isXRP(account) && !isXRP(issue.account),
1609 "ripple::issueIOU : neither account nor issuer is XRP");
1610
1611 // Consistency check
1612 XRPL_ASSERT(issue == amount.issue(), "ripple::issueIOU : matching issue");
1613
1614 // Can't send to self!
1615 XRPL_ASSERT(
1616 issue.account != account, "ripple::issueIOU : not issuer account");
1617
1618 JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": "
1619 << amount.getFullText();
1620
1621 bool bSenderHigh = issue.account > account;
1622
1623 auto const index = keylet::line(issue.account, account, issue.currency);
1624
1625 if (auto state = view.peek(index))
1626 {
1627 STAmount final_balance = state->getFieldAmount(sfBalance);
1628
1629 if (bSenderHigh)
1630 final_balance.negate(); // Put balance in sender terms.
1631
1632 STAmount const start_balance = final_balance;
1633
1634 final_balance -= amount;
1635
1636 auto const must_delete = updateTrustLine(
1637 view,
1638 state,
1639 bSenderHigh,
1640 issue.account,
1641 start_balance,
1642 final_balance,
1643 j);
1644
1645 view.creditHook(issue.account, account, amount, start_balance);
1646
1647 if (bSenderHigh)
1648 final_balance.negate();
1649
1650 // Adjust the balance on the trust line if necessary. We do this even if
1651 // we are going to delete the line to reflect the correct balance at the
1652 // time of deletion.
1653 state->setFieldAmount(sfBalance, final_balance);
1654 if (must_delete)
1655 return trustDelete(
1656 view,
1657 state,
1658 bSenderHigh ? account : issue.account,
1659 bSenderHigh ? issue.account : account,
1660 j);
1661
1662 view.update(state);
1663
1664 return tesSUCCESS;
1665 }
1666
1667 // NIKB TODO: The limit uses the receiver's account as the issuer and
1668 // this is unnecessarily inefficient as copying which could be avoided
1669 // is now required. Consider available options.
1670 STAmount const limit(Issue{issue.currency, account});
1671 STAmount final_balance = amount;
1672
1673 final_balance.setIssuer(noAccount());
1674
1675 auto const receiverAccount = view.peek(keylet::account(account));
1676 if (!receiverAccount)
1677 return tefINTERNAL;
1678
1679 bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0;
1680
1681 return trustCreate(
1682 view,
1683 bSenderHigh,
1684 issue.account,
1685 account,
1686 index.key,
1687 receiverAccount,
1688 false,
1689 noRipple,
1690 false,
1691 final_balance,
1692 limit,
1693 0,
1694 0,
1695 j);
1696}
1697
1698TER
1700 ApplyView& view,
1701 AccountID const& account,
1702 STAmount const& amount,
1703 Issue const& issue,
1705{
1706 XRPL_ASSERT(
1707 !isXRP(account) && !isXRP(issue.account),
1708 "ripple::redeemIOU : neither account nor issuer is XRP");
1709
1710 // Consistency check
1711 XRPL_ASSERT(issue == amount.issue(), "ripple::redeemIOU : matching issue");
1712
1713 // Can't send to self!
1714 XRPL_ASSERT(
1715 issue.account != account, "ripple::redeemIOU : not issuer account");
1716
1717 JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": "
1718 << amount.getFullText();
1719
1720 bool bSenderHigh = account > issue.account;
1721
1722 if (auto state =
1723 view.peek(keylet::line(account, issue.account, issue.currency)))
1724 {
1725 STAmount final_balance = state->getFieldAmount(sfBalance);
1726
1727 if (bSenderHigh)
1728 final_balance.negate(); // Put balance in sender terms.
1729
1730 STAmount const start_balance = final_balance;
1731
1732 final_balance -= amount;
1733
1734 auto const must_delete = updateTrustLine(
1735 view, state, bSenderHigh, account, start_balance, final_balance, j);
1736
1737 view.creditHook(account, issue.account, amount, start_balance);
1738
1739 if (bSenderHigh)
1740 final_balance.negate();
1741
1742 // Adjust the balance on the trust line if necessary. We do this even if
1743 // we are going to delete the line to reflect the correct balance at the
1744 // time of deletion.
1745 state->setFieldAmount(sfBalance, final_balance);
1746
1747 if (must_delete)
1748 {
1749 return trustDelete(
1750 view,
1751 state,
1752 bSenderHigh ? issue.account : account,
1753 bSenderHigh ? account : issue.account,
1754 j);
1755 }
1756
1757 view.update(state);
1758 return tesSUCCESS;
1759 }
1760
1761 // In order to hold an IOU, a trust line *MUST* exist to track the
1762 // balance. If it doesn't, then something is very wrong. Don't try
1763 // to continue.
1764 JLOG(j.fatal()) << "redeemIOU: " << to_string(account)
1765 << " attempts to redeem " << amount.getFullText()
1766 << " but no trust line exists!";
1767
1768 return tefINTERNAL;
1769}
1770
1771TER
1773 ApplyView& view,
1774 AccountID const& from,
1775 AccountID const& to,
1776 STAmount const& amount,
1778{
1779 XRPL_ASSERT(
1780 from != beast::zero, "ripple::transferXRP : nonzero from account");
1781 XRPL_ASSERT(to != beast::zero, "ripple::transferXRP : nonzero to account");
1782 XRPL_ASSERT(from != to, "ripple::transferXRP : sender is not receiver");
1783 XRPL_ASSERT(amount.native(), "ripple::transferXRP : amount is XRP");
1784
1785 SLE::pointer const sender = view.peek(keylet::account(from));
1786 SLE::pointer const receiver = view.peek(keylet::account(to));
1787 if (!sender || !receiver)
1788 return tefINTERNAL;
1789
1790 JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> "
1791 << to_string(to) << ") : " << amount.getFullText();
1792
1793 if (sender->getFieldAmount(sfBalance) < amount)
1794 {
1795 // VFALCO Its unfortunate we have to keep
1796 // mutating these TER everywhere
1797 // FIXME: this logic should be moved to callers maybe?
1798 return view.open() ? TER{telFAILED_PROCESSING}
1800 }
1801
1802 // Decrement XRP balance.
1803 sender->setFieldAmount(
1804 sfBalance, sender->getFieldAmount(sfBalance) - amount);
1805 view.update(sender);
1806
1807 receiver->setFieldAmount(
1808 sfBalance, receiver->getFieldAmount(sfBalance) + amount);
1809 view.update(receiver);
1810
1811 return tesSUCCESS;
1812}
1813
1814TER
1815requireAuth(ReadView const& view, Issue const& issue, AccountID const& account)
1816{
1817 if (isXRP(issue) || issue.account == account)
1818 return tesSUCCESS;
1819 if (auto const issuerAccount = view.read(keylet::account(issue.account));
1820 issuerAccount && (*issuerAccount)[sfFlags] & lsfRequireAuth)
1821 {
1822 if (auto const trustLine =
1823 view.read(keylet::line(account, issue.account, issue.currency)))
1824 return ((*trustLine)[sfFlags] &
1825 ((account > issue.account) ? lsfLowAuth : lsfHighAuth))
1826 ? tesSUCCESS
1827 : TER{tecNO_AUTH};
1828 return TER{tecNO_LINE};
1829 }
1830
1831 return tesSUCCESS;
1832}
1833
1834TER
1836 ReadView const& view,
1837 MPTIssue const& mptIssue,
1838 AccountID const& account)
1839{
1840 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
1841 auto const sleIssuance = view.read(mptID);
1842
1843 if (!sleIssuance)
1844 return tecOBJECT_NOT_FOUND;
1845
1846 auto const mptIssuer = sleIssuance->getAccountID(sfIssuer);
1847
1848 // issuer is always "authorized"
1849 if (mptIssuer == account)
1850 return tesSUCCESS;
1851
1852 auto const mptokenID = keylet::mptoken(mptID.key, account);
1853 auto const sleToken = view.read(mptokenID);
1854
1855 // if account has no MPToken, fail
1856 if (!sleToken)
1857 return tecNO_AUTH;
1858
1859 // mptoken must be authorized if issuance enabled requireAuth
1860 if (sleIssuance->getFieldU32(sfFlags) & lsfMPTRequireAuth &&
1861 !(sleToken->getFlags() & lsfMPTAuthorized))
1862 return tecNO_AUTH;
1863
1864 return tesSUCCESS;
1865}
1866
1867TER
1869 ReadView const& view,
1870 MPTIssue const& mptIssue,
1871 AccountID const& from,
1872 AccountID const& to)
1873{
1874 auto const mptID = keylet::mptIssuance(mptIssue.getMptID());
1875 auto const sleIssuance = view.read(mptID);
1876 if (!sleIssuance)
1877 return tecOBJECT_NOT_FOUND;
1878
1879 if (!(sleIssuance->getFieldU32(sfFlags) & lsfMPTCanTransfer))
1880 {
1881 if (from != (*sleIssuance)[sfIssuer] && to != (*sleIssuance)[sfIssuer])
1882 return TER{tecNO_AUTH};
1883 }
1884 return tesSUCCESS;
1885}
1886
1887TER
1889 ApplyView& view,
1890 Keylet const& ownerDirKeylet,
1891 EntryDeleter const& deleter,
1893 std::optional<uint16_t> maxNodesToDelete)
1894{
1895 // Delete all the entries in the account directory.
1896 std::shared_ptr<SLE> sleDirNode{};
1897 unsigned int uDirEntry{0};
1898 uint256 dirEntry{beast::zero};
1899 std::uint32_t deleted = 0;
1900
1901 if (view.exists(ownerDirKeylet) &&
1902 dirFirst(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
1903 {
1904 do
1905 {
1906 if (maxNodesToDelete && ++deleted > *maxNodesToDelete)
1907 return tecINCOMPLETE;
1908
1909 // Choose the right way to delete each directory node.
1910 auto sleItem = view.peek(keylet::child(dirEntry));
1911 if (!sleItem)
1912 {
1913 // Directory node has an invalid index. Bail out.
1914 JLOG(j.fatal())
1915 << "DeleteAccount: Directory node in ledger " << view.seq()
1916 << " has index to object that is missing: "
1917 << to_string(dirEntry);
1918 return tefBAD_LEDGER;
1919 }
1920
1921 LedgerEntryType const nodeType{safe_cast<LedgerEntryType>(
1922 sleItem->getFieldU16(sfLedgerEntryType))};
1923
1924 // Deleter handles the details of specific account-owned object
1925 // deletion
1926 auto const [ter, skipEntry] = deleter(nodeType, dirEntry, sleItem);
1927 if (ter != tesSUCCESS)
1928 return ter;
1929
1930 // dirFirst() and dirNext() are like iterators with exposed
1931 // internal state. We'll take advantage of that exposed state
1932 // to solve a common C++ problem: iterator invalidation while
1933 // deleting elements from a container.
1934 //
1935 // We have just deleted one directory entry, which means our
1936 // "iterator state" is invalid.
1937 //
1938 // 1. During the process of getting an entry from the
1939 // directory uDirEntry was incremented from 'it' to 'it'+1.
1940 //
1941 // 2. We then deleted the entry at index 'it', which means the
1942 // entry that was at 'it'+1 has now moved to 'it'.
1943 //
1944 // 3. So we verify that uDirEntry is indeed 'it'+1. Then we jam it
1945 // back to 'it' to "un-invalidate" the iterator.
1946 XRPL_ASSERT(
1947 uDirEntry >= 1,
1948 "ripple::cleanupOnAccountDelete : minimum dir entries");
1949 if (uDirEntry == 0)
1950 {
1951 JLOG(j.error())
1952 << "DeleteAccount iterator re-validation failed.";
1953 return tefBAD_LEDGER;
1954 }
1955 if (skipEntry == SkipEntry::No)
1956 uDirEntry--;
1957
1958 } while (
1959 dirNext(view, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
1960 }
1961
1962 return tesSUCCESS;
1963}
1964
1965TER
1967 ApplyView& view,
1968 std::shared_ptr<SLE> sleState,
1971{
1972 if (!sleState || sleState->getType() != ltRIPPLE_STATE)
1973 return tecINTERNAL;
1974
1975 auto const& [low, high] = std::minmax(
1976 sleState->getFieldAmount(sfLowLimit).getIssuer(),
1977 sleState->getFieldAmount(sfHighLimit).getIssuer());
1978 auto sleLow = view.peek(keylet::account(low));
1979 auto sleHigh = view.peek(keylet::account(high));
1980 if (!sleLow || !sleHigh)
1981 return tecINTERNAL;
1982 bool const ammLow = sleLow->isFieldPresent(sfAMMID);
1983 bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
1984
1985 // can't both be AMM
1986 if (ammLow && ammHigh)
1987 return tecINTERNAL;
1988
1989 // at least one must be
1990 if (!ammLow && !ammHigh)
1991 return terNO_AMM;
1992
1993 // one must be the target amm
1994 if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
1995 return terNO_AMM;
1996
1997 if (auto const ter = trustDelete(view, sleState, low, high, j);
1998 ter != tesSUCCESS)
1999 {
2000 JLOG(j.error())
2001 << "deleteAMMTrustLine: failed to delete the trustline.";
2002 return ter;
2003 }
2004
2005 auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
2006 if (!(sleState->getFlags() & uFlags))
2007 return tecINTERNAL;
2008
2009 adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
2010
2011 return tesSUCCESS;
2012}
2013
2014TER
2016 ApplyView& view,
2017 AccountID const& uSenderID,
2018 AccountID const& uReceiverID,
2019 STAmount const& saAmount,
2020 bool bCheckIssuer,
2022{
2023 return std::visit(
2024 [&]<ValidIssueType TIss>(TIss const& issue) {
2025 if constexpr (std::is_same_v<TIss, Issue>)
2026 {
2027 return rippleCreditIOU(
2028 view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
2029 }
2030 else
2031 {
2032 XRPL_ASSERT(
2033 !bCheckIssuer,
2034 "ripple::rippleCredit : not checking issuer");
2035 return rippleCreditMPT(
2036 view, uSenderID, uReceiverID, saAmount, j);
2037 }
2038 },
2039 saAmount.asset().value());
2040}
2041
2042} // namespace ripple
Provide a light-weight way to check active() before string formatting.
Definition: Journal.h:194
A generic endpoint for log messages.
Definition: Journal.h:59
Stream fatal() const
Definition: Journal.h:341
Stream error() const
Definition: Journal.h:335
Stream debug() const
Definition: Journal.h:317
static Sink & getNullSink()
Returns a Sink which does nothing.
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
Stream warn() const
Definition: Journal.h:329
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:136
virtual void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
Definition: ApplyView.h:236
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:189
virtual void adjustOwnerCountHook(AccountID const &account, std::uint32_t cur, std::uint32_t next)
Definition: ApplyView.h:247
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:310
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:143
A currency issued by an account.
Definition: Issue.h:36
AccountID account
Definition: Issue.h:39
Currency currency
Definition: Issue.h:38
constexpr value_type value() const
Returns the underlying value.
Definition: MPTAmount.h:136
MPTID const & getMptID() const
Definition: MPTIssue.cpp:43
std::chrono::time_point< NetClock > time_point
Definition: chrono.h:70
std::chrono::duration< rep, period > duration
Definition: chrono.h:69
A view into a ledger.
Definition: ReadView.h:55
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:115
virtual std::uint32_t ownerCountHook(AccountID const &account, std::uint32_t count) const
Definition: ReadView.h:196
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
Definition: ReadView.h:182
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:122
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:122
constexpr bool holds() const noexcept
Definition: STAmount.h:456
Asset const & asset() const
Definition: STAmount.h:474
constexpr TIss const & get() const
void setIssuer(AccountID const &uIssuer)
Definition: STAmount.h:569
Currency const & getCurrency() const
Definition: STAmount.h:493
XRPAmount xrp() const
Definition: STAmount.cpp:273
AccountID const & getIssuer() const
Definition: STAmount.h:499
MPTAmount mpt() const
Definition: STAmount.cpp:303
Issue const & issue() const
Definition: STAmount.h:487
std::string getFullText() const override
Definition: STAmount.cpp:505
bool native() const noexcept
Definition: STAmount.h:449
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
Definition: STAmount.h:511
const std::shared_ptr< STLedgerEntry > & ref
Definition: STLedgerEntry.h:38
std::shared_ptr< STLedgerEntry > pointer
Definition: STLedgerEntry.h:37
AccountID getAccountID(SField const &field) const
Definition: STObject.cpp:621
std::uint32_t getFieldU32(SField const &field) const
Definition: STObject.cpp:585
void setFieldAmount(SField const &field, STAmount const &)
Definition: STObject.cpp:759
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:635
void setFieldU32(SField const &field, std::uint32_t)
Definition: STObject.cpp:711
std::size_t size() const
Definition: STVector256.h:168
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:91
bool internalDirNext(V &view, uint256 const &root, std::shared_ptr< N > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:43
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition: Indexes.cpp:508
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition: Indexes.cpp:166
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:220
Keylet const & amendments() noexcept
The index of the amendment table.
Definition: Indexes.cpp:190
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:494
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:160
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition: Indexes.cpp:356
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:350
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition: Indexes.cpp:172
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:177
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:1547
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition: View.cpp:366
FreezeHandling
Controls the treatment of frozen account balances.
Definition: View.h:80
@ fhZERO_IF_FROZEN
Definition: View.h:80
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:136
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
AccountID const & xrpAccount()
Compute AccountID from public key.
Definition: AccountID.cpp:170
@ telFAILED_PROCESSING
Definition: TER.h:56
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:204
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition: View.cpp:1966
static TER rippleSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1435
bool dirFirst(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:114
bool dirNext(ApplyView &view, uint256 const &root, std::shared_ptr< SLE > &page, unsigned int &index, uint256 &entry)
Definition: View.cpp:125
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account)
Check if the account lacks required authorization.
Definition: View.cpp:1815
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition: View.cpp:759
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition: View.cpp:573
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
Definition: Protocol.h:116
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:1699
@ lsfMPTCanTransfer
@ lsfDefaultRipple
@ lsfHighNoRipple
@ lsfRequireAuth
@ lsfHighFreeze
@ lsfLowNoRipple
@ lsfHighReserve
@ lsfMPTRequireAuth
@ lsfMPTAuthorized
@ lsfGlobalFreeze
@ lsfLowReserve
@ lsfLowFreeze
@ lsfMPTLocked
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:47
AuthHandling
Controls the treatment of unauthorized MPT balances.
Definition: View.h:83
@ ahZERO_IF_UNAUTHORIZED
Definition: View.h:83
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Definition: View.cpp:848
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
Definition: View.cpp:1772
@ 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:1010
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:238
TER trustCreate(ApplyView &view, const bool bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, const bool bAuth, const bool bNoRipple, const bool bFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uQualityIn, std::uint32_t uQualityOut, beast::Journal j)
Create a trust line.
Definition: View.cpp:856
std::set< uint256 > getEnabledAmendments(ReadView const &view)
Definition: View.cpp:719
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:1868
@ tefBAD_LEDGER
Definition: TER.h:170
@ tefINTERNAL
Definition: TER.h:173
AccountID ammAccountID(std::uint16_t prefix, uint256 const &parentHash, uint256 const &ammID)
Calculate AMM account ID.
Definition: AMMCore.cpp:30
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:392
static TER rippleCreditIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Definition: View.cpp:1050
static bool adjustOwnerCount(ApplyContext &ctx, int count)
Definition: SetOracle.cpp:186
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:1600
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1255
std::map< uint256, NetClock::time_point > majorityAmendments_t
Definition: View.h:288
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:147
WaiveTransferFee
Definition: View.h:46
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
Definition: View.cpp:970
@ tecOBJECT_NOT_FOUND
Definition: TER.h:313
@ tecNO_TARGET
Definition: TER.h:291
@ tecDIR_FULL
Definition: TER.h:274
@ tecINCOMPLETE
Definition: TER.h:322
@ tecINSUFFICIENT_FUNDS
Definition: TER.h:312
@ tecINTERNAL
Definition: TER.h:297
@ tecNO_LINE
Definition: TER.h:288
@ tecPATH_DRY
Definition: TER.h:281
@ tecFAILED_PROCESSING
Definition: TER.h:273
@ tecNO_AUTH
Definition: TER.h:287
@ tesSUCCESS
Definition: TER.h:242
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:271
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition: View.cpp:736
STLedgerEntry SLE
Definition: STLedgerEntry.h:97
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
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:495
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< uint16_t > maxNodesToDelete)
Definition: View.cpp:1888
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:467
Number root(Number f, unsigned d)
Definition: Number.cpp:630
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition: View.cpp:164
static TER rippleSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1203
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:2015
@ terNO_AMM
Definition: TER.h:227
TERSubset< CanCvtToTER > TER
Definition: TER.h:627
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, const char *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
Definition: View.cpp:597
static bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition: Escrow.cpp:89
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
Definition: View.cpp:1375
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
Definition: View.cpp:705
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:1526
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1501
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:430
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Definition: View.cpp:173
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:38
T visit(T... args)