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