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