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