xrpld
Loading...
Searching...
No Matches
SponsorshipTransfer.cpp
1#include <xrpl/tx/transactors/sponsor/SponsorshipTransfer.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/beast/utility/instrumentation.h>
7#include <xrpl/core/ServiceRegistry.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/SponsorHelpers.h>
12#include <xrpl/protocol/Feature.h>
13#include <xrpl/protocol/Indexes.h>
14#include <xrpl/protocol/SField.h>
15#include <xrpl/protocol/TER.h>
16#include <xrpl/protocol/TxFlags.h>
17#include <xrpl/protocol/XRPAmount.h>
18#include <xrpl/tx/Transactor.h>
19
20#include <bit>
21#include <cstdint>
22#include <limits>
23#include <memory>
24
25namespace xrpl {
26
27// Increment an uint32 sponsor count field and update the SLE.
28static TER
30 ApplyView& view,
31 SLE::Ref sle,
32 SF_UINT32 const& field,
33 std::uint32_t const delta)
34{
35 auto const currentValue = sle->getFieldU32(field);
36 if (std::numeric_limits<std::uint32_t>::max() - currentValue < delta)
37 {
38 // LCOV_EXCL_START
39 UNREACHABLE("xrpl::incrementSponsorCount : sponsor field overflow");
40 return tecINTERNAL;
41 // LCOV_EXCL_STOP
42 }
43
44 sle->at(field) = currentValue + delta;
45 view.update(sle);
46 return tesSUCCESS;
47}
48
49// Decrement an uint32 sponsor count field and update the SLE.
50static TER
52 ApplyView& view,
53 SLE::Ref sle,
54 SF_UINT32 const& field,
55 std::uint32_t const delta)
56{
57 auto const currentValue = sle->getFieldU32(field);
58 if (currentValue < delta)
59 {
60 // LCOV_EXCL_START
61 UNREACHABLE("xrpl::decrementSponsorCount : sponsor field underflow");
62 return tecINTERNAL;
63 // LCOV_EXCL_STOP
64 }
65
66 sle->at(field) = currentValue - delta;
67 view.update(sle);
68 return tesSUCCESS;
69}
70
71// Consume the sponsor's pre-funded reserve budget and lowers the Sponsorship
72// object's RemainingOwnerCount.
73static TER
75{
76 if (delta == 0)
77 return tesSUCCESS; // LCOV_EXCL_LINE
78
79 auto const currentReserveCount = sponsorshipSle->getFieldU32(sfRemainingOwnerCount);
80 if (currentReserveCount < delta)
81 {
82 // LCOV_EXCL_START
83 // Already verified by checkReserve (sufficient RemainingOwnerCount)
84 UNREACHABLE("xrpl::decrementPrefundedReserveCount : invalid reserve count");
85 return tefINTERNAL;
86 // LCOV_EXCL_STOP
87 }
88
89 sponsorshipSle->at(sfRemainingOwnerCount) = currentReserveCount - delta;
90 view.update(sponsorshipSle);
91 return tesSUCCESS;
92}
93
96{
97 return tfSponsorshipTransferMask;
98}
99
100NotTEC
102{
103 static constexpr auto transferFlags =
104 tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd;
105 if (std::popcount(ctx.tx.getFlags() & transferFlags) != 1)
106 {
107 JLOG(ctx.j.debug()) << "preflight: Only one SponsorshipTransfer flag can be set per tx.";
108 return temINVALID_FLAG;
109 }
110
111 if (ctx.tx.isFlag(tfSponsorshipCreate))
112 {
113 // Creating sponsorship transfers an unsponsored target from the sponsee
114 // to a reserve sponsor identified by sfSponsor + spfSponsorReserve.
115 if (!ctx.tx.isFieldPresent(sfSponsor))
116 {
117 JLOG(ctx.j.debug()) << "preflight: sfSponsor must be present when creating sponsorship";
118 return temMALFORMED;
119 }
120
121 if (!isReserveSponsored(ctx.tx))
122 {
123 JLOG(ctx.j.debug())
124 << "preflight: spfSponsorReserve must be set when creating sponsorship";
125 return temINVALID_FLAG;
126 }
127
128 if (ctx.tx.isFieldPresent(sfSponsee))
129 {
130 JLOG(ctx.j.debug())
131 << "preflight: sfSponsee must not be present when creating sponsorship";
132 return temMALFORMED;
133 }
134 }
135
136 if (ctx.tx.isFlag(tfSponsorshipReassign))
137 {
138 // Reassigning sponsorship transfers an already sponsored target from its
139 // current reserve sponsor to the new sponsor identified by sfSponsor +
140 // spfSponsorReserve.
141 if (!ctx.tx.isFieldPresent(sfSponsor))
142 {
143 JLOG(ctx.j.debug())
144 << "preflight: sfSponsor must be present when reassigning sponsorship";
145 return temMALFORMED;
146 }
147
148 if (!isReserveSponsored(ctx.tx))
149 {
150 JLOG(ctx.j.debug())
151 << "preflight: spfSponsorReserve must be set when reassigning sponsorship";
152 return temINVALID_FLAG;
153 }
154 if (ctx.tx.isFieldPresent(sfSponsee))
155 {
156 JLOG(ctx.j.debug())
157 << "preflight: sfSponsee must not be present when reassigning sponsorship";
158 return temMALFORMED;
159 }
160 }
161
162 if (ctx.tx.isFlag(tfSponsorshipEnd))
163 {
164 // Ending sponsorship removes reserve sponsorship from a sponsored target.
165 // The target is sfSponsee when provided; otherwise it is sfAccount.
166 if (ctx.tx.isFieldPresent(sfSponsor))
167 {
168 JLOG(ctx.j.debug())
169 << "preflight: sfSponsor must not be present when ending sponsorship";
170 return temMALFORMED;
171 }
172
173 // sfSponsorFlags should not be present if it is ending sponsorship.
174 if (ctx.tx.isFieldPresent(sfSponsorFlags))
175 {
176 // Unreachable: reaching here means sfSponsor is absent, which is already checked above,
177 // and preflight1Sponsor already rejects sfSponsorFlags present without sfSponsor with
178 // temINVALID_FLAG. Keep this as a defensive check.
179 // LCOV_EXCL_START
180 UNREACHABLE(
181 "xrpl::SponsorshipTransfer::preflight : sfSponsorFlags present without sfSponsor "
182 "when ending sponsorship");
183 return temINVALID_FLAG;
184 // LCOV_EXCL_STOP
185 }
186
187 if (ctx.tx.isFieldPresent(sfSponsee) &&
188 ctx.tx.getAccountID(sfSponsee) == ctx.tx.getAccountID(sfAccount))
189 {
190 JLOG(ctx.j.debug()) << "preflight: sfSponsee should not be the same as the account";
191 return temMALFORMED;
192 }
193 }
194
195 // Account-level reserve sponsorship changes the reserve responsibility for
196 // the account itself, so the new sponsor must explicitly co-sign. Object-level
197 // sponsorship may use pre-funded reserve sponsorship instead.
198 bool const isCreateOrReassign =
199 ctx.tx.isFlag(tfSponsorshipCreate) || ctx.tx.isFlag(tfSponsorshipReassign);
200 auto const reserveSponsor = getTxReserveSponsorID(ctx.tx);
201 bool const isAccountReserveSponsorship =
202 isCreateOrReassign && reserveSponsor && !ctx.tx.isFieldPresent(sfObjectID);
203
204 // A proposed SponsorshipTransfer is stored unsigned; its SponsorSignature
205 // is collected on-ledger afterward, so its absence here is expected, not
206 // an error (On-Chain Cosigner spec ยง5.3.1.2).
207 if (isAccountReserveSponsorship && !ctx.tx.isFieldPresent(sfSponsorSignature) &&
208 (ctx.flags & TapProposal) == 0)
209 {
210 JLOG(ctx.j.debug()) << "preflight: account sponsorship requires sfSponsorSignature";
211 return temMALFORMED;
212 }
213
214 if (auto const objectID = ctx.tx[~sfObjectID];
215 ctx.rules.enabled(fixCleanup3_5_0) && objectID && *objectID == beast::kZero)
216 {
217 JLOG(ctx.j.debug()) << "preflight: sfObjectID must not be zero";
218 return temMALFORMED;
219 }
220
221 return tesSUCCESS;
222}
223
224TER
226{
227 auto const objectID = ctx.tx[~sfObjectID];
228 auto const newSponsorSleExpected = getTxReserveSponsor(ctx.view, ctx.tx);
229 if (!newSponsorSleExpected)
230 return newSponsorSleExpected.error(); // LCOV_EXCL_LINE
231 auto const newSponsorSle = *newSponsorSleExpected;
232
233 auto const account = ctx.tx[sfAccount];
234 auto const sponseeID = ctx.tx[~sfSponsee].value_or(account);
235 auto const sponseeSle = ctx.view.read(keylet::account(sponseeID));
236 if (!sponseeSle)
237 {
238 // If it is ending sponsorship, sfSponsee is user input, return terNO_ACCOUNT if it does not
239 // exist.
240 if (ctx.tx.isFieldPresent(sfSponsee))
241 return terNO_ACCOUNT;
242
243 // If it is creating or reassigning sponsorship, sfSponsee is the account itself, which is
244 // always present by the time preclaim runs. Return tecINTERNAL if it does not exist.
245 return tecINTERNAL; // LCOV_EXCL_LINE
246 }
247
248 // Default setup with an account sponsorship transfer. If it is an object transfer, they will be
249 // overridden to the object SLE and its type-specific sponsor field:
250 // sfHighSponsor/sfLowSponsor for a RippleState, sfSponsor for other object types.
251 SLE::const_pointer targetSle = sponseeSle;
252 auto const* sponsorField = &sfSponsor;
253
254 if (objectID.has_value())
255 {
256 auto const objectSle = ctx.view.read(keylet::unchecked(*objectID));
257 if (!objectSle)
258 return tecNO_ENTRY;
259
261 return tecNO_PERMISSION;
262
263 if (!isLedgerEntryOwner(ctx.view, *objectSle, sponseeID))
264 return tecNO_PERMISSION;
265
266 // Object transfer: the target is the object, and its sponsor field
267 // depends on the object type, a RippleState stores the sponsor in
268 // sfHighSponsor/sfLowSponsor, while other object type uses sfSponsor.
269 targetSle = objectSle;
270 sponsorField = &getLedgerEntrySponsorField(*objectSle, sponseeID);
271 }
272
273 bool const isSponsored = targetSle->isFieldPresent(*sponsorField);
274
275 if (ctx.tx.isFlag(tfSponsorshipCreate))
276 {
277 // Creating a new sponsorship: needs a new reserve sponsor, and the
278 // target must not already be sponsored
279 if (!newSponsorSle || isSponsored)
280 return tecNO_PERMISSION;
281 }
282 else if (ctx.tx.isFlag(tfSponsorshipReassign))
283 {
284 // Reassigning sponsorship: needs a new reserve sponsor, and the target must already
285 // be sponsored
286 if (!newSponsorSle || !isSponsored)
287 return tecNO_PERMISSION;
288
289 // Reassigning to the current sponsor would change no state, but would
290 // still draw down the sponsor's pre-funded reserve budget (and its
291 // reserve headroom would be double-counted in checkReserve).
292 if (targetSle->getAccountID(*sponsorField) == ctx.tx.getAccountID(sfSponsor))
293 return tecNO_PERMISSION;
294 }
295 else if (ctx.tx.isFlag(tfSponsorshipEnd))
296 {
297 // Ending sponsorship: no new reserve sponsor, the target must be sponsored.
298 if (newSponsorSle || !isSponsored)
299 return tecNO_PERMISSION;
300
301 // Only the sponsor or sponsee can end sponsorship.
302 auto const sponsor = targetSle->getAccountID(*sponsorField);
303 if (account != sponsor && account != sponseeID)
304 return tecNO_PERMISSION;
305 }
306
307 return tesSUCCESS;
308}
309
310TER
312{
313 auto const objectID = ctx_.tx[~sfObjectID];
314
315 auto const sponseeID = ctx_.tx[~sfSponsee].value_or(accountID_);
316 auto const sponseeSle = view().peek(keylet::account(sponseeID));
317 if (!sponseeSle)
318 return tefINTERNAL; // LCOV_EXCL_LINE
319
320 auto const balanceBeforeFee = [&](SLE::ConstRef sle) -> XRPAmount {
321 if (sle->getAccountID(sfAccount) == accountID_)
322 return preFeeBalance_;
323 return sle->getFieldAmount(sfBalance).xrp();
324 };
325
326 bool const isCreate = ctx_.tx.isFlag(tfSponsorshipCreate);
327 bool const isReassign = ctx_.tx.isFlag(tfSponsorshipReassign);
328
329 if (objectID.has_value())
330 {
331 // Transfer object sponsor
332 auto const objectSle = view().peek(keylet::unchecked(*objectID));
333 if (!objectSle)
334 return tefINTERNAL; // LCOV_EXCL_LINE
335
336 // preclaim established that the sponsee owns the object.
337 if (!isLedgerEntryOwner(view(), *objectSle, sponseeID))
338 return tefINTERNAL; // LCOV_EXCL_LINE
339
340 auto const ownerSle = view().peek(keylet::account(sponseeID));
341 if (!ownerSle)
342 return tefINTERNAL; // LCOV_EXCL_LINE
343
344 auto const ownerCountDelta =
345 static_cast<std::int32_t>(getLedgerEntryOwnerCount(*objectSle));
346 auto const& sponsorField = getLedgerEntrySponsorField(*objectSle, sponseeID);
347
348 if (isCreate || isReassign)
349 {
350 auto const newSponsor = ctx_.tx[~sfSponsor];
351 XRPL_ASSERT(
352 newSponsor.has_value(),
353 "xrpl::SponsorshipTransfer::doApply : sfSponsor present for object sponsor "
354 "create/reassign");
355 if (!newSponsor)
356 return tefINTERNAL; // LCOV_EXCL_LINE
357 auto const newSponsorID = *newSponsor;
358 auto const newSponsorSle = view().peek(keylet::account(newSponsorID));
359 if (!newSponsorSle)
360 return tefINTERNAL; // LCOV_EXCL_LINE
361
362 // Check if new sponsor has sufficient balance
363 // NOLINTNEXTLINE(readability-suspicious-call-argument)
364 if (auto const ter = checkReserve(
365 ctx_.getApplyViewContext(),
366 sponseeSle,
367 sponseeSle->getFieldAmount(sfBalance).xrp(),
368 newSponsorSle,
369 {.ownerCountDelta = ownerCountDelta},
370 ctx_.journal);
371 !isTesSuccess(ter))
372 return ter;
373
374 if (isCreate)
375 {
376 // Update owner's sponsored count
377 if (auto const ter = incrementSponsorCount(
378 view(), ownerSle, sfSponsoredOwnerCount, ownerCountDelta);
379 !isTesSuccess(ter))
380 return ter; // LCOV_EXCL_LINE
381 }
382 else if (isReassign)
383 {
384 auto const oldSponsorID = objectSle->getAccountID(sponsorField);
385 if (!oldSponsorID)
386 return tefINTERNAL; // LCOV_EXCL_LINE
387 auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
388 if (!oldSponsorSle)
389 return tefINTERNAL; // LCOV_EXCL_LINE
390
391 // Decrement old sponsor's sponsoring count
392 if (auto const ter = decrementSponsorCount(
393 view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
394 !isTesSuccess(ter))
395 return ter; // LCOV_EXCL_LINE
396 }
397
398 // Increment new sponsor's sponsoring count
399 if (auto const ter = incrementSponsorCount(
400 view(), newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
401 !isTesSuccess(ter))
402 return ter; // LCOV_EXCL_LINE
403
404 // Object is now sponsored by new sponsor
405 objectSle->setAccountID(sponsorField, newSponsorID);
406 view().update(objectSle);
407
408 auto const sponsorshipSle = view().peek(keylet::sponsorship(newSponsorID, sponseeID));
409 if (sponsorshipSle)
410 {
411 // Update ReserveCount for sponsorship object if it exists
412 if (auto const ter =
413 decrementPrefundedReserveCount(view(), sponsorshipSle, ownerCountDelta);
414 !isTesSuccess(ter))
415 return ter; // LCOV_EXCL_LINE
416 }
417 }
418 else if (ctx_.tx.isFlag(tfSponsorshipEnd))
419 {
420 // End object sponsor
421 auto const oldSponsorID = objectSle->getAccountID(sponsorField);
422 if (!oldSponsorID)
423 return tefINTERNAL; // LCOV_EXCL_LINE
424 auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
425 if (!oldSponsorSle)
426 return tefINTERNAL; // LCOV_EXCL_LINE
427
428 // The owner reclaims the reserve burden when the object is no longer
429 // sponsored, so it must be able to hold that reserve on its own once the
430 // sponsorship is removed. This mirrors the account-level End check below,
431 // keeping the behavior consistent across accounts and objects: a
432 // sponsorship can only be ended if the sponsee self-funds, another sponsor
433 // steps in (Reassign), or the object/account is deleted.
434 if (view().rules().enabled(fixCleanup3_4_0))
435 {
436 if (auto const ter = checkReserve(
437 ctx_.getApplyViewContext(),
438 sponseeSle,
439 balanceBeforeFee(sponseeSle),
440 SLE::pointer(),
441 {.ownerCountDelta = ownerCountDelta},
442 ctx_.journal);
443 !isTesSuccess(ter))
444 return ter;
445 }
446
447 // Decrement sponsored count
448 if (auto const ter = decrementSponsorCount(
449 view(), sponseeSle, sfSponsoredOwnerCount, ownerCountDelta);
450 !isTesSuccess(ter))
451 return ter; // LCOV_EXCL_LINE
452
453 // Decrement old sponsoring count
454 if (auto const ter = decrementSponsorCount(
455 view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
456 !isTesSuccess(ter))
457 return ter; // LCOV_EXCL_LINE
458
459 // Remove sponsor from object
460 objectSle->makeFieldAbsent(sponsorField);
461 view().update(objectSle);
462 }
463 }
464 else
465 {
466 // Account-level sponsorship is always co-signed (preflight requires
467 // sfSponsorSignature), so there is no pre-funded budget to draw down here.
468 if (isCreate || isReassign)
469 {
470 auto const newSponsor = ctx_.tx[~sfSponsor];
471 XRPL_ASSERT(
472 newSponsor.has_value(),
473 "xrpl::SponsorshipTransfer::doApply : sfSponsor present for account sponsor "
474 "create/reassign");
475 if (!newSponsor)
476 return tefINTERNAL; // LCOV_EXCL_LINE
477 auto const newSponsorID = *newSponsor;
478 auto const newSponsorSle = view().peek(keylet::account(newSponsorID));
479 if (!newSponsorSle)
480 return tefINTERNAL; // LCOV_EXCL_LINE
481
482 if (auto const ter = checkReserve(
483 ctx_.getApplyViewContext(),
484 sponseeSle,
485 sponseeSle->getFieldAmount(sfBalance).xrp(),
486 newSponsorSle,
487 {.accountCountDelta = 1},
488 ctx_.journal);
489 !isTesSuccess(ter))
490 return ter;
491
492 if (isReassign)
493 {
494 auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
495 if (!oldSponsorID)
496 return tefINTERNAL; // LCOV_EXCL_LINE
497 auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
498 if (!oldSponsorSle)
499 return tefINTERNAL; // LCOV_EXCL_LINE
500
501 // Decrement old sponsoring count
502 if (auto const ter =
503 decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1);
504 !isTesSuccess(ter))
505 return ter; // LCOV_EXCL_LINE
506 }
507
508 // Increment new sponsoring count
509 if (auto const ter =
510 incrementSponsorCount(view(), newSponsorSle, sfSponsoringAccountCount, 1);
511 !isTesSuccess(ter))
512 return ter; // LCOV_EXCL_LINE
513
514 // Account is now sponsored by new sponsor
515 sponseeSle->setAccountID(sfSponsor, newSponsorID);
516 view().update(sponseeSle);
517 }
518 else if (ctx_.tx.isFlag(tfSponsorshipEnd))
519 {
520 // End account sponsor
521 auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
522 if (!oldSponsorID)
523 return tefINTERNAL; // LCOV_EXCL_LINE
524 auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
525 if (!oldSponsorSle)
526 return tefINTERNAL; // LCOV_EXCL_LINE
527
528 // The sponsee must be able to hold its own account reserve after
529 // the sponsorship is removed.
530 if (auto const ter = checkReserve(
531 ctx_.getApplyViewContext(),
532 sponseeSle,
533 balanceBeforeFee(sponseeSle),
534 SLE::pointer(),
535 {.accountCountDelta = 1},
536 ctx_.journal);
537 !isTesSuccess(ter))
538 return ter;
539
540 sponseeSle->makeFieldAbsent(sfSponsor);
541 view().update(sponseeSle);
542
543 // Decrement account sponsoring count
544 if (auto const ter =
545 decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1);
546 !isTesSuccess(ter))
547 return ter; // LCOV_EXCL_LINE
548 }
549 }
550
551 return tesSUCCESS;
552}
553
554void
558
559bool
561 STTx const&,
562 TER,
563 XRPAmount,
564 ReadView const&,
565 beast::Journal const&)
566{
567 return true;
568}
569
570} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream debug() const
Definition Journal.h:344
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 update(SLE::Ref sle)=0
Indicate changes to a peeked SLE.
A view into a ledger.
Definition ReadView.h:41
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
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & ConstRef
std::shared_ptr< STLedgerEntry > const & Ref
std::shared_ptr< STLedgerEntry const > const_pointer
bool isFlag(std::uint32_t) const
Definition STObject.cpp:511
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:643
std::uint32_t getFlags() const
Definition STObject.cpp:517
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
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.
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
ApplyView & view()
Definition Transactor.h:184
AccountID const accountID_
Definition Transactor.h:166
XRPAmount preFeeBalance_
Definition Transactor.h:167
ApplyContext & ctx_
Definition Transactor.h:162
T max(T... args)
constexpr Zero kZero
Definition Zero.h:30
Keylet unchecked(UInt256 const &key) noexcept
Any ledger entry.
Definition Indexes.cpp:397
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
Keylet sponsorship(AccountID const &sponsor, AccountID const &sponsee) noexcept
A Sponsorship.
Definition Indexes.cpp:354
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_ACCOUNT
Definition TER.h:218
static TER decrementSponsorCount(ApplyView &view, SLE::Ref sle, SF_UINT32 const &field, std::uint32_t const delta)
static TER incrementSponsorCount(ApplyView &view, SLE::Ref sle, SF_UINT32 const &field, std::uint32_t const delta)
@ tefINTERNAL
Definition TER.h:168
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.
TypedField< STInteger< std::uint32_t > > SF_UINT32
Definition SField.h:344
bool isReserveSponsored(STTx const &tx)
Whether the transaction's reserve is sponsored (sfSponsor present + spfSponsorReserve set).
std::expected< SLE::pointer, TER > getTxReserveSponsor(ApplyViewContext ctx)
Return a mutable SLE for the transaction's reserve sponsor account.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:614
static TER decrementPrefundedReserveCount(ApplyView &view, SLE::Ref sponsorshipSle, std::uint32_t const delta)
std::uint32_t getLedgerEntryOwnerCount(SLE const &sle)
Return the number of owner-count units the ledger entry consumes.
@ TapProposal
Definition ApplyView.h:53
@ temINVALID_FLAG
Definition TER.h:99
@ temMALFORMED
Definition TER.h:75
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecNO_ENTRY
Definition TER.h:314
@ tecINTERNAL
Definition TER.h:318
@ tecNO_PERMISSION
Definition TER.h:313
std::optional< AccountID > getTxReserveSponsorID(STTx const &tx)
Return the AccountID of the transaction's reserve sponsor, or nullopt if unsponsored.
bool isLedgerEntryOwner(ReadView const &view, SLE const &sle, AccountID const &account)
Whether account is the owner of a ledger entry for sponsorship purposes.
@ tesSUCCESS
Definition TER.h:250
SF_ACCOUNT const & getLedgerEntrySponsorField(SLE const &sle, AccountID const &owner)
Return the SField used to store the reserve sponsor for owner on sle.
bool isLedgerEntrySupportedBySponsorship(SLE const &sle)
Whether this ledger entry type can have a reserve sponsor attached to it.
T popcount(T... args)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:92
ReadView const & view
Definition Transactor.h:95
State information when preflighting a tx.
Definition Transactor.h:39
beast::Journal const j
Definition Transactor.h:46