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