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