rippled
Loading...
Searching...
No Matches
RippleStateHelpers.cpp
1#include <xrpl/ledger/helpers/RippleStateHelpers.h>
2//
3#include <xrpl/basics/Log.h>
4#include <xrpl/ledger/ApplyView.h>
5#include <xrpl/ledger/ReadView.h>
6#include <xrpl/ledger/helpers/AccountRootHelpers.h>
7#include <xrpl/ledger/helpers/DirectoryHelpers.h>
8#include <xrpl/protocol/AmountConversions.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/LedgerFormats.h>
12#include <xrpl/protocol/Rules.h>
13
14namespace xrpl {
15
16//------------------------------------------------------------------------------
17//
18// Credit functions (from Credit.cpp)
19//
20//------------------------------------------------------------------------------
21
22STAmount
24 ReadView const& view,
25 AccountID const& account,
26 AccountID const& issuer,
27 Currency const& currency)
28{
29 STAmount result(Issue{currency, account});
30
31 auto sleRippleState = view.read(keylet::line(account, issuer, currency));
32
33 if (sleRippleState)
34 {
35 result = sleRippleState->getFieldAmount(account < issuer ? sfLowLimit : sfHighLimit);
36 result.setIssuer(account);
37 }
38
39 XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditLimit : result issuer match");
40 XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditLimit : result currency match");
41 return result;
42}
43
44IOUAmount
45creditLimit2(ReadView const& v, AccountID const& acc, AccountID const& iss, Currency const& cur)
46{
47 return toAmount<IOUAmount>(creditLimit(v, acc, iss, cur));
48}
49
50STAmount
52 ReadView const& view,
53 AccountID const& account,
54 AccountID const& issuer,
55 Currency const& currency)
56{
57 STAmount result(Issue{currency, account});
58
59 auto sleRippleState = view.read(keylet::line(account, issuer, currency));
60
61 if (sleRippleState)
62 {
63 result = sleRippleState->getFieldAmount(sfBalance);
64 if (account < issuer)
65 result.negate();
66 result.setIssuer(account);
67 }
68
69 XRPL_ASSERT(result.getIssuer() == account, "xrpl::creditBalance : result issuer match");
70 XRPL_ASSERT(result.getCurrency() == currency, "xrpl::creditBalance : result currency match");
71 return result;
72}
73
74//------------------------------------------------------------------------------
75//
76// Freeze checking (IOU-specific)
77//
78//------------------------------------------------------------------------------
79
80bool
82 ReadView const& view,
83 AccountID const& account,
84 Currency const& currency,
85 AccountID const& issuer)
86{
87 if (isXRP(currency))
88 return false;
89 if (issuer != account)
90 {
91 // Check if the issuer froze the line
92 auto const sle = view.read(keylet::line(account, issuer, currency));
93 if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
94 return true;
95 }
96 return false;
97}
98
99// Can the specified account spend the specified currency issued by
100// the specified issuer or does the freeze flag prohibit it?
101bool
103 ReadView const& view,
104 AccountID const& account,
105 Currency const& currency,
106 AccountID const& issuer)
107{
108 if (isXRP(currency))
109 return false;
110 auto sle = view.read(keylet::account(issuer));
111 if (sle && sle->isFlag(lsfGlobalFreeze))
112 return true;
113 if (issuer != account)
114 {
115 // Check if the issuer froze the line
116 sle = view.read(keylet::line(account, issuer, currency));
117 if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
118 return true;
119 }
120 return false;
121}
122
123bool
125 ReadView const& view,
126 AccountID const& account,
127 Currency const& currency,
128 AccountID const& issuer)
129{
130 if (isXRP(currency))
131 {
132 return false;
133 }
134
135 if (issuer == account)
136 {
137 return false;
138 }
139
140 auto const sle = view.read(keylet::line(account, issuer, currency));
141 if (!sle)
142 {
143 return false;
144 }
145
146 return sle->isFlag(lsfHighDeepFreeze) || sle->isFlag(lsfLowDeepFreeze);
147}
148
149//------------------------------------------------------------------------------
150//
151// Trust line operations
152//
153//------------------------------------------------------------------------------
154
155TER
157 ApplyView& view,
158 bool const bSrcHigh,
159 AccountID const& uSrcAccountID,
160 AccountID const& uDstAccountID,
161 uint256 const& uIndex, // --> ripple state entry
162 SLE::ref sleAccount, // --> the account being set.
163 bool const bAuth, // --> authorize account.
164 bool const bNoRipple, // --> others cannot ripple through
165 bool const bFreeze, // --> funds cannot leave
166 bool bDeepFreeze, // --> can neither receive nor send funds
167 STAmount const& saBalance, // --> balance of account being set.
168 // Issuer should be noAccount()
169 STAmount const& saLimit, // --> limit for account being set.
170 // Issuer should be the account being set.
171 std::uint32_t uQualityIn,
172 std::uint32_t uQualityOut,
174{
175 JLOG(j.trace()) << "trustCreate: " << to_string(uSrcAccountID) << ", "
176 << to_string(uDstAccountID) << ", " << saBalance.getFullText();
177
178 auto const& uLowAccountID = !bSrcHigh ? uSrcAccountID : uDstAccountID;
179 auto const& uHighAccountID = bSrcHigh ? uSrcAccountID : uDstAccountID;
180 if (uLowAccountID == uHighAccountID)
181 {
182 // LCOV_EXCL_START
183 UNREACHABLE("xrpl::trustCreate : trust line to self");
184 if (view.rules().enabled(featureLendingProtocol))
185 return tecINTERNAL;
186 // LCOV_EXCL_STOP
187 }
188
189 auto const sleRippleState = std::make_shared<SLE>(ltRIPPLE_STATE, uIndex);
190 view.insert(sleRippleState);
191
192 auto lowNode = view.dirInsert(
193 keylet::ownerDir(uLowAccountID), sleRippleState->key(), describeOwnerDir(uLowAccountID));
194
195 if (!lowNode)
196 return tecDIR_FULL; // LCOV_EXCL_LINE
197
198 auto highNode = view.dirInsert(
199 keylet::ownerDir(uHighAccountID), sleRippleState->key(), describeOwnerDir(uHighAccountID));
200
201 if (!highNode)
202 return tecDIR_FULL; // LCOV_EXCL_LINE
203
204 bool const bSetDst = saLimit.getIssuer() == uDstAccountID;
205 bool const bSetHigh = bSrcHigh ^ bSetDst;
206
207 XRPL_ASSERT(sleAccount, "xrpl::trustCreate : non-null SLE");
208 if (!sleAccount)
209 return tefINTERNAL; // LCOV_EXCL_LINE
210
211 XRPL_ASSERT(
212 sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID),
213 "xrpl::trustCreate : matching account ID");
214 auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
215 if (!slePeer)
216 return tecNO_TARGET;
217
218 // Remember deletion hints.
219 sleRippleState->setFieldU64(sfLowNode, *lowNode);
220 sleRippleState->setFieldU64(sfHighNode, *highNode);
221
222 sleRippleState->setFieldAmount(bSetHigh ? sfHighLimit : sfLowLimit, saLimit);
223 sleRippleState->setFieldAmount(
224 bSetHigh ? sfLowLimit : sfHighLimit,
225 STAmount(Issue{saBalance.getCurrency(), bSetDst ? uSrcAccountID : uDstAccountID}));
226
227 if (uQualityIn != 0u)
228 sleRippleState->setFieldU32(bSetHigh ? sfHighQualityIn : sfLowQualityIn, uQualityIn);
229
230 if (uQualityOut != 0u)
231 sleRippleState->setFieldU32(bSetHigh ? sfHighQualityOut : sfLowQualityOut, uQualityOut);
232
233 std::uint32_t uFlags = bSetHigh ? lsfHighReserve : lsfLowReserve;
234
235 if (bAuth)
236 {
237 uFlags |= (bSetHigh ? lsfHighAuth : lsfLowAuth);
238 }
239 if (bNoRipple)
240 {
241 uFlags |= (bSetHigh ? lsfHighNoRipple : lsfLowNoRipple);
242 }
243 if (bFreeze)
244 {
245 uFlags |= (bSetHigh ? lsfHighFreeze : lsfLowFreeze);
246 }
247 if (bDeepFreeze)
248 {
249 uFlags |= (bSetHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze);
250 }
251
252 if ((slePeer->getFlags() & lsfDefaultRipple) == 0)
253 {
254 // The other side's default is no rippling
255 uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple);
256 }
257
258 sleRippleState->setFieldU32(sfFlags, uFlags);
259 adjustOwnerCount(view, sleAccount, 1, j);
260
261 // ONLY: Create ripple balance.
262 sleRippleState->setFieldAmount(sfBalance, bSetHigh ? -saBalance : saBalance);
263
264 view.creditHook(uSrcAccountID, uDstAccountID, saBalance, saBalance.zeroed());
265
266 return tesSUCCESS;
267}
268
269TER
271 ApplyView& view,
272 std::shared_ptr<SLE> const& sleRippleState,
273 AccountID const& uLowAccountID,
274 AccountID const& uHighAccountID,
276{
277 // Detect legacy dirs.
278 std::uint64_t const uLowNode = sleRippleState->getFieldU64(sfLowNode);
279 std::uint64_t const uHighNode = sleRippleState->getFieldU64(sfHighNode);
280
281 JLOG(j.trace()) << "trustDelete: Deleting ripple line: low";
282
283 if (!view.dirRemove(keylet::ownerDir(uLowAccountID), uLowNode, sleRippleState->key(), false))
284 {
285 return tefBAD_LEDGER; // LCOV_EXCL_LINE
286 }
287
288 JLOG(j.trace()) << "trustDelete: Deleting ripple line: high";
289
290 if (!view.dirRemove(keylet::ownerDir(uHighAccountID), uHighNode, sleRippleState->key(), false))
291 {
292 return tefBAD_LEDGER; // LCOV_EXCL_LINE
293 }
294
295 JLOG(j.trace()) << "trustDelete: Deleting ripple line: state";
296 view.erase(sleRippleState);
297
298 return tesSUCCESS;
299}
300
301//------------------------------------------------------------------------------
302//
303// IOU issuance/redemption
304//
305//------------------------------------------------------------------------------
306
307static bool
309 ApplyView& view,
310 SLE::pointer state,
311 bool bSenderHigh,
312 AccountID const& sender,
313 STAmount const& before,
314 STAmount const& after,
316{
317 if (!state)
318 return false;
319 std::uint32_t const flags(state->getFieldU32(sfFlags));
320
321 auto sle = view.peek(keylet::account(sender));
322 if (!sle)
323 return false;
324
325 // YYY Could skip this if rippling in reverse.
326 if (before > beast::zero
327 // Sender balance was positive.
328 && after <= beast::zero
329 // Sender is zero or negative.
330 && ((flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u)
331 // Sender reserve is set.
332 && static_cast<bool>(flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
333 static_cast<bool>(sle->getFlags() & lsfDefaultRipple) &&
334 ((flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) &&
335 !state->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
336 // Sender trust limit is 0.
337 && (state->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) == 0u)
338 // Sender quality in is 0.
339 && (state->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut) == 0u))
340 // Sender quality out is 0.
341 {
342 // VFALCO Where is the line being deleted?
343 // Clear the reserve of the sender, possibly delete the line!
344 adjustOwnerCount(view, sle, -1, j);
345
346 // Clear reserve flag.
347 state->setFieldU32(sfFlags, flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
348
349 // Balance is zero, receiver reserve is clear.
350 if (!after // Balance is zero.
351 && ((flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)) == 0u))
352 return true;
353 }
354 return false;
355}
356
357TER
359 ApplyView& view,
360 AccountID const& account,
361 STAmount const& amount,
362 Issue const& issue,
364{
365 XRPL_ASSERT(
366 !isXRP(account) && !isXRP(issue.account),
367 "xrpl::issueIOU : neither account nor issuer is XRP");
368
369 // Consistency check
370 XRPL_ASSERT(issue == amount.issue(), "xrpl::issueIOU : matching issue");
371
372 // Can't send to self!
373 XRPL_ASSERT(issue.account != account, "xrpl::issueIOU : not issuer account");
374
375 JLOG(j.trace()) << "issueIOU: " << to_string(account) << ": " << amount.getFullText();
376
377 bool const bSenderHigh = issue.account > account;
378
379 auto const index = keylet::line(issue.account, account, issue.currency);
380
381 if (auto state = view.peek(index))
382 {
383 STAmount final_balance = state->getFieldAmount(sfBalance);
384
385 if (bSenderHigh)
386 final_balance.negate(); // Put balance in sender terms.
387
388 STAmount const start_balance = final_balance;
389
390 final_balance -= amount;
391
392 auto const must_delete = updateTrustLine(
393 view, state, bSenderHigh, issue.account, start_balance, final_balance, j);
394
395 view.creditHook(issue.account, account, amount, start_balance);
396
397 if (bSenderHigh)
398 final_balance.negate();
399
400 // Adjust the balance on the trust line if necessary. We do this even
401 // if we are going to delete the line to reflect the correct balance
402 // at the time of deletion.
403 state->setFieldAmount(sfBalance, final_balance);
404 if (must_delete)
405 {
406 return trustDelete(
407 view,
408 state,
409 bSenderHigh ? account : issue.account,
410 bSenderHigh ? issue.account : account,
411 j);
412 }
413
414 view.update(state);
415
416 return tesSUCCESS;
417 }
418
419 // NIKB TODO: The limit uses the receiver's account as the issuer and
420 // this is unnecessarily inefficient as copying which could be avoided
421 // is now required. Consider available options.
422 STAmount const limit(Issue{issue.currency, account});
423 STAmount final_balance = amount;
424
425 final_balance.setIssuer(noAccount());
426
427 auto const receiverAccount = view.peek(keylet::account(account));
428 if (!receiverAccount)
429 return tefINTERNAL; // LCOV_EXCL_LINE
430
431 bool const noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0;
432
433 return trustCreate(
434 view,
435 bSenderHigh,
436 issue.account,
437 account,
438 index.key,
439 receiverAccount,
440 false,
441 noRipple,
442 false,
443 false,
444 final_balance,
445 limit,
446 0,
447 0,
448 j);
449}
450
451TER
453 ApplyView& view,
454 AccountID const& account,
455 STAmount const& amount,
456 Issue const& issue,
458{
459 XRPL_ASSERT(
460 !isXRP(account) && !isXRP(issue.account),
461 "xrpl::redeemIOU : neither account nor issuer is XRP");
462
463 // Consistency check
464 XRPL_ASSERT(issue == amount.issue(), "xrpl::redeemIOU : matching issue");
465
466 // Can't send to self!
467 XRPL_ASSERT(issue.account != account, "xrpl::redeemIOU : not issuer account");
468
469 JLOG(j.trace()) << "redeemIOU: " << to_string(account) << ": " << amount.getFullText();
470
471 bool const bSenderHigh = account > issue.account;
472
473 if (auto state = view.peek(keylet::line(account, issue.account, issue.currency)))
474 {
475 STAmount final_balance = state->getFieldAmount(sfBalance);
476
477 if (bSenderHigh)
478 final_balance.negate(); // Put balance in sender terms.
479
480 STAmount const start_balance = final_balance;
481
482 final_balance -= amount;
483
484 auto const must_delete =
485 updateTrustLine(view, state, bSenderHigh, account, start_balance, final_balance, j);
486
487 view.creditHook(account, issue.account, amount, start_balance);
488
489 if (bSenderHigh)
490 final_balance.negate();
491
492 // Adjust the balance on the trust line if necessary. We do this even
493 // if we are going to delete the line to reflect the correct balance
494 // at the time of deletion.
495 state->setFieldAmount(sfBalance, final_balance);
496
497 if (must_delete)
498 {
499 return trustDelete(
500 view,
501 state,
502 bSenderHigh ? issue.account : account,
503 bSenderHigh ? account : issue.account,
504 j);
505 }
506
507 view.update(state);
508 return tesSUCCESS;
509 }
510
511 // In order to hold an IOU, a trust line *MUST* exist to track the
512 // balance. If it doesn't, then something is very wrong. Don't try
513 // to continue.
514 // LCOV_EXCL_START
515 JLOG(j.fatal()) << "redeemIOU: " << to_string(account) << " attempts to "
516 << "redeem " << amount.getFullText() << " but no trust line exists!";
517
518 return tefINTERNAL;
519 // LCOV_EXCL_STOP
520}
521
522//------------------------------------------------------------------------------
523//
524// Authorization and transfer checks (IOU-specific)
525//
526//------------------------------------------------------------------------------
527
528TER
529requireAuth(ReadView const& view, Issue const& issue, AccountID const& account, AuthType authType)
530{
531 if (isXRP(issue) || issue.account == account)
532 return tesSUCCESS;
533
534 auto const trustLine = view.read(keylet::line(account, issue.account, issue.currency));
535 // If account has no line, and this is a strong check, fail
536 if (!trustLine && authType == AuthType::StrongAuth)
537 return tecNO_LINE;
538
539 // If this is a weak or legacy check, or if the account has a line, fail if
540 // auth is required and not set on the line
541 if (auto const issuerAccount = view.read(keylet::account(issue.account));
542 issuerAccount && (((*issuerAccount)[sfFlags] & lsfRequireAuth) != 0u))
543 {
544 if (trustLine)
545 {
546 return (((*trustLine)[sfFlags] &
547 ((account > issue.account) ? lsfLowAuth : lsfHighAuth)) != 0u)
548 ? tesSUCCESS
549 : TER{tecNO_AUTH};
550 }
551 return TER{tecNO_LINE};
552 }
553
554 return tesSUCCESS;
555}
556
557TER
558canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to)
559{
560 if (issue.native())
561 return tesSUCCESS;
562
563 auto const& issuerId = issue.getIssuer();
564 if (issuerId == from || issuerId == to)
565 return tesSUCCESS;
566 auto const sleIssuer = view.read(keylet::account(issuerId));
567 if (sleIssuer == nullptr)
568 return tefINTERNAL; // LCOV_EXCL_LINE
569
570 auto const isRippleDisabled = [&](AccountID account) -> bool {
571 // Line might not exist, but some transfers can create it. If this
572 // is the case, just check the default ripple on the issuer account.
573 auto const line = view.read(keylet::line(account, issue));
574 if (line)
575 {
576 bool const issuerHigh = issuerId > account;
577 return line->isFlag(issuerHigh ? lsfHighNoRipple : lsfLowNoRipple);
578 }
579 return !sleIssuer->isFlag(lsfDefaultRipple);
580 };
581
582 // Fail if rippling disabled on both trust lines
583 if (isRippleDisabled(from) && isRippleDisabled(to))
584 return terNO_RIPPLE;
585
586 return tesSUCCESS;
587}
588
589//------------------------------------------------------------------------------
590//
591// Empty holding operations (IOU-specific)
592//
593//------------------------------------------------------------------------------
594
595TER
597 ApplyView& view,
598 AccountID const& accountID,
599 XRPAmount priorBalance,
600 Issue const& issue,
601 beast::Journal journal)
602{
603 // Every account can hold XRP. An issuer can issue directly.
604 if (issue.native() || accountID == issue.getIssuer())
605 return tesSUCCESS;
606
607 auto const& issuerId = issue.getIssuer();
608 auto const& currency = issue.currency;
609 if (isGlobalFrozen(view, issuerId))
610 return tecFROZEN; // LCOV_EXCL_LINE
611
612 auto const& srcId = issuerId;
613 auto const& dstId = accountID;
614 auto const high = srcId > dstId;
615 auto const index = keylet::line(srcId, dstId, currency);
616 auto const sleSrc = view.peek(keylet::account(srcId));
617 auto const sleDst = view.peek(keylet::account(dstId));
618 if (!sleDst || !sleSrc)
619 return tefINTERNAL; // LCOV_EXCL_LINE
620 if (!sleSrc->isFlag(lsfDefaultRipple))
621 return tecINTERNAL; // LCOV_EXCL_LINE
622 // If the line already exists, don't create it again.
623 if (view.read(index))
624 return tecDUPLICATE;
625
626 // Can the account cover the trust line reserve ?
627 std::uint32_t const ownerCount = sleDst->at(sfOwnerCount);
628 if (priorBalance < view.fees().accountReserve(ownerCount + 1))
630
631 return trustCreate(
632 view,
633 high,
634 srcId,
635 dstId,
636 index.key,
637 sleDst,
638 /*bAuth=*/false,
639 /*bNoRipple=*/true,
640 /*bFreeze=*/false,
641 /*deepFreeze*/ false,
642 /*saBalance=*/STAmount{Issue{currency, noAccount()}},
643 /*saLimit=*/STAmount{Issue{currency, dstId}},
644 /*uQualityIn=*/0,
645 /*uQualityOut=*/0,
646 journal);
647}
648
649TER
651 ApplyView& view,
652 AccountID const& accountID,
653 Issue const& issue,
654 beast::Journal journal)
655{
656 if (issue.native())
657 {
658 auto const sle = view.read(keylet::account(accountID));
659 if (!sle)
660 return tecINTERNAL; // LCOV_EXCL_LINE
661
662 auto const balance = sle->getFieldAmount(sfBalance);
663 if (balance.xrp() != 0)
664 return tecHAS_OBLIGATIONS;
665
666 return tesSUCCESS;
667 }
668
669 // `asset` is an IOU.
670 // If the account is the issuer, then no line should exist. Check anyway.
671 // If a line does exist, it will get deleted. If not, return success.
672 bool const accountIsIssuer = accountID == issue.account;
673 auto const line = view.peek(keylet::line(accountID, issue));
674 if (!line)
675 return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND;
676 if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::zero)
677 return tecHAS_OBLIGATIONS;
678
679 // Adjust the owner count(s)
680 if (line->isFlag(lsfLowReserve))
681 {
682 // Clear reserve for low account.
683 auto sleLowAccount = view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
684 if (!sleLowAccount)
685 return tecINTERNAL; // LCOV_EXCL_LINE
686
687 adjustOwnerCount(view, sleLowAccount, -1, journal);
688 // It's not really necessary to clear the reserve flag, since the line
689 // is about to be deleted, but this will make the metadata reflect an
690 // accurate state at the time of deletion.
691 line->clearFlag(lsfLowReserve);
692 }
693
694 if (line->isFlag(lsfHighReserve))
695 {
696 // Clear reserve for high account.
697 auto sleHighAccount = view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
698 if (!sleHighAccount)
699 return tecINTERNAL; // LCOV_EXCL_LINE
700
701 adjustOwnerCount(view, sleHighAccount, -1, journal);
702 // It's not really necessary to clear the reserve flag, since the line
703 // is about to be deleted, but this will make the metadata reflect an
704 // accurate state at the time of deletion.
705 line->clearFlag(lsfHighReserve);
706 }
707
708 return trustDelete(
709 view, line, line->at(sfLowLimit)->getIssuer(), line->at(sfHighLimit)->getIssuer(), journal);
710}
711
712TER
714 ApplyView& view,
715 std::shared_ptr<SLE> sleState,
716 std::optional<AccountID> const& ammAccountID,
718{
719 if (!sleState || sleState->getType() != ltRIPPLE_STATE)
720 return tecINTERNAL; // LCOV_EXCL_LINE
721
722 auto const& [low, high] = std::minmax(
723 sleState->getFieldAmount(sfLowLimit).getIssuer(),
724 sleState->getFieldAmount(sfHighLimit).getIssuer());
725 auto sleLow = view.peek(keylet::account(low));
726 auto sleHigh = view.peek(keylet::account(high));
727 if (!sleLow || !sleHigh)
728 return tecINTERNAL; // LCOV_EXCL_LINE
729
730 bool const ammLow = sleLow->isFieldPresent(sfAMMID);
731 bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
732
733 // can't both be AMM
734 if (ammLow && ammHigh)
735 return tecINTERNAL; // LCOV_EXCL_LINE
736
737 // at least one must be
738 if (!ammLow && !ammHigh)
739 return terNO_AMM;
740
741 // one must be the target amm
742 if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
743 return terNO_AMM;
744
745 if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter))
746 {
747 JLOG(j.error()) << "deleteAMMTrustLine: failed to delete the trustline.";
748 return ter;
749 }
750
751 auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
752 if ((sleState->getFlags() & uFlags) == 0u)
753 return tecINTERNAL; // LCOV_EXCL_LINE
754
755 adjustOwnerCount(view, !ammLow ? sleLow : sleHigh, -1, j);
756
757 return tesSUCCESS;
758}
759
760} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:325
Stream error() const
Definition Journal.h:319
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:116
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.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
Definition ApplyView.h:216
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:289
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
bool native() const
Definition Issue.cpp:53
AccountID const & getIssuer() const
Definition Issue.h:25
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:120
std::string getFullText() const override
Definition STAmount.cpp:646
void negate()
Definition STAmount.h:548
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
Definition STAmount.h:494
Currency const & getCurrency() const
Definition STAmount.h:476
AccountID const & getIssuer() const
Definition STAmount.h:482
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:593
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:735
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:649
T is_same_v
T minmax(T... args)
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:336
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 account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_AMM
Definition TER.h:207
@ terNO_RIPPLE
Definition TER.h:204
bool isXRP(AccountID const &c)
Definition AccountID.h:70
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
bool isIndividualFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
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.
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, MPTIssue const &mptIssue, beast::Journal journal)
@ tefBAD_LEDGER
Definition TER.h:150
@ tefINTERNAL
Definition TER.h:153
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
STAmount creditLimit(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const &currency)
Calculate the maximum amount of IOUs that an account can hold.
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
IOUAmount creditLimit2(ReadView const &v, AccountID const &acc, AccountID const &iss, Currency const &cur)
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Check if the issuer has the global freeze flag set.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, int depth=0)
TERSubset< CanCvtToTER > TER
Definition TER.h:622
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:523
STAmount creditBalance(ReadView const &view, AccountID const &account, AccountID const &issuer, Currency const &currency)
Returns the amount of IOUs issued by issuer that are held by an account.
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
std::function< void(SLE::ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
AccountID const & noAccount()
A placeholder for empty accounts.
static bool updateTrustLine(ApplyView &view, SLE::pointer state, bool bSenderHigh, AccountID const &sender, STAmount const &before, STAmount const &after, beast::Journal j)
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
IOUAmount toAmount< IOUAmount >(STAmount const &amt)
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
@ tecDIR_FULL
Definition TER.h:268
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:273
@ tecNO_TARGET
Definition TER.h:285
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecNO_AUTH
Definition TER.h:281
@ tecINTERNAL
Definition TER.h:291
@ tecFROZEN
Definition TER.h:284
@ tecNO_LINE
Definition TER.h:282
@ tecDUPLICATE
Definition TER.h:296
@ tecHAS_OBLIGATIONS
Definition TER.h:298
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, MPTIssue const &mptIssue, beast::Journal journal)
@ tesSUCCESS
Definition TER.h:225
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, int depth=0)
Check if the account lacks required authorization for MPT.
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
uint256 key
Definition Keylet.h:20