1#include <xrpl/tx/transactors/sponsor/SponsorshipTransfer.h>
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>
35 auto const currentValue = sle->getFieldU32(field);
39 UNREACHABLE(
"xrpl::incrementSponsorCount : sponsor field overflow");
44 sle->at(field) = currentValue + delta;
57 auto const currentValue = sle->getFieldU32(field);
58 if (currentValue < delta)
61 UNREACHABLE(
"xrpl::decrementSponsorCount : sponsor field underflow");
66 sle->at(field) = currentValue - delta;
79 auto const currentReserveCount = sponsorshipSle->getFieldU32(sfRemainingOwnerCount);
80 if (currentReserveCount < delta)
84 UNREACHABLE(
"xrpl::decrementPrefundedReserveCount : invalid reserve count");
89 sponsorshipSle->at(sfRemainingOwnerCount) = currentReserveCount - delta;
90 view.
update(sponsorshipSle);
97 return tfSponsorshipTransferMask;
103 static constexpr auto transferFlags =
104 tfSponsorshipCreate | tfSponsorshipReassign | tfSponsorshipEnd;
107 JLOG(ctx.
j.
debug()) <<
"preflight: Only one SponsorshipTransfer flag can be set per tx.";
111 if (ctx.
tx.
isFlag(tfSponsorshipCreate))
117 JLOG(ctx.
j.
debug()) <<
"preflight: sfSponsor must be present when creating sponsorship";
124 <<
"preflight: spfSponsorReserve must be set when creating sponsorship";
131 <<
"preflight: sfSponsee must not be present when creating sponsorship";
136 if (ctx.
tx.
isFlag(tfSponsorshipReassign))
144 <<
"preflight: sfSponsor must be present when reassigning sponsorship";
151 <<
"preflight: spfSponsorReserve must be set when reassigning sponsorship";
157 <<
"preflight: sfSponsee must not be present when reassigning sponsorship";
162 if (ctx.
tx.
isFlag(tfSponsorshipEnd))
169 <<
"preflight: sfSponsor must not be present when ending sponsorship";
181 "xrpl::SponsorshipTransfer::preflight : sfSponsorFlags present without sfSponsor "
182 "when ending sponsorship");
190 JLOG(ctx.
j.
debug()) <<
"preflight: sfSponsee should not be the same as the account";
198 bool const isCreateOrReassign =
201 bool const isAccountReserveSponsorship =
207 if (isAccountReserveSponsorship && !ctx.
tx.
isFieldPresent(sfSponsorSignature) &&
210 JLOG(ctx.
j.
debug()) <<
"preflight: account sponsorship requires sfSponsorSignature";
214 if (
auto const objectID = ctx.
tx[~sfObjectID];
217 JLOG(ctx.
j.
debug()) <<
"preflight: sfObjectID must not be zero";
227 auto const objectID = ctx.
tx[~sfObjectID];
229 if (!newSponsorSleExpected)
230 return newSponsorSleExpected.error();
231 auto const newSponsorSle = *newSponsorSleExpected;
233 auto const account = ctx.
tx[sfAccount];
234 auto const sponseeID = ctx.
tx[~sfSponsee].value_or(account);
252 auto const* sponsorField = &sfSponsor;
254 if (objectID.has_value())
269 targetSle = objectSle;
273 bool const isSponsored = targetSle->isFieldPresent(*sponsorField);
275 if (ctx.
tx.
isFlag(tfSponsorshipCreate))
279 if (!newSponsorSle || isSponsored)
282 else if (ctx.
tx.
isFlag(tfSponsorshipReassign))
286 if (!newSponsorSle || !isSponsored)
292 if (targetSle->getAccountID(*sponsorField) == ctx.
tx.
getAccountID(sfSponsor))
295 else if (ctx.
tx.
isFlag(tfSponsorshipEnd))
298 if (newSponsorSle || !isSponsored)
302 auto const sponsor = targetSle->getAccountID(*sponsorField);
303 if (account != sponsor && account != sponseeID)
313 auto const objectID =
ctx_.tx[~sfObjectID];
321 if (sle->getAccountID(sfAccount) ==
accountID_)
323 return sle->getFieldAmount(sfBalance).xrp();
326 bool const isCreate =
ctx_.tx.isFlag(tfSponsorshipCreate);
327 bool const isReassign =
ctx_.tx.isFlag(tfSponsorshipReassign);
329 if (objectID.has_value())
344 auto const ownerCountDelta =
348 if (isCreate || isReassign)
350 auto const newSponsor =
ctx_.tx[~sfSponsor];
352 newSponsor.has_value(),
353 "xrpl::SponsorshipTransfer::doApply : sfSponsor present for object sponsor "
357 auto const newSponsorID = *newSponsor;
365 ctx_.getApplyViewContext(),
367 sponseeSle->getFieldAmount(sfBalance).xrp(),
369 {.ownerCountDelta = ownerCountDelta},
378 view(), ownerSle, sfSponsoredOwnerCount, ownerCountDelta);
384 auto const oldSponsorID = objectSle->getAccountID(sponsorField);
393 view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
400 view(), newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
405 objectSle->setAccountID(sponsorField, newSponsorID);
418 else if (
ctx_.tx.isFlag(tfSponsorshipEnd))
421 auto const oldSponsorID = objectSle->getAccountID(sponsorField);
434 if (
view().rules().enabled(fixCleanup3_4_0))
437 ctx_.getApplyViewContext(),
439 balanceBeforeFee(sponseeSle),
441 {.ownerCountDelta = ownerCountDelta},
449 view(), sponseeSle, sfSponsoredOwnerCount, ownerCountDelta);
455 view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
460 objectSle->makeFieldAbsent(sponsorField);
468 if (isCreate || isReassign)
470 auto const newSponsor =
ctx_.tx[~sfSponsor];
472 newSponsor.has_value(),
473 "xrpl::SponsorshipTransfer::doApply : sfSponsor present for account sponsor "
477 auto const newSponsorID = *newSponsor;
483 ctx_.getApplyViewContext(),
485 sponseeSle->getFieldAmount(sfBalance).xrp(),
487 {.accountCountDelta = 1},
494 auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
515 sponseeSle->setAccountID(sfSponsor, newSponsorID);
518 else if (
ctx_.tx.isFlag(tfSponsorshipEnd))
521 auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
531 ctx_.getApplyViewContext(),
533 balanceBeforeFee(sponseeSle),
535 {.accountCountDelta = 1},
540 sponseeSle->makeFieldAbsent(sfSponsor);
A generic endpoint for log messages.
Writeable view to a ledger, for applying a transaction.
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.
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.
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
bool isFieldPresent(SField const &field) const
AccountID getAccountID(SField const &field) const
std::uint32_t getFlags() const
AccountID const accountID_
Keylet unchecked(UInt256 const &key) noexcept
Any ledger entry.
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet sponsorship(AccountID const &sponsor, AccountID const &sponsee) noexcept
A Sponsorship.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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)
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
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
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.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
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.
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.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.