From e42c7c85516aefa2370eac484759ec10264f5402 Mon Sep 17 00:00:00 2001 From: Olek <115580134+oleks-rip@users.noreply.github.com> Date: Mon, 29 Jun 2026 16:50:30 -0400 Subject: [PATCH] Revert back to simple and inconsistent reserve calculations. (#7633) --- .../xrpl/ledger/helpers/AccountRootHelpers.h | 175 +++++++++---- .../ledger/helpers/AccountRootHelpers.cpp | 229 ++++++++---------- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 2 +- .../tx/transactors/dex/AMMWithdraw.cpp | 7 +- src/libxrpl/tx/transactors/token/TrustSet.cpp | 2 +- 5 files changed, 233 insertions(+), 182 deletions(-) diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index e19fe23393..403626d06f 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -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 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 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 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); diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index f3ea3da896..3264fa1a45 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -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 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::max(); + totalOwnerCount = std::numeric_limits::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::max()) { - Throw( - "xrpl::ownerCountHlp : OwnerCount must be greater than or equal to " - "SponsoredOwnerCount"); + JLOG(j.error()) << "Reserve count exceeds max!"; + totalAccountCount = std::numeric_limits::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(adjustment) - sponsoredCount + sponsoringCount; + static_cast(ownerCountAdj) - sponsoredOwnerCount + sponsoringOwnerCount; + if (deltaCount > std::numeric_limits::max()) { deltaCount = std::numeric_limits::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::min()) { deltaCount = std::numeric_limits::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::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("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("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("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("xrpl::accountReserve : valid sle"); if (sle->getType() != ltACCOUNT_ROOT) Throw("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; } diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index fcdbd58ea2..8fbda7f2bb 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -205,7 +205,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, tx, sleAcct, priorBalance, *sponsorSle, 1, 0, journal); diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index adab7a2bf4..0fdfae1a9a 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -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; } diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index 1cd06cdb82..6961b40bd1 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -332,7 +332,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