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 if (trustLine->isFlag((account > issue.account) ? lsfLowAuth : lsfHighAuth))
588 return tesSUCCESS;
589
590 // A pseudo-account cannot submit transactions and only stores assets for the object
591 // that owns it, so it is implicitly authorized.
592 if (view.rules().enabled(fixCleanup3_4_0) && isPseudoAccount(view, account))
593 return tesSUCCESS;
594
595 return TER{tecNO_AUTH};
596 }
597 return TER{tecNO_LINE};
598 }
599
600 return tesSUCCESS;
601}
602
603TER
604canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, AccountID const& to)
605{
606 if (issue.native())
607 return tesSUCCESS;
608
609 auto const& issuerId = issue.getIssuer();
610 if (issuerId == from || issuerId == to)
611 return tesSUCCESS;
612 auto const sleIssuer = view.read(keylet::account(issuerId));
613 if (sleIssuer == nullptr)
614 return tefINTERNAL; // LCOV_EXCL_LINE
615
616 auto const isRippleDisabled = [&](AccountID account) -> bool {
617 // Line might not exist, but some transfers can create it. If this
618 // is the case, just check the default ripple on the issuer account.
619 auto const line = view.read(keylet::trustLine(account, issue));
620 if (line)
621 {
622 bool const issuerHigh = issuerId > account;
623 return line->isFlag(issuerHigh ? lsfHighNoRipple : lsfLowNoRipple);
624 }
625 return !sleIssuer->isFlag(lsfDefaultRipple);
626 };
627
628 // Fail if rippling disabled on both trust lines
629 if (isRippleDisabled(from) && isRippleDisabled(to))
630 return terNO_RIPPLE;
631
632 return tesSUCCESS;
633}
634
635//------------------------------------------------------------------------------
636//
637// Empty holding operations (IOU-specific)
638//
639//------------------------------------------------------------------------------
640
641TER
644 AccountID const& accountID,
645 XRPAmount priorBalance,
646 Issue const& issue,
647 beast::Journal journal)
648{
649 // Every account can hold XRP. An issuer can issue directly.
650 if (issue.native() || accountID == issue.getIssuer())
651 return tesSUCCESS;
652
653 auto const& issuerId = issue.getIssuer();
654 auto const& currency = issue.currency;
655 auto const& srcId = issuerId;
656 auto const& dstId = accountID;
657 auto const high = srcId > dstId;
658 auto const index = keylet::trustLine(srcId, dstId, currency);
659 // Post-fixCleanup3_4_0: an existing line is a no-op. Issuer freeze and
660 // DefaultRipple only matter when this function has to create a line.
661 bool const fix340Enabled = ctx.view.rules().enabled(fixCleanup3_4_0);
662 if (fix340Enabled && ctx.view.exists(index))
663 return tecDUPLICATE;
664
665 if (isGlobalFrozen(ctx.view, issuerId))
666 return tecFROZEN; // LCOV_EXCL_LINE
667
668 auto const sleSrc = ctx.view.peek(keylet::account(srcId));
669 auto const sleDst = ctx.view.peek(keylet::account(dstId));
670 if (!sleDst || !sleSrc)
671 return tefINTERNAL; // LCOV_EXCL_LINE
672 // Create path: DefaultRipple is still required. terNO_RIPPLE is
673 // intentional so VaultWithdraw / CoverWithdraw fail in preclaim via
674 // canAddHolding (retryable, no fee) rather than claiming a tec* fee
675 // in doApply. Transactor::operator() will not apply and will not
676 // convert it to tefINTERNAL.
677 if (!sleSrc->isFlag(lsfDefaultRipple))
678 return fix340Enabled ? TER{terNO_RIPPLE} : tecINTERNAL;
679 // If the line already exists, don't create it again.
680 if (!fix340Enabled && ctx.view.exists(index))
681 return tecDUPLICATE;
682
683 // A reserve sponsor only covers tx.Account's own objects.
684 auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst);
685 if (!sponsorExp)
686 return sponsorExp.error(); // LCOV_EXCL_LINE
687 auto const sponsorSle = *sponsorExp;
688
689 // Can the account cover the trust line reserve ?
690 if (auto const ret = checkReserve(
691 ctx,
692 sleDst,
693 priorBalance,
694 sponsorSle,
695 {.ownerCountDelta = 1},
696 journal,
698 !isTesSuccess(ret))
699 {
700 return ret;
701 }
702
703 return trustCreate(
704 ctx.view,
705 high,
706 srcId,
707 dstId,
708 index.key,
709 sleDst,
710 /*bAuth=*/false,
711 /*bNoRipple=*/true,
712 /*bFreeze=*/false,
713 /*deepFreeze*/ false,
714 /*saBalance=*/STAmount{Issue{currency, noAccount()}},
715 /*saLimit=*/STAmount{Issue{currency, dstId}},
716 /*uQualityIn=*/0,
717 /*uQualityOut=*/0,
718 sponsorSle,
719 journal);
720}
721
722TER
725 AccountID const& accountID,
726 Issue const& issue,
727 beast::Journal journal)
728{
729 if (issue.native())
730 {
731 auto const sle = ctx.view.read(keylet::account(accountID));
732 if (!sle)
733 return tecINTERNAL; // LCOV_EXCL_LINE
734
735 auto const balance = sle->getFieldAmount(sfBalance);
736 if (balance.xrp() != 0)
737 return tecHAS_OBLIGATIONS;
738
739 return tesSUCCESS;
740 }
741
742 // `asset` is an IOU.
743 // If the account is the issuer, then no line should exist. Check anyway.
744 // If a line does exist, it will get deleted. If not, return success.
745 bool const accountIsIssuer = accountID == issue.account;
746 auto const line = ctx.view.peek(keylet::trustLine(accountID, issue));
747 if (!line)
748 return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND;
749 if (!accountIsIssuer && line->at(sfBalance)->iou() != beast::kZero)
750 return tecHAS_OBLIGATIONS;
751
752 // Adjust the owner count(s)
753 if (line->isFlag(lsfLowReserve))
754 {
755 // Clear reserve for low account.
756 auto sleLowAccount = ctx.view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
757 if (!sleLowAccount)
758 return tecINTERNAL; // LCOV_EXCL_LINE
759
760 auto const currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor);
761
762 decreaseOwnerCount(ctx.view, sleLowAccount, currentLowSponsor, 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(lsfLowReserve);
767 removeSponsorFromLedgerEntry(line, sfLowSponsor);
768 }
769
770 if (line->isFlag(lsfHighReserve))
771 {
772 // Clear reserve for high account.
773 auto sleHighAccount = ctx.view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
774 if (!sleHighAccount)
775 return tecINTERNAL; // LCOV_EXCL_LINE
776
777 auto const currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor);
778
779 decreaseOwnerCount(ctx.view, sleHighAccount, currentHighSponsor, 1, journal);
780 // It's not really necessary to clear the reserve flag, since the line
781 // is about to be deleted, but this will make the metadata reflect an
782 // accurate state at the time of deletion.
783 line->clearFlag(lsfHighReserve);
784 removeSponsorFromLedgerEntry(line, sfHighSponsor);
785 }
786
787 return trustDelete(
788 ctx.view,
789 line,
790 line->at(sfLowLimit)->getIssuer(),
791 line->at(sfHighLimit)->getIssuer(),
792 journal);
793}
794
795TER
797 ApplyView& view,
798 SLE::pointer sleState,
799 std::optional<AccountID> const& ammAccountID,
801{
802 if (!sleState || sleState->getType() != ltRIPPLE_STATE)
803 return tecINTERNAL; // LCOV_EXCL_LINE
804
805 auto const& [low, high] = std::minmax(
806 sleState->getFieldAmount(sfLowLimit).getIssuer(),
807 sleState->getFieldAmount(sfHighLimit).getIssuer());
808 auto sleLow = view.peek(keylet::account(low));
809 auto sleHigh = view.peek(keylet::account(high));
810 if (!sleLow || !sleHigh)
811 return tecINTERNAL; // LCOV_EXCL_LINE
812
813 bool const ammLow = sleLow->isFieldPresent(sfAMMID);
814 bool const ammHigh = sleHigh->isFieldPresent(sfAMMID);
815
816 // can't both be AMM
817 if (ammLow && ammHigh)
818 return tecINTERNAL; // LCOV_EXCL_LINE
819
820 // at least one must be
821 if (!ammLow && !ammHigh)
822 return terNO_AMM;
823
824 // one must be the target amm
825 if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
826 return terNO_AMM;
827
828 auto const sponsorSle =
829 getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor);
830
831 if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter))
832 {
833 JLOG(j.error()) << "deleteAMMTrustLine: failed to delete the trustline.";
834 return ter;
835 }
836
837 auto const uFlags = !ammLow ? lsfLowReserve : lsfHighReserve;
838 if (!sleState->isFlag(uFlags))
839 return tecINTERNAL; // LCOV_EXCL_LINE
840
841 decreaseOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, 1, j);
842
843 return tesSUCCESS;
844}
845
846TER
848 ApplyView& view,
849 SLE::pointer sleMpt,
850 AccountID const& ammAccountID,
852{
853 if (!view.dirRemove(
854 keylet::ownerDir(ammAccountID), (*sleMpt)[sfOwnerNode], sleMpt->key(), false))
855 return tefBAD_LEDGER; // LCOV_EXCL_LINE
856
857 view.erase(sleMpt);
858
859 return tesSUCCESS;
860}
861
862} // 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:141
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.
virtual void creditHookIOU(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
Definition ApplyView.h:248
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:373
bool dirRemove(Keylet const &directory, std::uint64_t page, UInt256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void erase(SLE::Ref sle)=0
Remove a peeked SLE.
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 bool exists(Keylet const &k) const =0
Determine if a state item exists.
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:182
constexpr TIss const & get() const
std::string getFullText() const override
Definition STAmount.cpp:637
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 > pointer
std::shared_ptr< STLedgerEntry > const & Ref
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:403
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
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:275
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void removeSponsorFromLedgerEntry(SLE::Ref sle, SF_ACCOUNT const &field=sfSponsor)
Remove the reserve sponsor field from a ledger entry.
@ terNO_AMM
Definition TER.h:228
@ terNO_RIPPLE
Definition TER.h:225
bool isXRP(AccountID const &c)
Definition AccountID.h:84
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:165
@ tefINTERNAL
Definition TER.h:168
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 checkReserve(ApplyViewContext ctx, SLE::ConstRef accSle, XRPAmount accBalance, SLE::ConstRef sponsorSle, Adjustment adj, beast::Journal j, TER insufReserveCode=tecINSUFFICIENT_RESERVE)
Check if an account has sufficient reserve.
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.
std::expected< SLE::pointer, TER > getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle)
The transaction's reserve sponsor for the given account, if applicable.
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.
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.
TER issueIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, SLE::Ref sponsorSle, beast::Journal j)
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
void addSponsorToLedgerEntry(SLE::Ref sle, SLE::ConstRef sponsorSle, SF_ACCOUNT const &field=sfSponsor)
Stamp a reserve sponsor onto a ledger entry using an explicit sponsor SLE.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
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.
BaseUInt< 256 > UInt256
Definition base_uint.h:580
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:644
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.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, std::uint8_t depth=0)
Returns true if account cannot send or receive tokens of mptIssue because a freeze applies.
bool isPseudoAccount(SLE::const_pointer sleAcct)
Returns true if and only if sleAcct is a pseudo-account of any kind (i.e.
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 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:683
SLE::pointer getLedgerEntryReserveSponsor(ApplyView &view, SLE::ConstRef sle, SF_ACCOUNT const &field=sfSponsor)
Return a mutable SLE for the reserve sponsor recorded on a ledger entry.
TERSubset< CanCvtToTER > TER
Definition TER.h:654
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:295
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:300
@ tecNO_TARGET
Definition TER.h:312
@ tecOBJECT_NOT_FOUND
Definition TER.h:334
@ tecNO_AUTH
Definition TER.h:308
@ tecINTERNAL
Definition TER.h:318
@ tecFROZEN
Definition TER.h:311
@ tecNO_LINE
Definition TER.h:309
@ tecDUPLICATE
Definition TER.h:323
@ tecHAS_OBLIGATIONS
Definition TER.h:325
std::function< void(SLE::Ref)> describeOwnerDir(AccountID const &account)
Returns a function that sets the owner on a directory SLE.
TER trustDelete(ApplyView &view, SLE::Ref sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
@ tesSUCCESS
Definition TER.h:250
Bundles the mutable ledger view and the transaction being applied.
Definition ApplyView.h:451
UInt256 key
Definition Keylet.h:21