Merge branch 'xrplf/sponsor' of https://github.com/XRPLF/rippled into mvadari/sponsor/apply-view-context

This commit is contained in:
Mayukha Vadari
2026-06-29 17:39:51 -04:00
5 changed files with 233 additions and 182 deletions

View File

@@ -21,46 +21,92 @@ namespace xrpl {
[[nodiscard]] bool
isGlobalFrozen(ReadView const& view, AccountID const& issuer);
// Calculate liquid XRP balance for an account.
// This function may be used to calculate the amount of XRP that
// the holder is able to freely spend. It subtracts reserve requirements.
//
// ownerCountAdj adjusts the owner count in case the caller calculates
// before ledger entries are added or removed. Positive to add, negative
// to subtract.
//
// @param ownerCountAdj positive to add to count, negative to reduce count.
/** Calculate liquid XRP balance for an account.
*
* This function may be used to calculate the amount of XRP that
* the holder is able to freely spend. It subtracts reserve requirements.
*
* ownerCountAdj adjusts the owner count in case the caller calculates
* before ledger entries are added or removed. Positive to add, negative
* to subtract.
*
* @param view The ledger view to read from
* @param id The account ID to check
* @param ownerCountAdj Positive to add to count, negative to reduce count
* @param j Journal for logging
* @return The liquid XRP amount available to the account
*/
[[nodiscard]] XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
/** Returns the account reserve, in drops.
Actual owner count can be adjusted by delta in ownerCountAdj
The reserve is calculated as
(ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) *
increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base"
*/
*
* Actual owner count can be adjusted by delta in ownerCountAdj
* Actual reserve count can be adjusted by delta in accountCountAdj
* The reserve is calculated as:
* (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) *
* increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base"
*
* @param view The ledger view to read from
* @param sle The ledger entry for the account
* @param j Journal for logging
* @param ownerCountAdj Adjustment to the owner count (default: 0)
* @param accountCountAdj Adjustment to the account count (default: 0)
* @return The account reserve amount in drops
*/
[[nodiscard]] XRPAmount
accountReserve(
ReadView const& view,
SLE::const_ref sle,
beast::Journal j,
std::int32_t ownerCountAdj = 0,
std::int32_t reserveCountAdj = 0);
std::int32_t accountCountAdj = 0);
/** Convenience overload that accepts AccountID instead of SLE.
*
* @param view The ledger view to read from
* @param id The account ID
* @param j Journal for logging
* @param ownerCountAdj Adjustment to the owner count (default: 0)
* @param accountCountAdj Adjustment to the account count (default: 0)
* @return The account reserve amount in drops
*/
[[nodiscard]] inline XRPAmount
accountReserve(
ReadView const& view,
AccountID const& id,
beast::Journal j,
std::int32_t ownerCountAdj = 0,
std::int32_t reserveCountAdj = 0)
std::int32_t accountCountAdj = 0)
{
return accountReserve(view, view.read(keylet::account(id)), j, ownerCountAdj, reserveCountAdj);
return accountReserve(view, view.read(keylet::account(id)), j, ownerCountAdj, accountCountAdj);
}
/** @brief Return the hypothetical reserve required by an account with the provided counters.
*
* @param view The ledger view to read from
* @param ownerCount Number of objects for which the account will be responsible.
* @param accountCount Number of accounts for which the account will be responsible.
* Defaults to 1, as normally every account is responsible for its own reserve.
* Can be 0 if the account is sponsored.
* Can be greater than 1 if the account is sponsoring other accounts.
* @return The hypothetical reserve amount
*/
XRPAmount
baseAccountReserve(ReadView const& view, std::int32_t ownerCount);
baseAccountReserve(ReadView const& view, std::int32_t ownerCount, std::int32_t accountCount = 1);
/** Check if an account has insufficient reserve.
*
* @param view The ledger view to read from
* @param tx The transaction being processed
* @param accSle The account's ledger entry
* @param accBalance The account's balance
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param ownerCountAdj Adjustment to the owner count
* @param accountCountAdj Adjustment to the account count (default: 0)
* @param j Journal for logging (default: null sink)
* @return Transaction result code
*/
[[nodiscard]] TER
checkInsufficientReserve(
ReadView const& view,
@@ -68,60 +114,97 @@ checkInsufficientReserve(
SLE::const_ref accSle,
STAmount const& accBalance,
SLE::const_ref sponsorSle,
std::int32_t ownerCountDelta,
std::int32_t reserveCountDelta = 0,
std::int32_t ownerCountAdj,
std::int32_t accountCountAdj = 0,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
/** Return number of the objects which reserve is covered by the account(sle) (so called "owner
* count"). Actual owner count can be adjusted by delta in ownerCountAdj.
*
* @param sle The account's ledger entry
* @param j Journal for logging
* @param ownerCountAdj Adjustment to the owner count (default: 0)
* @return The adjusted owner count
*/
std::uint32_t
ownerCount(
ReadView const& view,
SLE::const_ref sle,
beast::Journal j,
std::int32_t ownerCountAdj = 0);
ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj = 0);
/** Adjust the owner count up or down. */
/** Adjust the owner counters of the account up or down. If sponsor provided adjust its counters
* too.
*
* @param view The apply view for making changes
* @param accountSle The account's ledger entry
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param accountCountAdj Adjustment amount for the account count
* @param j Journal for logging (default: null sink)
*/
void
adjustOwnerCount(
ApplyView& view,
SLE::ref accountSle,
SLE::ref sponsorSle,
std::int32_t amount,
std::int32_t accountCountAdj,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
/** Convenience overload that accepts AccountID instead of SLE references.
*
* @param view The apply view for making changes
* @param account The account ID
* @param sponsor The optional sponsor account ID
* @param accountCountAdj Adjustment amount for the account count
* @param j Journal for logging (default: null sink)
*/
inline void
adjustOwnerCount(
ApplyView& view,
AccountID const& account,
std::optional<AccountID> const& sponsor,
std::int32_t amount,
std::int32_t accountCountAdj,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
{
adjustOwnerCount(
view,
view.peek(keylet::account(account)),
sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(),
amount,
accountCountAdj,
j);
}
/** Adjust the owner counters of the account up or down. If object has sponsor adjust its counters
* too. Used primarily just before deleting the object.
*
* @param view The apply view for making changes
* @param accountSle The account's ledger entry
* @param objectSle The object's ledger entry
* @param accountCountAdj Adjustment amount for the account count
* @param j Journal for logging (default: null sink)
*/
void
adjustOwnerCountObj(
ApplyView& view,
SLE::ref accountSle,
SLE::ref objectSle,
std::int32_t amount,
std::int32_t accountCountAdj,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
/** Convenience overload that accepts AccountID instead of account SLE reference.
*
* @param view The apply view for making changes
* @param account The account ID
* @param objectSle The object's ledger entry
* @param accountCountAdj Adjustment amount for the account count
* @param j Journal for logging (default: null sink)
*/
inline void
adjustOwnerCountObj(
ApplyView& view,
AccountID const& account,
SLE::ref objectSle,
std::int32_t amount,
std::int32_t accountCountAdj,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
{
SLE::ref accountSle = view.peek(keylet::account(account));
adjustOwnerCountObj(view, accountSle, objectSle, amount, j);
adjustOwnerCountObj(view, accountSle, objectSle, accountCountAdj, j);
}
/** Returns IOU issuer transfer fee as Rate. Rate specifies
@@ -142,25 +225,25 @@ pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey);
/** Returns the list of fields that define an ACCOUNT_ROOT as a pseudo-account
if set.
The list is constructed during initialization and is const after that.
Pseudo-account designator fields MUST be maintained by including the
SField::sMD_PseudoAccount flag in the SField definition.
The list is constructed during initialization and is const after that.
Pseudo-account designator fields MUST be maintained by including the
SField::sMD_PseudoAccount flag in the SField definition.
*/
[[nodiscard]] std::vector<SField const*> const&
getPseudoAccountFields();
/** Returns true if and only if sleAcct is a pseudo-account or specific
pseudo-accounts in pseudoFieldFilter.
Returns false if sleAcct is:
- NOT a pseudo-account OR
- NOT a ltACCOUNT_ROOT OR
- null pointer
*/
/** Convenience overload that reads the account from the view. */
[[nodiscard]] bool
isPseudoAccount(SLE::const_ref sleAcct, std::set<SField const*> const& pseudoFieldFilter = {});
/** Convenience overload that reads the account from the view. */
/** Convenience overload that reads the account from the view.
*
* @param view The ledger view to read from
* @param accountId The account ID to check
* @param pseudoFieldFilter Optional set of specific pseudo-account fields to filter (default:
* empty)
* @return true if the account is a pseudo-account (or matches the filter), false otherwise
*/
[[nodiscard]] inline bool
isPseudoAccount(
ReadView const& view,
@@ -183,8 +266,8 @@ createPseudoAccount(ApplyView& view, uint256 const& pseudoOwnerKey, SField const
/** Checks the destination and tag.
- Checks that the SLE is not null.
- If the SLE requires a destination tag, checks that there is a tag.
- Checks that the SLE is not null.
- If the SLE requires a destination tag, checks that there is a tag.
*/
[[nodiscard]] TER
checkDestinationAndTag(SLE::const_ref toSle, bool hasDestinationTag);

View File

@@ -51,141 +51,103 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer)
// Returns adjusted owner count.
static std::uint32_t
confineOwnerCount(
std::uint32_t current,
std::int32_t adjustment,
std::uint32_t currentOwnerCount,
std::int32_t ownerCountAdj,
std::optional<AccountID> const& id = std::nullopt,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
{
std::uint32_t adjusted{current + adjustment};
if (adjustment > 0)
std::uint32_t totalOwnerCount{currentOwnerCount + ownerCountAdj};
if (ownerCountAdj > 0)
{
// Overflow is well defined on unsigned
if (adjusted < current)
if (totalOwnerCount < currentOwnerCount)
{
if (id)
{
JLOG(j.fatal()) << "Account " << *id << " owner count exceeds max!";
}
adjusted = std::numeric_limits<std::uint32_t>::max();
totalOwnerCount = std::numeric_limits<std::uint32_t>::max();
}
}
else
{
// Underflow is well defined on unsigned
if (adjusted > current)
if (totalOwnerCount > currentOwnerCount)
{
if (id)
{
JLOG(j.fatal()) << "Account " << *id << " owner count set below 0!";
}
adjusted = 0;
totalOwnerCount = 0;
XRPL_ASSERT(!id, "xrpl::confineOwnerCount : id is not set");
}
}
return adjusted;
return totalOwnerCount;
}
// Return number of the accounts which reserve is covered by current account (so called "reserve
// count")
static std::uint32_t
ownerCountHlp(
ReadView const& view,
SLE::const_ref sle,
std::int32_t adjustment,
bool reportConfine,
beast::Journal j)
accountCountImpl(SLE::const_ref sle, std::int32_t accountCountAdj, beast::Journal j)
{
AccountID const id = sle->getAccountID(sfAccount);
std::uint32_t const savedCount = sle->at(sfOwnerCount);
std::uint32_t const hookedCount = view.ownerCountHook(id, savedCount);
bool const isSponsored = sle->isFieldPresent(sfSponsor);
std::int64_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount);
std::int64_t const currentAccountCount = (isSponsored ? 0 : 1) + sponsoringAccountCount;
std::uint32_t const sponsoredCount = sle->at(sfSponsoredOwnerCount);
std::uint32_t const sponsoringCount = sle->at(sfSponsoringOwnerCount);
if (hookedCount < sponsoredCount)
std::int64_t totalAccountCount{currentAccountCount + accountCountAdj};
if (totalAccountCount > std::numeric_limits<std::uint32_t>::max())
{
Throw<std::logic_error>(
"xrpl::ownerCountHlp : OwnerCount must be greater than or equal to "
"SponsoredOwnerCount");
JLOG(j.error()) << "Reserve count exceeds max!";
totalAccountCount = std::numeric_limits<std::uint32_t>::max();
}
else if (totalAccountCount < 0)
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::accountCountImpl : Reserve count set below 0");
JLOG(j.fatal()) << "Reserve count set below 0";
totalAccountCount = 0;
// LCOV_EXCL_STOP
}
return totalAccountCount;
}
std::uint32_t
ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj)
{
XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::ownerCount : sle is account root");
AccountID const id = sle->getAccountID(sfAccount);
std::uint32_t const currentOwnerCount = sle->at(sfOwnerCount);
std::uint32_t const sponsoredOwnerCount = sle->at(sfSponsoredOwnerCount);
std::uint32_t const sponsoringOwnerCount = sle->at(sfSponsoringOwnerCount);
XRPL_ASSERT(
currentOwnerCount >= sponsoredOwnerCount,
"xrpl::ownerCount : OwnerCount must be greater than or equal to "
"SponsoredOwnerCount");
std::int64_t deltaCount =
static_cast<std::int64_t>(adjustment) - sponsoredCount + sponsoringCount;
static_cast<std::int64_t>(ownerCountAdj) - sponsoredOwnerCount + sponsoringOwnerCount;
if (deltaCount > std::numeric_limits<std::int32_t>::max())
{
deltaCount = std::numeric_limits<std::int32_t>::max();
JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, "
<< "adjustment: " << adjustment << ", sponsoredCount: " << sponsoredCount
<< ", sponsoringOwnerCount: " << sponsoringCount;
<< "adjustment: " << ownerCountAdj
<< ", sponsoredCount: " << sponsoredOwnerCount
<< ", sponsoringOwnerCount: " << sponsoringOwnerCount;
}
else if (deltaCount < std::numeric_limits<std::int32_t>::min())
{
deltaCount = std::numeric_limits<std::int32_t>::min();
JLOG(j.fatal()) << "Account " << id << " delta count exceeds min, "
<< "adjustment: " << adjustment << ", sponsoredCount: " << sponsoredCount
<< ", sponsoringCount: " << sponsoringCount;
JLOG(j.fatal()) << "Account " << id << " delta count is below min, "
<< "adjustment: " << ownerCountAdj
<< ", sponsoredCount: " << sponsoredOwnerCount
<< ", sponsoringCount: " << sponsoringOwnerCount;
}
std::uint32_t const confinedCount = reportConfine
? confineOwnerCount(hookedCount, deltaCount, id, j)
: confineOwnerCount(hookedCount, deltaCount);
return confinedCount;
}
static std::uint32_t
reserveCountHlp(SLE::const_ref sle, std::int32_t adjustment, beast::Journal j)
{
bool const isSponsored = sle->isFieldPresent(sfSponsor);
std::uint32_t const sponsoringCount = sle->getFieldU32(sfSponsoringAccountCount);
std::uint32_t const reserveCount = (isSponsored ? 0 : 1) + sponsoringCount;
std::uint32_t adjusted{reserveCount + adjustment};
if (adjustment > 0)
{
// Overflow is well defined on unsigned
if (adjusted < reserveCount)
{
JLOG(j.fatal()) << "Reserve count exceeds max!";
adjusted = std::numeric_limits<std::uint32_t>::max();
}
}
else
{
// Underflow is well defined on unsigned
if (adjusted > reserveCount)
{
JLOG(j.fatal()) << "Reserve count set below 0!";
adjusted = 0;
}
}
return adjusted;
}
static inline XRPAmount
baseReserveHlp(ReadView const& view, std::uint32_t ownerCount, std::uint32_t reserveCount)
{
auto const& fees = view.fees();
return (fees.reserve * reserveCount) + (fees.increment * ownerCount);
}
static XRPAmount
reserveHlp(
ReadView const& view,
SLE::const_ref sle,
std::uint32_t ownerCount,
std::uint32_t reserveCount)
{
// Pseudo-accounts have no reserve requirement
if (isPseudoAccount(sle))
return XRPAmount(0);
auto const reserve = baseReserveHlp(view, ownerCount, reserveCount);
return reserve;
}
std::uint32_t
ownerCount(ReadView const& view, SLE::const_ref sle, beast::Journal j, std::int32_t adjustment)
{
return ownerCountHlp(view, sle, adjustment, true, j);
return confineOwnerCount(currentOwnerCount, deltaCount);
}
XRPAmount
@@ -195,9 +157,15 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
if (sle == nullptr)
return beast::kZero;
std::uint32_t const ownerCount = ownerCountHlp(view, sle, ownerCountAdj, false, j);
std::uint32_t const reserveCount = reserveCountHlp(sle, 0, j);
auto const reserve = reserveHlp(view, sle, ownerCount, reserveCount);
// Return balance minus reserve
std::uint32_t const currentOwnerCount =
confineOwnerCount(view.ownerCountHook(id, ownerCount(sle, j)), ownerCountAdj);
std::uint32_t const currentAccountCount = accountCountImpl(sle, 0, j);
// Pseudo-accounts have no reserve requirement
auto const reserve = isPseudoAccount(sle)
? XRPAmount{0}
: baseAccountReserve(view, currentOwnerCount, currentAccountCount);
auto const fullBalance = sle->getFieldAmount(sfBalance);
@@ -209,7 +177,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
<< " amount=" << amount.getFullText()
<< " fullBalance=" << fullBalance.getFullText()
<< " balance=" << balance.getFullText() << " reserve=" << reserve
<< " ownerCount=" << ownerCount << " ownerCountAdj=" << ownerCountAdj;
<< " ownerCount=" << currentOwnerCount << " ownerCountAdj=" << ownerCountAdj;
return amount.xrp();
}
@@ -226,20 +194,21 @@ transferRate(ReadView const& view, AccountID const& issuer)
}
static void
adjustOwnerCountHlp(
adjustOwnerCountImpl(
ApplyView& view,
SLE::ref sle,
SF_UINT32 const& sfield,
AccountID const& accID,
std::int32_t adjustment,
std::int32_t ownerCountAdj,
beast::Journal j,
bool callHook = true)
{
std::uint32_t const current = sle->at(sfield);
std::uint32_t const adjusted = confineOwnerCount(current, adjustment, accID, j);
std::uint32_t const currentOwnerCount = sle->at(sfield);
std::uint32_t const totalOwnerCount =
confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j);
if (callHook)
view.adjustOwnerCountHook(accID, current, adjusted);
sle->at(sfield) = adjusted;
view.adjustOwnerCountHook(accID, currentOwnerCount, totalOwnerCount);
sle->at(sfield) = totalOwnerCount;
view.update(sle);
}
@@ -248,7 +217,7 @@ adjustOwnerCount(
ApplyView& view,
SLE::ref accountSle,
SLE::ref sponsorSle,
std::int32_t adjustment,
std::int32_t ownerCountAdj,
beast::Journal j)
{
if (!accountSle)
@@ -260,8 +229,8 @@ adjustOwnerCount(
if (!validType)
Throw<std::logic_error>("xrpl::adjustOwnerCount : valid account sle type");
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input");
if (adjustment == 0)
XRPL_ASSERT(ownerCountAdj, "xrpl::adjustOwnerCount : nonzero ownerCountAdj input");
if (ownerCountAdj == 0)
return;
auto const accountID = accountSle->getAccountID(sfAccount);
@@ -271,20 +240,21 @@ adjustOwnerCount(
Throw<std::logic_error>("xrpl::adjustOwnerCount : valid sponsor sle type");
auto const sponsorID = sponsorSle->getAccountID(sfAccount);
adjustOwnerCountHlp(view, accountSle, sfSponsoredOwnerCount, accountID, adjustment, j);
adjustOwnerCountHlp(view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j);
adjustOwnerCountImpl(view, accountSle, sfSponsoredOwnerCount, accountID, ownerCountAdj, j);
adjustOwnerCountImpl(view, sponsorSle, sfSponsoringOwnerCount, sponsorID, ownerCountAdj, j);
auto sponsorshipSle = view.peek(keylet::sponsorship(sponsorID, accountID));
if (sponsorshipSle && adjustment > 0)
if (sponsorshipSle && ownerCountAdj > 0)
{
// update the pre-funded RemainingOwnerCount on Sponsorship ledger object
// Remaining owner count moves opposite to adjustment:
// +adjustment => consume reserve (-),
adjustOwnerCountHlp(
view, sponsorshipSle, sfRemainingOwnerCount, sponsorID, -adjustment, j, false);
// Only decrease the pre-funded ReserveCount on Sponsorship if we assign new objects.
// Removing/reassigning ownership of the object doesn't increase RemainingOwnerCount
// back. Don't call hook because this counter is not something that require reserve
// (like other sf...OwnerCounts do).
adjustOwnerCountImpl(
view, sponsorshipSle, sfRemainingOwnerCount, sponsorID, -ownerCountAdj, j, false);
}
}
adjustOwnerCountHlp(view, accountSle, sfOwnerCount, accountID, adjustment, j);
adjustOwnerCountImpl(view, accountSle, sfOwnerCount, accountID, ownerCountAdj, j);
}
void
@@ -292,7 +262,7 @@ adjustOwnerCountObj(
ApplyView& view,
SLE::ref accountSle,
SLE::ref objectSle,
std::int32_t amount,
std::int32_t accountCountAdj,
beast::Journal j)
{
if (!objectSle)
@@ -301,7 +271,7 @@ adjustOwnerCountObj(
Throw<std::logic_error>("xrpl::adjustOwnerCount : valid object sle type");
SLE::ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle);
adjustOwnerCount(view, accountSle, sponsorSle, amount, j);
adjustOwnerCount(view, accountSle, sponsorSle, accountCountAdj, j);
}
XRPAmount
@@ -310,24 +280,24 @@ accountReserve(
SLE::const_ref sle,
beast::Journal j,
std::int32_t ownerCountAdj,
std::int32_t reserveCountAdj)
std::int32_t accountCountAdj)
{
if (!sle)
Throw<std::runtime_error>("xrpl::accountReserve : valid sle");
if (sle->getType() != ltACCOUNT_ROOT)
Throw<std::logic_error>("xrpl::accountReserve : valid sle type");
std::uint32_t const ownerCount = ownerCountHlp(view, sle, ownerCountAdj, true, j);
std::uint32_t const reserveCount = reserveCountHlp(sle, reserveCountAdj, j);
std::uint32_t const currentOwnerCount = ownerCount(sle, j, ownerCountAdj);
std::uint32_t const currentAccountCount = accountCountImpl(sle, accountCountAdj, j);
return reserveHlp(view, sle, ownerCount, reserveCount);
return baseAccountReserve(view, currentOwnerCount, currentAccountCount);
}
XRPAmount
baseAccountReserve(ReadView const& view, std::int32_t ownerCount)
baseAccountReserve(ReadView const& view, std::int32_t ownerCount, std::int32_t accountCount)
{
auto const reserve = baseReserveHlp(view, ownerCount, 1);
return reserve;
auto const& fees = view.fees();
return (fees.reserve * accountCount) + (fees.increment * ownerCount);
}
TER
@@ -337,8 +307,8 @@ checkInsufficientReserve(
SLE::const_ref accSle,
STAmount const& accBalance,
SLE::const_ref sponsorSle,
std::int32_t ownerCountDelta,
std::int32_t reserveCountDelta,
std::int32_t ownerCountAdj,
std::int32_t accountCountAdj,
beast::Journal j)
{
if (sponsorSle)
@@ -356,21 +326,20 @@ checkInsufficientReserve(
if (sle)
{
auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount);
if (ownerCountAllowed < ownerCountDelta)
if (ownerCountAllowed < ownerCountAdj)
return tecINSUFFICIENT_RESERVE;
}
auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance);
STAmount const sponsorReserve =
accountReserve(view, sponsorSle, j, ownerCountDelta, reserveCountDelta);
accountReserve(view, sponsorSle, j, ownerCountAdj, accountCountAdj);
if (sponsorBalance < sponsorReserve)
return tecINSUFFICIENT_RESERVE;
}
else
{
STAmount const reserve =
accountReserve(view, accSle, j, ownerCountDelta, reserveCountDelta);
STAmount const reserve = accountReserve(view, accSle, j, ownerCountAdj, accountCountAdj);
if (accBalance < reserve)
return tecINSUFFICIENT_RESERVE;
}

View File

@@ -204,7 +204,7 @@ authorizeMPToken(
// The "free-tier" shortcut (ownerCount < 2) does not apply once a sponsor is on
// the tx — the sponsor must always cover the reserve (via balance or prefunded
// budget), so this check always runs for sponsored transactions.
if (*sponsorSle || ownerCount(view, sleAcct, journal) >= 2)
if (*sponsorSle || ownerCount(sleAcct, journal) >= 2)
{
if (auto const ret = checkInsufficientReserve(
view, ctx.tx, sleAcct, priorBalance, *sponsorSle, 1, 0, journal);

View File

@@ -630,15 +630,14 @@ AMMWithdraw::withdraw(
if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE
STAmount const balance = (*sleAccount)[sfBalance];
std::uint32_t const currentOwnerCount = ownerCount(view, sleAccount, journal);
auto const balance = (*sleAccount)[sfBalance]->xrp();
std::uint32_t const currentOwnerCount = ownerCount(sleAccount, journal);
// See also TrustSet::doApply() and MPTokenAuthorize::authorize()
XRPAmount const reserve(
(currentOwnerCount < 2) ? XRPAmount(beast::kZero)
: accountReserve(view, sleAccount, journal, 1));
auto const balanceAdj = isIssue ? std::max(priorBalance, balance.xrp()) : priorBalance;
auto const balanceAdj = isIssue ? std::max(priorBalance, balance) : priorBalance;
if (balanceAdj < reserve)
return tecINSUFFICIENT_RESERVE;
}

View File

@@ -333,7 +333,7 @@ TrustSet::doApply()
if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE
std::uint32_t const uOwnerCount = ownerCount(view(), *sponsorSle ? *sponsorSle : sle, j_);
std::uint32_t const uOwnerCount = ownerCount(*sponsorSle ? *sponsorSle : sle, j_);
// The "free-tier" shortcut (ownerCount < 2) only applies when there is no sponsor.
// With any sponsor on the tx, the sponsor must cover the reserve (via balance or