xrpld
Loading...
Searching...
No Matches
TrustSet.cpp
1#include <xrpl/tx/transactors/token/TrustSet.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/core/ServiceRegistry.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/ledger/helpers/AccountRootHelpers.h>
9#include <xrpl/ledger/helpers/RippleStateHelpers.h>
10#include <xrpl/ledger/helpers/SponsorHelpers.h>
11#include <xrpl/protocol/AMMCore.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Feature.h>
14#include <xrpl/protocol/Indexes.h>
15#include <xrpl/protocol/LedgerFormats.h>
16#include <xrpl/protocol/Permissions.h>
17#include <xrpl/protocol/Quality.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/STAmount.h>
20#include <xrpl/protocol/STLedgerEntry.h>
21#include <xrpl/protocol/STTx.h>
22#include <xrpl/protocol/TER.h>
23#include <xrpl/protocol/TxFlags.h>
24#include <xrpl/protocol/UintTypes.h>
25#include <xrpl/protocol/XRPAmount.h>
26#include <xrpl/tx/Transactor.h>
27
28#include <cstdint>
29#include <unordered_set>
30
31namespace {
32
33uint32_t
34computeFreezeFlags(
35 uint32_t uFlags,
36 bool bHigh,
37 bool bNoFreeze,
38 bool bSetFreeze,
39 bool bClearFreeze,
40 bool bSetDeepFreeze,
41 bool bClearDeepFreeze)
42{
43 if (bSetFreeze && !bClearFreeze && !bNoFreeze)
44 {
45 uFlags |= (bHigh ? xrpl::lsfHighFreeze : xrpl::lsfLowFreeze);
46 }
47 else if (bClearFreeze && !bSetFreeze)
48 {
49 uFlags &= ~(bHigh ? xrpl::lsfHighFreeze : xrpl::lsfLowFreeze);
50 }
51 if (bSetDeepFreeze && !bClearDeepFreeze && !bNoFreeze)
52 {
53 uFlags |= (bHigh ? xrpl::lsfHighDeepFreeze : xrpl::lsfLowDeepFreeze);
54 }
55 else if (bClearDeepFreeze && !bSetDeepFreeze)
56 {
57 uFlags &= ~(bHigh ? xrpl::lsfHighDeepFreeze : xrpl::lsfLowDeepFreeze);
58 }
59
60 return uFlags;
61}
62
63} // namespace
64
65namespace xrpl {
66
67std::uint32_t
69{
70 return tfTrustSetMask;
71}
72
75{
76 auto& tx = ctx.tx;
77 auto& j = ctx.j;
78
79 if (!ctx.rules.enabled(featureDeepFreeze))
80 {
81 // Even though the deep freeze flags are included in the
82 // `tfTrustSetMask`, they are not valid if the amendment is not enabled.
83 if ((tx.getFlags() & (tfSetDeepFreeze | tfClearDeepFreeze)) != 0u)
84 {
85 return temINVALID_FLAG;
86 }
87 }
88
89 STAmount const saLimitAmount(tx.getFieldAmount(sfLimitAmount));
90
91 if (!isLegalNet(saLimitAmount))
92 return temBAD_AMOUNT;
93
94 if (saLimitAmount.native())
95 {
96 JLOG(j.trace()) << "Malformed transaction: specifies native limit "
97 << saLimitAmount.getFullText();
98 return temBAD_LIMIT;
99 }
100
101 if (badCurrency() == saLimitAmount.get<Issue>().currency)
102 {
103 JLOG(j.trace()) << "Malformed transaction: specifies XRP as IOU";
104 return temBAD_CURRENCY;
105 }
106
107 if (saLimitAmount < beast::kZero)
108 {
109 JLOG(j.trace()) << "Malformed transaction: Negative credit limit.";
110 return temBAD_LIMIT;
111 }
112
113 // Check if destination makes sense.
114 auto const& issuer = saLimitAmount.getIssuer();
115
116 if (!issuer || issuer == noAccount())
117 {
118 JLOG(j.trace()) << "Malformed transaction: no destination account.";
119 return temDST_NEEDED;
120 }
121
122 return tesSUCCESS;
123}
124
125NotTEC
127 ReadView const& view,
128 STTx const& tx,
129 std::unordered_set<GranularPermissionType> const& heldGranularPermissions)
130{
131 auto const saLimitAmount = tx.getFieldAmount(sfLimitAmount);
132 auto const sleRippleState = view.read(
134 tx[sfAccount], saLimitAmount.getIssuer(), saLimitAmount.get<Issue>().currency));
135
136 // granular permissions are not allowed to create a trustline
137 if (!sleRippleState)
139
140 // updating LimitAmount is not allowed with granular permissions,
141 // unless there's a new granular permission for this in the future.
142 auto const curLimit = tx[sfAccount] > saLimitAmount.getIssuer()
143 ? sleRippleState->getFieldAmount(sfHighLimit)
144 : sleRippleState->getFieldAmount(sfLowLimit);
145
146 STAmount saLimitAllow = saLimitAmount;
147 saLimitAllow.get<Issue>().account = tx[sfAccount];
148
149 if (curLimit != saLimitAllow)
151
152 return tesSUCCESS;
153}
154
155TER
157{
158 auto const id = ctx.tx[sfAccount];
159
160 auto const sle = ctx.view.read(keylet::account(id));
161 if (!sle)
162 return terNO_ACCOUNT;
163
164 bool const bSetAuth = ctx.tx.isFlag(tfSetfAuth);
165
166 if (bSetAuth && !sle->isFlag(lsfRequireAuth))
167 {
168 JLOG(ctx.j.trace()) << "Retry: Auth not required.";
169 return tefNO_AUTH_REQUIRED;
170 }
171
172 auto const saLimitAmount = ctx.tx[sfLimitAmount];
173
174 auto const currency = saLimitAmount.get<Issue>().currency;
175 auto const uDstAccountID = saLimitAmount.getIssuer();
176
177 if (id == uDstAccountID)
178 return temDST_IS_SRC;
179
180 // This might be nullptr
181 auto const sleDst = ctx.view.read(keylet::account(uDstAccountID));
182 if ((ammEnabled(ctx.view.rules()) || ctx.view.rules().enabled(featureSingleAssetVault)) &&
183 sleDst == nullptr)
184 return tecNO_DST;
185
186 // If the destination has opted to disallow incoming trustlines
187 // then honour that flag
188 if (sleDst && sleDst->isFlag(lsfDisallowIncomingTrustline) &&
189 !ctx.view.exists(keylet::trustLine(id, uDstAccountID, currency)))
190 {
191 return tecNO_PERMISSION;
192 }
193
194 // In general, trust lines to pseudo accounts are not permitted, unless
195 // enabled in the code section below, for specific cases. This block is not
196 // amendment-gated because sleDst will not have a pseudo-account designator
197 // field populated, unless the appropriate amendment was already enabled.
198 if (sleDst && isPseudoAccount(sleDst))
199 {
200 // If destination is AMM and the trustline doesn't exist then only allow
201 // TrustSet if the asset is AMM LP token and AMM is not in empty state.
202 if (sleDst->isFieldPresent(sfAMMID))
203 {
204 if (ctx.view.exists(keylet::trustLine(id, uDstAccountID, currency)))
205 {
206 // pass
207 }
208 else if (auto const ammSle = ctx.view.read({ltAMM, sleDst->getFieldH256(sfAMMID)}))
209 {
210 auto const lpTokens = ammSle->getFieldAmount(sfLPTokenBalance);
211 if (lpTokens == beast::kZero)
212 {
213 return tecAMM_EMPTY;
214 }
215 if (lpTokens.get<Issue>().currency != saLimitAmount.get<Issue>().currency)
216 {
217 return tecNO_PERMISSION;
218 }
219 }
220 else
221 {
222 return tecINTERNAL; // LCOV_EXCL_LINE
223 }
224 }
225 else if (sleDst->isFieldPresent(sfVaultID) || sleDst->isFieldPresent(sfLoanBrokerID))
226 {
227 if (!ctx.view.exists(keylet::trustLine(id, uDstAccountID, currency)))
228 return tecNO_PERMISSION;
229 // else pass
230 }
231 else
232 {
233 return tecPSEUDO_ACCOUNT;
234 }
235 }
236
237 // Checking all freeze/deep freeze flag invariants.
238 if (ctx.view.rules().enabled(featureDeepFreeze))
239 {
240 bool const bNoFreeze = sle->isFlag(lsfNoFreeze);
241 bool const bSetFreeze = ctx.tx.isFlag(tfSetFreeze);
242 bool const bSetDeepFreeze = ctx.tx.isFlag(tfSetDeepFreeze);
243
244 if (bNoFreeze && (bSetFreeze || bSetDeepFreeze))
245 {
246 // Cannot freeze the trust line if NoFreeze is set
247 return tecNO_PERMISSION;
248 }
249
250 bool const bClearFreeze = ctx.tx.isFlag(tfClearFreeze);
251 bool const bClearDeepFreeze = ctx.tx.isFlag(tfClearDeepFreeze);
252 if ((bSetFreeze || bSetDeepFreeze) && (bClearFreeze || bClearDeepFreeze))
253 {
254 // Freezing and unfreezing in the same transaction should be
255 // illegal
256 return tecNO_PERMISSION;
257 }
258
259 bool const bHigh = id > uDstAccountID;
260 // Fetching current state of trust line
261 auto const sleRippleState = ctx.view.read(keylet::trustLine(id, uDstAccountID, currency));
262 std::uint32_t uFlags = sleRippleState ? sleRippleState->getFieldU32(sfFlags) : 0u;
263 // Computing expected trust line state
264 uFlags = computeFreezeFlags(
265 uFlags, bHigh, bNoFreeze, bSetFreeze, bClearFreeze, bSetDeepFreeze, bClearDeepFreeze);
266
267 auto const frozen = uFlags & (bHigh ? lsfHighFreeze : lsfLowFreeze);
268 auto const deepFrozen = uFlags & (bHigh ? lsfHighDeepFreeze : lsfLowDeepFreeze);
269
270 // Trying to set deep freeze on not already frozen trust line must
271 // fail. This also checks that clearing normal freeze while deep
272 // frozen must not work
273 if ((deepFrozen != 0u) && (frozen == 0u))
274 {
275 return tecNO_PERMISSION;
276 }
277 }
278
279 return tesSUCCESS;
280}
281
282TER
284{
285 TER terResult = tesSUCCESS;
286
287 STAmount const saLimitAmount(ctx_.tx.getFieldAmount(sfLimitAmount));
288 bool const bQualityIn(ctx_.tx.isFieldPresent(sfQualityIn));
289 bool const bQualityOut(ctx_.tx.isFieldPresent(sfQualityOut));
290
291 Currency const currency(saLimitAmount.get<Issue>().currency);
292 AccountID const uDstAccountID(saLimitAmount.getIssuer());
293
294 // true, if current is high account.
295 bool const bHigh = accountID_ > uDstAccountID;
296
297 auto const sle = view().peek(keylet::account(accountID_));
298 if (!sle)
299 return tefINTERNAL; // LCOV_EXCL_LINE
300
301 // The reserve that is required to create the line. Note
302 // that although the reserve increases with every item
303 // an account owns, in the case of trust lines we only
304 // *enforce* a reserve if the user owns more than two
305 // items.
306 //
307 // We do this because being able to exchange currencies,
308 // which needs trust lines, is a powerful XRPL feature.
309 // So we want to make it easy for a gateway to fund the
310 // accounts of its users without fear of being tricked.
311 //
312 // Without this logic, a gateway that wanted to have a
313 // new user use its services, would have to give that
314 // user enough XRP to cover not only the account reserve
315 // but the incremental reserve for the trust line as
316 // well. A person with no intention of using the gateway
317 // could use the extra XRP for their own purposes.
318
319 auto const sponsorExp = getTxReserveSponsor(ctx_.getApplyViewContext());
320 if (!sponsorExp)
321 return sponsorExp.error(); // LCOV_EXCL_LINE
322 auto const sponsorSle = *sponsorExp;
323
324 auto getSponsor = [&sponsorSle, this](AccountID const& account) {
325 return (sponsorSle && account == accountID_) ? sponsorSle : SLE::pointer();
326 };
327
328 // The "free-tier" shortcut (ownerCount < 2) only applies when there is no sponsor.
329 // With any sponsor on the tx, the sponsor must cover the reserve (via balance or
330 // prefunded budget), so the reserve check always runs.
331 bool const freeTrustLine = !sponsorSle && (ownerCount(sle, j_) < 2);
332 std::uint32_t const uOwnerCount = ownerCount(sle, j_);
333 XRPAmount const reserveCreate(
334 (uOwnerCount < 2) ? XRPAmount(beast::kZero)
335 : accountReserve(view(), sle, j_, {.ownerCountDelta = 1}));
336
337 std::uint32_t const uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0);
338 std::uint32_t uQualityOut(bQualityOut ? ctx_.tx.getFieldU32(sfQualityOut) : 0);
339
340 if (bQualityOut && QUALITY_ONE == uQualityOut)
341 uQualityOut = 0;
342
343 bool const bSetAuth = ctx_.tx.isFlag(tfSetfAuth);
344 bool const bSetNoRipple = ctx_.tx.isFlag(tfSetNoRipple);
345 bool const bClearNoRipple = ctx_.tx.isFlag(tfClearNoRipple);
346 bool const bSetFreeze = ctx_.tx.isFlag(tfSetFreeze);
347 bool const bClearFreeze = ctx_.tx.isFlag(tfClearFreeze);
348 bool const bSetDeepFreeze = ctx_.tx.isFlag(tfSetDeepFreeze);
349 bool const bClearDeepFreeze = ctx_.tx.isFlag(tfClearDeepFreeze);
350
351 auto viewJ = ctx_.registry.get().getJournal("View");
352
353 SLE::pointer const sleDst = view().peek(keylet::account(uDstAccountID));
354
355 if (!sleDst)
356 {
357 JLOG(j_.trace()) << "Delay transaction: Destination account does not exist.";
358 return tecNO_DST;
359 }
360
361 STAmount saLimitAllow = saLimitAmount;
362 saLimitAllow.get<Issue>().account = accountID_;
363
364 SLE::pointer const sleRippleState =
365 view().peek(keylet::trustLine(accountID_, uDstAccountID, currency));
366
367 if (sleRippleState)
368 {
369 STAmount saLowBalance;
370 STAmount saLowLimit;
371 STAmount saHighBalance;
372 STAmount saHighLimit;
373 std::uint32_t uLowQualityIn = 0;
374 std::uint32_t uLowQualityOut = 0;
375 std::uint32_t uHighQualityIn = 0;
376 std::uint32_t uHighQualityOut = 0;
377 auto const& uLowAccountID = !bHigh ? accountID_ : uDstAccountID;
378 auto const& uHighAccountID = bHigh ? accountID_ : uDstAccountID;
379 SLE::ref sleLowAccount = !bHigh ? sle : sleDst;
380 SLE::ref sleHighAccount = bHigh ? sle : sleDst;
381
382 //
383 // Balances
384 //
385
386 saLowBalance = sleRippleState->getFieldAmount(sfBalance);
387 saHighBalance = -saLowBalance;
388
389 //
390 // Limits
391 //
392
393 sleRippleState->setFieldAmount(!bHigh ? sfLowLimit : sfHighLimit, saLimitAllow);
394
395 saLowLimit = !bHigh ? saLimitAllow : sleRippleState->getFieldAmount(sfLowLimit);
396 saHighLimit = bHigh ? saLimitAllow : sleRippleState->getFieldAmount(sfHighLimit);
397
398 //
399 // Quality in
400 //
401
402 if (!bQualityIn)
403 {
404 // Not setting. Just get it.
405
406 uLowQualityIn = sleRippleState->getFieldU32(sfLowQualityIn);
407 uHighQualityIn = sleRippleState->getFieldU32(sfHighQualityIn);
408 }
409 else if (uQualityIn != 0u)
410 {
411 // Setting.
412
413 sleRippleState->setFieldU32(!bHigh ? sfLowQualityIn : sfHighQualityIn, uQualityIn);
414
415 uLowQualityIn = !bHigh ? uQualityIn : sleRippleState->getFieldU32(sfLowQualityIn);
416 uHighQualityIn = bHigh ? uQualityIn : sleRippleState->getFieldU32(sfHighQualityIn);
417 }
418 else
419 {
420 // Clearing.
421
422 sleRippleState->makeFieldAbsent(!bHigh ? sfLowQualityIn : sfHighQualityIn);
423
424 uLowQualityIn = !bHigh ? 0 : sleRippleState->getFieldU32(sfLowQualityIn);
425 uHighQualityIn = bHigh ? 0 : sleRippleState->getFieldU32(sfHighQualityIn);
426 }
427
428 if (QUALITY_ONE == uLowQualityIn)
429 uLowQualityIn = 0;
430
431 if (QUALITY_ONE == uHighQualityIn)
432 uHighQualityIn = 0;
433
434 //
435 // Quality out
436 //
437
438 if (!bQualityOut)
439 {
440 // Not setting. Just get it.
441
442 uLowQualityOut = sleRippleState->getFieldU32(sfLowQualityOut);
443 uHighQualityOut = sleRippleState->getFieldU32(sfHighQualityOut);
444 }
445 else if (uQualityOut != 0u)
446 {
447 // Setting.
448
449 sleRippleState->setFieldU32(!bHigh ? sfLowQualityOut : sfHighQualityOut, uQualityOut);
450
451 uLowQualityOut = !bHigh ? uQualityOut : sleRippleState->getFieldU32(sfLowQualityOut);
452 uHighQualityOut = bHigh ? uQualityOut : sleRippleState->getFieldU32(sfHighQualityOut);
453 }
454 else
455 {
456 // Clearing.
457
458 sleRippleState->makeFieldAbsent(!bHigh ? sfLowQualityOut : sfHighQualityOut);
459
460 uLowQualityOut = !bHigh ? 0 : sleRippleState->getFieldU32(sfLowQualityOut);
461 uHighQualityOut = bHigh ? 0 : sleRippleState->getFieldU32(sfHighQualityOut);
462 }
463
464 std::uint32_t const uFlagsIn(sleRippleState->getFieldU32(sfFlags));
465 std::uint32_t uFlagsOut(uFlagsIn);
466
467 if (bSetNoRipple && !bClearNoRipple)
468 {
469 if ((bHigh ? saHighBalance : saLowBalance) >= beast::kZero)
470 {
471 uFlagsOut |= (bHigh ? lsfHighNoRipple : lsfLowNoRipple);
472 }
473 else
474 {
475 // Cannot set noRipple on a negative balance.
476 return tecNO_PERMISSION;
477 }
478 }
479 else if (bClearNoRipple && !bSetNoRipple)
480 {
481 uFlagsOut &= ~(bHigh ? lsfHighNoRipple : lsfLowNoRipple);
482 }
483
484 // Have to use lsfNoFreeze to maintain pre-deep freeze behavior
485 bool const bNoFreeze = sle->isFlag(lsfNoFreeze);
486 uFlagsOut = computeFreezeFlags(
487 uFlagsOut,
488 bHigh,
489 bNoFreeze,
490 bSetFreeze,
491 bClearFreeze,
492 bSetDeepFreeze,
493 bClearDeepFreeze);
494
495 if (QUALITY_ONE == uLowQualityOut)
496 uLowQualityOut = 0;
497
498 if (QUALITY_ONE == uHighQualityOut)
499 uHighQualityOut = 0;
500
501 bool const bLowDefRipple = sleLowAccount->isFlag(lsfDefaultRipple);
502 bool const bHighDefRipple = sleHighAccount->isFlag(lsfDefaultRipple);
503
504 bool const bLowReserveSet = (uLowQualityIn != 0u) || (uLowQualityOut != 0u) ||
505 ((uFlagsOut & lsfLowNoRipple) == 0) != bLowDefRipple ||
506 ((uFlagsOut & lsfLowFreeze) != 0u) || saLowLimit || saLowBalance > beast::kZero;
507 bool const bLowReserveClear = !bLowReserveSet;
508
509 bool const bHighReserveSet = (uHighQualityIn != 0u) || (uHighQualityOut != 0u) ||
510 ((uFlagsOut & lsfHighNoRipple) == 0) != bHighDefRipple ||
511 ((uFlagsOut & lsfHighFreeze) != 0u) || saHighLimit || saHighBalance > beast::kZero;
512 bool const bHighReserveClear = !bHighReserveSet;
513
514 bool const bDefault = bLowReserveClear && bHighReserveClear;
515
516 bool const bLowReserved = sleRippleState->isFlag(lsfLowReserve);
517 bool const bHighReserved = sleRippleState->isFlag(lsfHighReserve);
518
519 bool bReserveIncrease = false;
520
521 auto const currentHighSponsor =
522 getLedgerEntryReserveSponsor(view(), sleRippleState, sfHighSponsor);
523 auto const currentLowSponsor =
524 getLedgerEntryReserveSponsor(view(), sleRippleState, sfLowSponsor);
525
526 if (bSetAuth)
527 {
528 uFlagsOut |= (bHigh ? lsfHighAuth : lsfLowAuth);
529 }
530
531 if (bLowReserveSet && !bLowReserved)
532 {
533 SLE::pointer const lowSponsor = getSponsor(uLowAccountID);
534
535 if (view().rules().enabled(featureSponsor))
536 {
537 if (auto const ret = checkReserve(
538 ctx_.getApplyViewContext(),
539 sleLowAccount,
541 lowSponsor,
542 {.ownerCountDelta = 1},
543 j_,
545 lowSponsor && !isTesSuccess(ret))
546 {
547 return ret;
548 }
549 }
550
551 // Set reserve for low account.
552 increaseOwnerCount(view(), sleLowAccount, lowSponsor, 1, viewJ);
553 uFlagsOut |= lsfLowReserve;
554
555 addSponsorToLedgerEntry(sleRippleState, lowSponsor, sfLowSponsor);
556
557 if (!bHigh)
558 bReserveIncrease = true;
559 }
560
561 if (bLowReserveClear && bLowReserved)
562 {
563 // Clear reserve for low account.
564 decreaseOwnerCount(view(), sleLowAccount, currentLowSponsor, 1, viewJ);
565 uFlagsOut &= ~lsfLowReserve;
566
567 removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsor);
568 }
569
570 if (bHighReserveSet && !bHighReserved)
571 {
572 SLE::pointer const highSponsor = getSponsor(uHighAccountID);
573
574 // should be checked PreFunded Sponsor before increaseOwnerCount()
575 // For PreFunded sponsors, we need to check if there are sufficient reserves before
576 // calling increaseOwnerCount().
577 if (view().rules().enabled(featureSponsor))
578 {
579 if (auto const ret = checkReserve(
580 ctx_.getApplyViewContext(),
581 sleHighAccount,
583 highSponsor,
584 {.ownerCountDelta = 1},
585 j_,
587 highSponsor && !isTesSuccess(ret))
588 {
589 return ret;
590 }
591 }
592
593 // Set reserve for high account.
594 increaseOwnerCount(view(), sleHighAccount, highSponsor, 1, viewJ);
595 uFlagsOut |= lsfHighReserve;
596
597 addSponsorToLedgerEntry(sleRippleState, highSponsor, sfHighSponsor);
598
599 if (bHigh)
600 bReserveIncrease = true;
601 }
602
603 if (bHighReserveClear && bHighReserved)
604 {
605 // Clear reserve for high account.
606 decreaseOwnerCount(view(), sleHighAccount, currentHighSponsor, 1, viewJ);
607 uFlagsOut &= ~lsfHighReserve;
608
609 removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsor);
610 }
611
612 if (uFlagsIn != uFlagsOut)
613 sleRippleState->setFieldU32(sfFlags, uFlagsOut);
614
615 if (view().rules().enabled(featureSponsor))
616 {
617 if (bDefault || badCurrency() == currency)
618 {
619 // Delete.
620
621 terResult =
622 trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ);
623 }
624 // Reserve is not scaled by load
625 else if (
626 auto const ret = checkReserve(
627 ctx_.getApplyViewContext(),
628 sle,
630 sponsorSle,
631 {},
632 j_,
634 !freeTrustLine && bReserveIncrease && !isTesSuccess(ret))
635 {
636 JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to "
637 "add trust line.";
638
639 // Another transaction could provide XRP to the account and then
640 // this transaction would succeed.
641 terResult = ret;
642 }
643 else
644 {
645 view().update(sleRippleState);
646
647 JLOG(j_.trace()) << "Modify ripple line";
648 }
649 }
650 else
651 {
652 if (bDefault || badCurrency() == currency)
653 {
654 // Delete.
655
656 terResult =
657 trustDelete(view(), sleRippleState, uLowAccountID, uHighAccountID, viewJ);
658 }
659 // Reserve is not scaled by load.
660 else if (bReserveIncrease && preFeeBalance_ < reserveCreate)
661 {
662 JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to "
663 "add trust line.";
664
665 // Another transaction could provide XRP to the account and then
666 // this transaction would succeed.
667 terResult = tecINSUF_RESERVE_LINE;
668 }
669 else
670 {
671 view().update(sleRippleState);
672
673 JLOG(j_.trace()) << "Modify ripple line";
674 }
675 }
676 }
677 // Line does not exist.
678 else if (
679 !saLimitAmount && // Setting default limit.
680 (!bQualityIn || (uQualityIn == 0u)) && // Not setting quality in or
681 // setting default quality in.
682 (!bQualityOut || (uQualityOut == 0u)) && // Not setting quality out or
683 // setting default quality out.
684 (!bSetAuth))
685 {
686 JLOG(j_.trace()) << "Redundant: Setting non-existent ripple line to defaults.";
688 }
689 // reserve is not scaled by load
690 else if (!view().rules().enabled(featureSponsor) && preFeeBalance_ < reserveCreate)
691 {
692 JLOG(j_.trace()) << "Delay transaction: Line does not exist. "
693 "Insufficent reserve to create line.";
694
695 // Another transaction could create the account and then this
696 // transaction would succeed.
697 terResult = tecNO_LINE_INSUF_RESERVE;
698 }
699 else if (
700 auto const ret = checkReserve(
701 ctx_.getApplyViewContext(),
702 sle,
704 sponsorSle,
705 {.ownerCountDelta = 1},
706 j_,
708 view().rules().enabled(featureSponsor) && !freeTrustLine &&
709 !isTesSuccess(ret)) // Reserve is not scaled by load.
710 {
711 JLOG(j_.trace()) << "Delay transaction: Line does not exist. "
712 "Insufficent reserve to create line.";
713
714 // Another transaction could create the account and then this
715 // transaction would succeed.
716 terResult = ret;
717 }
718 else
719 {
720 // Zero balance in currency.
721 STAmount const saBalance(Issue{currency, noAccount()});
722
723 auto const k = keylet::trustLine(accountID_, uDstAccountID, currency);
724
725 JLOG(j_.trace()) << "doTrustSet: Creating ripple line: " << to_string(k.key);
726
727 // Create a new ripple line.
728 terResult = trustCreate(
729 view(),
730 bHigh,
732 uDstAccountID,
733 k.key,
734 sle,
735 bSetAuth,
736 bSetNoRipple && !bClearNoRipple,
737 bSetFreeze && !bClearFreeze,
738 bSetDeepFreeze,
739 saBalance,
740 saLimitAllow, // Limit for who is being charged.
741 uQualityIn,
742 uQualityOut,
743 sponsorSle,
744 viewJ);
745 }
746
747 return terResult;
748}
749
750void
752{
753 // No transaction-specific invariants yet (future work).
754}
755
756bool
758{
759 // No transaction-specific invariants yet (future work).
760 return true;
761}
762
763} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream trace() const
Severity stream access functions.
Definition Journal.h:338
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
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 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:180
constexpr TIss const & get() const
std::string getFullText() const override
Definition STAmount.cpp:636
bool native() const noexcept
Definition STAmount.h:471
AccountID const & getIssuer() const
Definition STAmount.h:516
std::shared_ptr< STLedgerEntry > const & ref
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFlag(std::uint32_t) const
Definition STObject.cpp:511
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:657
beast::Journal const j_
Definition Transactor.h:155
ApplyView & view()
Definition Transactor.h:175
AccountID const accountID_
Definition Transactor.h:157
XRPAmount preFeeBalance_
Definition Transactor.h:158
ApplyContext & ctx_
Definition Transactor.h:153
static TER preclaim(PreclaimContext const &ctx)
Definition TrustSet.cpp:156
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition TrustSet.cpp:68
TER doApply() override
Definition TrustSet.cpp:283
static NotTEC checkGranularSemantics(ReadView const &view, STTx const &tx, std::unordered_set< GranularPermissionType > const &heldGranularPermissions)
Definition TrustSet.cpp:126
static NotTEC preflight(PreflightContext const &ctx)
Definition TrustSet.cpp:74
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
Definition TrustSet.cpp:751
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
Definition TrustSet.cpp:757
constexpr Zero kZero
Definition Zero.h:30
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_DELEGATE_PERMISSION
Definition TER.h:226
@ terNO_ACCOUNT
Definition TER.h:213
bool ammEnabled(Rules const &)
Return true if required AMM amendment is enabled.
Definition AMMCore.cpp:129
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.
@ tefNO_AUTH_REQUIRED
Definition TER.h:166
@ tefINTERNAL
Definition TER.h:165
bool isLegalNet(STAmount const &value)
Definition STAmount.h:616
BaseUInt< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:42
TER trustDelete(ApplyView &view, SLE::ref sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
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::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
std::expected< SLE::pointer, TER > getTxReserveSponsor(ApplyViewContext ctx)
Return a mutable SLE for the transaction's reserve sponsor account.
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.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:607
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.
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.
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.
@ temBAD_CURRENCY
Definition TER.h:78
@ temINVALID_FLAG
Definition TER.h:99
@ temDST_IS_SRC
Definition TER.h:96
@ temDST_NEEDED
Definition TER.h:97
@ temBAD_LIMIT
Definition TER.h:82
@ temBAD_AMOUNT
Definition TER.h:77
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
@ tecPSEUDO_ACCOUNT
Definition TER.h:365
@ tecAMM_EMPTY
Definition TER.h:335
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:295
@ tecINSUF_RESERVE_LINE
Definition TER.h:291
@ tecINTERNAL
Definition TER.h:313
@ tecNO_LINE_REDUNDANT
Definition TER.h:296
@ tecNO_PERMISSION
Definition TER.h:308
@ tecNO_DST
Definition TER.h:293
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.
bool isPseudoAccount(SLE::const_pointer sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
std::uint32_t ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj=0)
Return number of the objects which reserve is covered by the account(sle) (so called "ownercount").
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
XRPAmount accountReserve(ReadView const &view, SLE::const_ref sle, beast::Journal j, Adjustment adj={})
Returns the account reserve, in drops.
@ tesSUCCESS
Definition TER.h:245
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:83
ReadView const & view
Definition Transactor.h:86
beast::Journal const j
Definition Transactor.h:91
State information when preflighting a tx.
Definition Transactor.h:38
beast::Journal const j
Definition Transactor.h:45