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