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