diff --git a/include/xrpl/ledger/ApplyView.h b/include/xrpl/ledger/ApplyView.h index 4ee10dfd018..923ce66799b 100644 --- a/include/xrpl/ledger/ApplyView.h +++ b/include/xrpl/ledger/ApplyView.h @@ -412,10 +412,58 @@ class ApplyView : public ReadView emptyDirDelete(Keylet const& directory); }; +struct ReserveContext +{ + SLE::pointer accountSle; + SLE::pointer sponsorSle; + SLE::pointer sponsorshipSle; + + [[nodiscard]] AccountID const + accountID() const + { + return accountSle->getAccountID(sfAccount); + } + + [[nodiscard]] std::optional const + sponsorID() const + { + return sponsorSle ? std::optional{sponsorSle->getAccountID(sfAccount)} + : std::nullopt; + } + + [[nodiscard]] bool + isSponsored() const + { + return sponsorSle != nullptr; + } + + [[nodiscard]] bool + hasSponsorshipObj() const + { + return sponsorshipSle != nullptr; + } + + static ReserveContext + makeFromTx(ApplyView& view, STTx const& tx); + + static ReserveContext + makeFromAccount(ApplyView& view, SLE::pointer accountSle, SLE::pointer sponsorSle); + + static ReserveContext + makeFromObject(ApplyView& view, SLE::ref objectSle, SLE::pointer ownerSle); +}; + struct ApplyViewContext { ApplyView& view; STTx const& tx; + ReserveContext txReserveContext; + + static ApplyViewContext + makeFromTx(ApplyView& view, STTx const& tx) + { + return {.view = view, .tx = tx, .txReserveContext = ReserveContext::makeFromTx(view, tx)}; + } }; namespace directory { diff --git a/include/xrpl/ledger/helpers/AccountRootHelpers.h b/include/xrpl/ledger/helpers/AccountRootHelpers.h index 41361a7e399..3385a9cb05a 100644 --- a/include/xrpl/ledger/helpers/AccountRootHelpers.h +++ b/include/xrpl/ledger/helpers/AccountRootHelpers.h @@ -86,13 +86,15 @@ accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adju /** Check if an account has insufficient reserve. * - * @param view The ledger view to read from - * @param tx The transaction being processed + * The transaction-level reserve sponsor (if any) is taken from + * `ctx.txReserveContext` and, per XLS-68, is only applied when `accSle` is the + * tx.Account. For any other account the sponsor is ignored and the account + * must cover its own reserve. + * + * @param ctx The apply-view context (provides the view, tx and reserve context) * @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 adj Adjustment to the owner/account counts * @param j Journal for logging (default: null sink) * @return Transaction result code */ @@ -101,7 +103,6 @@ checkInsufficientReserve( ApplyViewContext ctx, SLE::const_ref accSle, STAmount const& accBalance, - SLE::const_ref sponsorSle, Adjustment adj, beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); @@ -124,43 +125,17 @@ ownerCount(SLE::const_ref sle, beast::Journal j, std::int32_t ownerCountAdj = 0) * and the sponsor's sponsoring count. * * @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 reserveCtx The account and sponsor information * @param count Amount to add to the owner count * @param j Journal for logging */ void increaseOwnerCount( ApplyView& view, - SLE::ref accountSle, - SLE::ref sponsorSle, + ReserveContext const& reserveCtx, std::uint32_t count, beast::Journal j); -/** 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 count Amount to add to the owner count - * @param j Journal for logging - */ -inline void -increaseOwnerCount( - ApplyView& view, - AccountID const& account, - std::optional const& sponsor, - std::uint32_t count, - beast::Journal j) -{ - increaseOwnerCount( - view, - view.peek(keylet::account(account)), - sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), - count, - j); -} - /** Decrease owner-count fields when the caller supplies the sponsor. * * This helper does not delete a ledger object. It updates reserve accounting @@ -169,43 +144,17 @@ increaseOwnerCount( * sfSponsor field. * * @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 reserveCtx The account and sponsor information * @param count Amount to remove from the owner count * @param j Journal for logging */ void decreaseOwnerCount( ApplyView& view, - SLE::ref accountSle, - SLE::ref sponsorSle, + ReserveContext const& reserveCtx, std::uint32_t count, beast::Journal j); -/** 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 count Amount to remove from the owner count - * @param j Journal for logging - */ -inline void -decreaseOwnerCount( - ApplyView& view, - AccountID const& account, - std::optional const& sponsor, - std::uint32_t count, - beast::Journal j) -{ - decreaseOwnerCount( - view, - view.peek(keylet::account(account)), - sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), - count, - j); -} - /** Decrease owner-count fields for an existing ledger object. * * This helper derives the reserve sponsor from objectSle's sfSponsor field, diff --git a/include/xrpl/ledger/helpers/EscrowHelpers.h b/include/xrpl/ledger/helpers/EscrowHelpers.h index 6a7d182dce2..b82dca3fe3f 100644 --- a/include/xrpl/ledger/helpers/EscrowHelpers.h +++ b/include/xrpl/ledger/helpers/EscrowHelpers.h @@ -23,6 +23,8 @@ #include #include +#include + namespace xrpl { template @@ -68,12 +70,8 @@ escrowUnlockApplyHelper( if (!ctx.view.exists(trustLineKey) && createAsset) { // Can the account cover the trust line's reserve? - auto const sponsorSle = getTxReserveSponsor(ctx); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE - - if (auto const ret = checkInsufficientReserve( - ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal); + if (auto const ret = + checkInsufficientReserve(ctx, sleDest, xrpBalance, {.ownerCountDelta = 1}, journal); !isTesSuccess(ret)) { JLOG(journal.trace()) << "Trust line does not exist. " @@ -86,8 +84,10 @@ escrowUnlockApplyHelper( STAmount initialBalance(issue); initialBalance.get().account = noAccount(); + // The ApplyViewContext overload derives the sponsor (XLS-68: only when + // sleDest is the tx.Account, which createAsset already guarantees). if (TER const ter = trustCreate( - ctx.view, // payment sandbox + ctx, // apply-view context recvLow, // is dest low? issuer, // source receiver, // destination @@ -101,7 +101,6 @@ escrowUnlockApplyHelper( Issue(currency, receiver), // limit of zero 0, // quality in 0, // quality out - *sponsorSle, // sponsor journal); // journal !isTesSuccess(ter)) { @@ -197,25 +196,21 @@ escrowUnlockApplyHelper( auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver); if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer) { - auto const sponsorSle = getTxReserveSponsor(ctx); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE - - if (auto const ret = checkInsufficientReserve( - ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal); + if (auto const ret = + checkInsufficientReserve(ctx, sleDest, xrpBalance, {.ownerCountDelta = 1}, journal); !isTesSuccess(ret)) return ret; - if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0); - !isTesSuccess(ter)) + auto const reserveCtx = + ReserveContext::makeFromAccount(ctx.view, sleDest, ctx.txReserveContext.sponsorSle); + + if (auto const ter = createMPToken(ctx.view, mptID, reserveCtx, 0); !isTesSuccess(ter)) { return ter; // LCOV_EXCL_LINE } // update owner count. - increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal); - auto mptSle = ctx.view.peek(mptKeylet); - addSponsorToLedgerEntry(mptSle, *sponsorSle); + increaseOwnerCount(ctx.view, reserveCtx, 1, journal); } if (!ctx.view.exists(mptKeylet) && !receiverIssuer) diff --git a/include/xrpl/ledger/helpers/MPTokenHelpers.h b/include/xrpl/ledger/helpers/MPTokenHelpers.h index 8a2a4a5b84a..db6bb883007 100644 --- a/include/xrpl/ledger/helpers/MPTokenHelpers.h +++ b/include/xrpl/ledger/helpers/MPTokenHelpers.h @@ -242,16 +242,15 @@ TER createMPToken( ApplyView& view, MPTID const& mptIssuanceID, - AccountID const& account, - SLE::ref sponsorSle, + ReserveContext const& reserveCtx, std::uint32_t const flags); TER checkCreateMPT( - xrpl::ApplyView& view, + ApplyView& view, + ReserveContext const& reserveCtx, xrpl::MPTIssue const& mptIssue, xrpl::AccountID const& holder, - SLE::ref sponsorSle, beast::Journal j); //------------------------------------------------------------------------------ diff --git a/include/xrpl/ledger/helpers/NFTokenHelpers.h b/include/xrpl/ledger/helpers/NFTokenHelpers.h index 1c4d395fbef..5e24d65b9c3 100644 --- a/include/xrpl/ledger/helpers/NFTokenHelpers.h +++ b/include/xrpl/ledger/helpers/NFTokenHelpers.h @@ -51,7 +51,7 @@ findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftoken /** Insert the token in the owner's token directory. */ TER -insertToken(ApplyView& view, AccountID owner, STObject&& nft); +insertToken(ApplyViewContext& view, AccountID owner, STObject&& nft); /** Remove the token from the owner's token directory. */ TER diff --git a/include/xrpl/ledger/helpers/RippleStateHelpers.h b/include/xrpl/ledger/helpers/RippleStateHelpers.h index 322bbcafb51..3569e571ecc 100644 --- a/include/xrpl/ledger/helpers/RippleStateHelpers.h +++ b/include/xrpl/ledger/helpers/RippleStateHelpers.h @@ -159,6 +159,60 @@ trustCreate( SLE::ref sponsorSle, beast::Journal j); +/** Create a trust line, deriving the reserve sponsor from the apply-view context. + + Convenience overload for transactor-level callers that hold an + ApplyViewContext. Per XLS-68, the transaction-level sponsor is applied only + when `sleAccount` is the tx.Account; otherwise no sponsor is recorded. The + bare ApplyView overload above remains for the payment engine, which has no + reserve context (and always creates unsponsored trust lines). +*/ +[[nodiscard]] inline TER +trustCreate( + ApplyViewContext ctx, + bool const bSrcHigh, + AccountID const& uSrcAccountID, + AccountID const& uDstAccountID, + uint256 const& uIndex, // --> ripple state entry + SLE::ref sleAccount, // --> the account being set. + bool const bAuth, // --> authorize account. + bool const bNoRipple, // --> others cannot ripple through + bool const bFreeze, // --> funds cannot leave + bool bDeepFreeze, // --> can neither receive nor send funds + STAmount const& saBalance, // --> balance of account being set. + // Issuer should be noAccount() + STAmount const& saLimit, // --> limit for account being set. + // Issuer should be the account being set. + std::uint32_t uQualityIn, + std::uint32_t uQualityOut, + beast::Journal j) +{ + // XLS-68: the transaction-level reserve sponsor covers only the tx.Account's + // own objects. sleAccount is the account taking on the trust line reserve, + // so the sponsor applies only when it is the tx.Account. + SLE::pointer const sponsorSle = + sleAccount->getAccountID(sfAccount) == ctx.txReserveContext.accountID() + ? ctx.txReserveContext.sponsorSle + : nullptr; + return trustCreate( + ctx.view, + bSrcHigh, + uSrcAccountID, + uDstAccountID, + uIndex, + sleAccount, + bAuth, + bNoRipple, + bFreeze, + bDeepFreeze, + saBalance, + saLimit, + uQualityIn, + uQualityOut, + sponsorSle, + j); +} + [[nodiscard]] TER trustDelete( ApplyView& view, diff --git a/include/xrpl/ledger/helpers/SponsorHelpers.h b/include/xrpl/ledger/helpers/SponsorHelpers.h index 0a1fc0691ab..7e10f7c9525 100644 --- a/include/xrpl/ledger/helpers/SponsorHelpers.h +++ b/include/xrpl/ledger/helpers/SponsorHelpers.h @@ -41,22 +41,6 @@ getTxReserveSponsorAccountID(STTx const& tx) return {}; } -inline std::expected -getTxReserveSponsor(ApplyViewContext ctx) -{ - auto const sponsorID = getTxReserveSponsorAccountID(ctx.tx); - if (sponsorID) - { - auto sle = ctx.view.peek(keylet::account(*sponsorID)); - - // already checked in Transactor::checkSponsor - if (!sle) - return std::unexpected(tecINTERNAL); - return sle; - } - return SLE::pointer(); -} - inline std::expected getTxReserveSponsor(ReadView const& view, STTx const& tx) { diff --git a/include/xrpl/tx/ApplyContext.h b/include/xrpl/tx/ApplyContext.h index f64957dd35c..8897113e5d3 100644 --- a/include/xrpl/tx/ApplyContext.h +++ b/include/xrpl/tx/ApplyContext.h @@ -133,7 +133,7 @@ class ApplyContext XRPL_ASSERT( view_.has_value(), "xrpl::ApplyContext::getApplyViewContext : view_ emplaced in constructor"); - return {.view = *view_, .tx = tx}; + return ApplyViewContext::makeFromTx(*view_, tx); } private: diff --git a/src/libxrpl/ledger/ApplyView.cpp b/src/libxrpl/ledger/ApplyView.cpp index 8575bc2f522..59630a643c5 100644 --- a/src/libxrpl/ledger/ApplyView.cpp +++ b/src/libxrpl/ledger/ApplyView.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include #include #include @@ -415,4 +418,50 @@ ApplyView::dirDelete(Keylet const& directory, std::function const sponsor = + isReserveSponsored(tx) ? std::optional{tx[sfSponsor]} : std::nullopt; + + return { + .accountSle = view.peek(keylet::account(account)), + .sponsorSle = sponsor ? view.peek(keylet::account(*sponsor)) : nullptr, + .sponsorshipSle = sponsor ? view.peek(keylet::sponsorship(*sponsor, account)) : nullptr, + }; +} + +ReserveContext +ReserveContext::makeFromAccount(ApplyView& view, SLE::pointer accountSle, SLE::pointer sponsorSle) +{ + if (!accountSle) + Throw("ReserveContext::makeFromAccount : valid account sle"); + + auto const accountID = accountSle->getAccountID(sfAccount); + std::optional const sponsorID = + sponsorSle ? std::optional{sponsorSle->getAccountID(sfAccount)} : std::nullopt; + return { + .accountSle = accountSle, + .sponsorSle = sponsorSle, + .sponsorshipSle = + sponsorID ? view.peek(keylet::sponsorship(*sponsorID, accountID)) : nullptr, + }; +} + +ReserveContext +ReserveContext::makeFromObject(ApplyView& view, SLE::ref objectSle, SLE::pointer ownerSle) +{ + auto const accountID = ownerSle->getAccountID(sfAccount); + SLE::ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle); + std::optional const sponsorID = + sponsorSle ? std::optional{sponsorSle->getAccountID(sfAccount)} : std::nullopt; + return { + .accountSle = ownerSle, + .sponsorSle = sponsorSle, + .sponsorshipSle = + sponsorID ? view.peek(keylet::sponsorship(*sponsorID, accountID)) : nullptr, + }; +} + } // namespace xrpl diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 7a624677df8..80f14dfd017 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -469,14 +468,11 @@ doWithdraw( // LCOV_EXCL_STOP } - auto const sponsorSle = getTxReserveSponsor(ctx); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto const sponsorSle = ctx.txReserveContext.sponsorSle; // Move the funds directly from the broker's pseudo-account to the // dstAcct - return accountSend( - ctx.view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes); + return accountSend(ctx.view, sourceAcct, dstAcct, amount, j, sponsorSle, WaiveTransferFee::Yes); } TER diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index d8e34622db2..d5c456f422c 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -262,8 +262,7 @@ adjustOwnerCountSigned( void increaseOwnerCount( ApplyView& view, - SLE::ref accountSle, - SLE::ref sponsorSle, + ReserveContext const& reserveCtx, std::uint32_t count, beast::Journal j) { @@ -273,14 +272,14 @@ increaseOwnerCount( if (count == 0 || count > std::numeric_limits::max()) return; // LCOV_EXCL_LINE - adjustOwnerCountSigned(view, accountSle, sponsorSle, static_cast(count), j); + adjustOwnerCountSigned( + view, reserveCtx.accountSle, reserveCtx.sponsorSle, static_cast(count), j); } void decreaseOwnerCount( ApplyView& view, - SLE::ref accountSle, - SLE::ref sponsorSle, + ReserveContext const& reserveCtx, std::uint32_t count, beast::Journal j) { @@ -290,13 +289,14 @@ decreaseOwnerCount( if (count == 0 || count > std::numeric_limits::max()) return; // LCOV_EXCL_LINE - adjustOwnerCountSigned(view, accountSle, sponsorSle, -static_cast(count), j); + adjustOwnerCountSigned( + view, reserveCtx.accountSle, reserveCtx.sponsorSle, -static_cast(count), j); } void decreaseOwnerCountForObject( ApplyView& view, - SLE::ref accountSle, + SLE::ref ownerSle, SLE::ref objectSle, std::uint32_t count, beast::Journal j) @@ -310,10 +310,8 @@ decreaseOwnerCountForObject( if (!validObjectType) return; // LCOV_EXCL_LINE - SLE::ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle); - decreaseOwnerCount(view, accountSle, sponsorSle, count, j); + decreaseOwnerCount(view, ReserveContext::makeFromObject(view, objectSle, ownerSle), count, j); } - XRPAmount accountReserve(ReadView const& view, SLE::const_ref sle, beast::Journal j, Adjustment adj) { @@ -331,12 +329,20 @@ accountReserve(ReadView const& view, SLE::const_ref sle, beast::Journal j, Adjus TER checkInsufficientReserve( ApplyViewContext ctx, + // NOTE: we still need the accSle to be passed in as the ctx.txReserveContext.accountSle may be + // outdated SLE::const_ref accSle, STAmount const& accBalance, - SLE::const_ref sponsorSle, Adjustment adj, beast::Journal j) { + // XLS-68: the transaction-level reserve sponsor covers only the objects + // owned by the tx.Account. For any other account (e.g. a pseudo-account or + // a counterparty), the sponsor does not apply and the account must cover + // its own reserve. + auto const sponsorSle = accSle->getAccountID(sfAccount) == ctx.txReserveContext.accountID() + ? ctx.txReserveContext.sponsorSle + : nullptr; if (sponsorSle) { auto const sle = ctx.view.read( diff --git a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp index a7077672fc3..5d377b23545 100644 --- a/src/libxrpl/ledger/helpers/CredentialHelpers.cpp +++ b/src/libxrpl/ledger/helpers/CredentialHelpers.cpp @@ -97,7 +97,8 @@ deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j) } if (isOwner) - decreaseOwnerCount(view, sleAccount, {}, 1, j); + decreaseOwnerCountForObject( + view, view.peek(keylet::account(account)), sleCredential, 1, j); return tesSUCCESS; }; diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index f87171a3b76..e00c3f82f2c 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -143,7 +143,7 @@ addEmptyHolding( if (accountID == mptIssue.getIssuer()) return tesSUCCESS; - return authorizeMPToken(ctx, priorBalance, mptID, accountID, journal, 0, std::nullopt); + return authorizeMPToken(ctx, priorBalance, mptID, accountID, journal); } [[nodiscard]] TER @@ -160,6 +160,10 @@ authorizeMPToken( if (!sleAcct) return tecINTERNAL; // LCOV_EXCL_LINE + auto const reserveCtx = account == ctx.txReserveContext.accountID() + ? ctx.txReserveContext + : ReserveContext::makeFromAccount(ctx.view, sleAcct, nullptr); + // If the account that submitted the tx is a holder // Note: `account_` is holder's account // `holderID` is NOT used @@ -181,7 +185,7 @@ authorizeMPToken( keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false)) return tecINTERNAL; // LCOV_EXCL_LINE - decreaseOwnerCountForObject(ctx.view, sleAcct, sleMpt, 1, journal); + decreaseOwnerCountForObject(ctx.view, reserveCtx.accountSle, sleMpt, 1, journal); ctx.view.erase(sleMpt); return tesSUCCESS; @@ -191,14 +195,7 @@ authorizeMPToken( // - add the new mptokenKey to the owner directory // - create the MPToken object for the holder - SLE::pointer sponsorSle; - if (account == ctx.tx[sfAccount]) - { - auto sle = getTxReserveSponsor(ctx); - if (!sle) - return sle.error(); // LCOV_EXCL_LINE - sponsorSle = std::move(*sle); - } + auto const isSponsored = reserveCtx.isSponsored(); // The reserve that is required to create the MPToken. Note // that although the reserve increases with every item @@ -208,10 +205,10 @@ 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(sleAcct, journal) >= 2) + if (isSponsored || ownerCount(sleAcct, journal) >= 2) { if (auto const ret = checkInsufficientReserve( - ctx, sleAcct, priorBalance, sponsorSle, {.ownerCountDelta = 1}, journal); + ctx, sleAcct, priorBalance, {.ownerCountDelta = 1}, journal); !isTesSuccess(ret)) return ret; } @@ -238,8 +235,8 @@ authorizeMPToken( ctx.view.insert(mptoken); // Update owner count. - increaseOwnerCount(ctx.view, sleAcct, sponsorSle, 1, journal); - addSponsorToLedgerEntry(mptoken, sponsorSle); + increaseOwnerCount(ctx.view, reserveCtx, 1, journal); + addSponsorToLedgerEntry(mptoken, reserveCtx.sponsorSle); return tesSUCCESS; } @@ -928,10 +925,10 @@ TER createMPToken( ApplyView& view, MPTID const& mptIssuanceID, - AccountID const& account, - SLE::ref sponsorSle, + ReserveContext const& reserveCtx, std::uint32_t const flags) { + auto const account = reserveCtx.accountID(); auto const mptokenKey = keylet::mptoken(mptIssuanceID, account); auto const ownerNode = @@ -946,8 +943,7 @@ createMPToken( (*mptoken)[sfFlags] = flags; (*mptoken)[sfOwnerNode] = *ownerNode; - if (sponsorSle) - addSponsorToLedgerEntry(mptoken, sponsorSle); + addSponsorToLedgerEntry(mptoken, reserveCtx.sponsorSle); view.insert(mptoken); @@ -956,12 +952,15 @@ createMPToken( TER checkCreateMPT( - xrpl::ApplyView& view, + ApplyView& view, + ReserveContext const& reserveCtx, xrpl::MPTIssue const& mptIssue, xrpl::AccountID const& holder, - SLE::ref sponsorSle, beast::Journal j) { + XRPL_ASSERT( + reserveCtx.accountID() == holder, "xrpl::checkCreateMPT : reserve context matches holder"); + if (mptIssue.getIssuer() == holder) return tesSUCCESS; @@ -969,7 +968,7 @@ checkCreateMPT( auto const mptokenID = keylet::mptoken(mptIssuanceID.key, holder); if (!view.exists(mptokenID)) { - if (auto const err = createMPToken(view, mptIssue.getMptID(), holder, sponsorSle, 0); + if (auto const err = createMPToken(view, mptIssue.getMptID(), reserveCtx, 0); !isTesSuccess(err)) { return err; @@ -980,7 +979,7 @@ checkCreateMPT( return tecINTERNAL; } - increaseOwnerCount(view, sleAcct, sponsorSle, 1, j); + increaseOwnerCount(view, reserveCtx, 1, j); } return tesSUCCESS; } diff --git a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp index eb0c96f79f0..ea19110afbc 100644 --- a/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/NFTokenHelpers.cpp @@ -69,11 +69,12 @@ locatePage(ApplyView& view, AccountID const& owner, uint256 const& id) static SLE::pointer getPageForToken( - ApplyView& view, + ApplyViewContext& ctx, AccountID const& owner, uint256 const& id, - std::function const& createCallback) + std::function const& createCallback) { + auto& view = ctx.view; auto const base = keylet::nftokenPageMin(owner); auto const first = keylet::nftokenPage(base, id); auto const last = keylet::nftokenPageMax(owner); @@ -91,7 +92,7 @@ getPageForToken( cp = std::make_shared(last); cp->setFieldArray(sfNFTokens, arr); view.insert(cp); - createCallback(view, owner); + createCallback(ctx, owner); return cp; } @@ -204,7 +205,7 @@ getPageForToken( cp->setFieldH256(sfPreviousPageMin, np->key()); view.update(cp); - createCallback(view, owner); + createCallback(ctx, owner); return (first.key < np->key()) ? np : cp; } @@ -260,16 +261,22 @@ changeTokenURI( /** Insert the token in the owner's token directory. */ TER -insertToken(ApplyView& view, AccountID owner, STObject&& nft) +insertToken(ApplyViewContext& ctx, AccountID owner, STObject&& nft) { + auto& view = ctx.view; XRPL_ASSERT(nft.isFieldPresent(sfNFTokenID), "xrpl::nft::insertToken : has NFT token"); // First, we need to locate the page the NFT belongs to, creating it // if necessary. This operation may fail if it is impossible to insert // the NFT. - SLE::pointer const page = - getPageForToken(view, owner, nft[sfNFTokenID], [](ApplyView& view, AccountID const& owner) { - increaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()}); + SLE::pointer const page = getPageForToken( + ctx, owner, nft[sfNFTokenID], [](ApplyViewContext& ctx, AccountID const& owner) { + auto const reserveCtx = owner == ctx.txReserveContext.accountID() + ? ctx.txReserveContext + : ReserveContext::makeFromAccount( + ctx.view, ctx.view.peek(keylet::account(owner)), nullptr); + increaseOwnerCount( + ctx.view, reserveCtx, 1, beast::Journal{beast::Journal::getNullSink()}); }); if (!page) @@ -417,7 +424,11 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S if (cnt != 0) { - decreaseOwnerCount(view, owner, {}, cnt, beast::Journal{beast::Journal::getNullSink()}); + decreaseOwnerCount( + view, + ReserveContext::makeFromAccount(view, view.peek(keylet::account(owner)), nullptr), + cnt, + beast::Journal{beast::Journal::getNullSink()}); } return tesSUCCESS; @@ -452,7 +463,11 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S curr->makeFieldAbsent(sfPreviousPageMin); } - decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()}); + decreaseOwnerCount( + view, + ReserveContext::makeFromAccount(view, view.peek(keylet::account(owner)), nullptr), + 1, + beast::Journal{beast::Journal::getNullSink()}); view.update(curr); view.erase(prev); @@ -507,7 +522,11 @@ removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID, S view.peek(Keylet(ltNFTOKEN_PAGE, next->key())))) cnt++; - decreaseOwnerCount(view, owner, {}, cnt, beast::Journal{beast::Journal::getNullSink()}); + decreaseOwnerCount( + view, + ReserveContext::makeFromAccount(view, view.peek(keylet::account(owner)), nullptr), + cnt, + beast::Journal{beast::Journal::getNullSink()}); return tesSUCCESS; } @@ -623,7 +642,11 @@ deleteTokenOffer(ApplyView& view, SLE::ref offer) false)) return false; - decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()}); + decreaseOwnerCount( + view, + ReserveContext::makeFromAccount(view, view.peek(keylet::account(owner)), nullptr), + 1, + beast::Journal{beast::Journal::getNullSink()}); view.erase(offer); return true; @@ -966,7 +989,11 @@ tokenOfferCreateApply( } // Update owner count. - increaseOwnerCount(view, acctID, {}, 1, j); + increaseOwnerCount( + view, + ReserveContext::makeFromAccount(view, view.peek(keylet::account(acctID)), nullptr), + 1, + j); return tesSUCCESS; } diff --git a/src/libxrpl/ledger/helpers/OfferHelpers.cpp b/src/libxrpl/ledger/helpers/OfferHelpers.cpp index d1c83f752ef..0b5626a97fa 100644 --- a/src/libxrpl/ledger/helpers/OfferHelpers.cpp +++ b/src/libxrpl/ledger/helpers/OfferHelpers.cpp @@ -55,7 +55,8 @@ offerDelete(ApplyView& view, SLE::ref sle, beast::Journal j) } } - decreaseOwnerCountForObject(view, owner, sle, 1, j); + auto const ownerSle = view.peek(keylet::account(owner)); + decreaseOwnerCountForObject(view, ownerSle, sle, 1, j); view.erase(sle); diff --git a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp index d6e1d499a46..91ee8dbea61 100644 --- a/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp +++ b/src/libxrpl/ledger/helpers/RippleStateHelpers.cpp @@ -30,7 +30,6 @@ #include #include #include -#include namespace xrpl { @@ -284,7 +283,7 @@ trustCreate( } sleRippleState->setFieldU32(sfFlags, uFlags); - increaseOwnerCount(view, sleAccount, sponsorSle, 1, j); + increaseOwnerCount(view, ReserveContext::makeFromAccount(view, sleAccount, sponsorSle), 1, j); addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsor : sfLowSponsor); @@ -379,7 +378,7 @@ updateTrustLine( // Clear the reserve of the sender, possibly delete the line! auto const currentSponsor = getLedgerEntryReserveSponsor(view, state, !bSenderHigh ? sfLowSponsor : sfHighSponsor); - decreaseOwnerCount(view, sle, currentSponsor, 1, j); + decreaseOwnerCount(view, ReserveContext::makeFromAccount(view, sle, currentSponsor), 1, j); // Clear reserve flag. state->clearFlag(senderReserveFlag); @@ -662,25 +661,14 @@ addEmptyHolding( if (ctx.view.read(index)) return tecDUPLICATE; - SLE::pointer sponsorSle; - - // A reserve sponsor only covers tx.Account's own objects. - if (!isPseudoAccount(sleDst) && accountID == ctx.tx[sfAccount]) - { - auto sle = getTxReserveSponsor(ctx); - if (!sle) - return sle.error(); // LCOV_EXCL_LINE - sponsorSle = std::move(*sle); - } - // Can the account cover the trust line reserve ? - if (auto const ret = checkInsufficientReserve( - ctx, sleDst, priorBalance, sponsorSle, {.ownerCountDelta = 1}, journal); + if (auto const ret = + checkInsufficientReserve(ctx, sleDst, priorBalance, {.ownerCountDelta = 1}, journal); !isTesSuccess(ret)) return tecNO_LINE_INSUF_RESERVE; return trustCreate( - ctx.view, + ctx, high, srcId, dstId, @@ -694,7 +682,6 @@ addEmptyHolding( /*saLimit=*/STAmount{Issue{currency, dstId}}, /*uQualityIn=*/0, /*uQualityOut=*/0, - sponsorSle, journal); } @@ -738,7 +725,11 @@ removeEmptyHolding( auto const currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor); - decreaseOwnerCount(ctx.view, sleLowAccount, currentLowSponsor, 1, journal); + decreaseOwnerCount( + ctx.view, + ReserveContext::makeFromAccount(ctx.view, sleLowAccount, currentLowSponsor), + 1, + journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -755,7 +746,11 @@ removeEmptyHolding( auto const currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor); - decreaseOwnerCount(ctx.view, sleHighAccount, currentHighSponsor, 1, journal); + decreaseOwnerCount( + ctx.view, + ReserveContext::makeFromAccount(ctx.view, sleHighAccount, currentHighSponsor), + 1, + journal); // It's not really necessary to clear the reserve flag, since the line // is about to be deleted, but this will make the metadata reflect an // accurate state at the time of deletion. @@ -817,7 +812,8 @@ deleteAMMTrustLine( if (!sleState->isFlag(uFlags)) return tecINTERNAL; // LCOV_EXCL_LINE - decreaseOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, 1, j); + decreaseOwnerCount( + view, ReserveContext::makeFromAccount(view, !ammLow ? sleLow : sleHigh, sponsorSle), 1, j); return tesSUCCESS; } diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index cbdbd13b305..af9f3a00f26 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include namespace xrpl { @@ -580,7 +581,7 @@ addEmptyHolding( { return std::visit( [&](TIss const& issue) -> TER { - return addEmptyHolding(ctx, accountID, priorBalance, issue, journal); + return addEmptyHolding(std::move(ctx), accountID, priorBalance, issue, journal); }, asset.value()); } @@ -594,7 +595,7 @@ removeEmptyHolding( { return std::visit( [&](TIss const& issue) -> TER { - return removeEmptyHolding(ctx, accountID, issue, journal); + return removeEmptyHolding(std::move(ctx), accountID, issue, journal); }, asset.value()); } @@ -721,7 +722,11 @@ directSendNoFeeIOU( // Clear the reserve of the sender, possibly delete the line! auto const currentSponsor = getLedgerEntryReserveSponsor( view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); - decreaseOwnerCount(view, view.peek(keylet::account(uSenderID)), currentSponsor, 1, j); + auto const senderSle = view.peek(keylet::account(uSenderID)); + if (!senderSle) + return tecINTERNAL; // LCOV_EXCL_LINE + decreaseOwnerCount( + view, ReserveContext::makeFromAccount(view, senderSle, currentSponsor), 1, j); removeSponsorFromLedgerEntry( sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); diff --git a/src/libxrpl/tx/paths/BookStep.cpp b/src/libxrpl/tx/paths/BookStep.cpp index bf24c66ee1d..e343f2a2067 100644 --- a/src/libxrpl/tx/paths/BookStep.cpp +++ b/src/libxrpl/tx/paths/BookStep.cpp @@ -731,11 +731,14 @@ BookStep::forEachOffer( // Create MPToken for the offer's owner. No need to check // for the reserve since the offer is removed if it is consumed. // Therefore, the owner count remains the same. - if (auto const err = checkCreateMPT(sb, assetIn.get(), owner, {}, j_); + if (auto const err = checkCreateMPT( + sb, + ReserveContext::makeFromAccount(sb, sb.peek(keylet::account(owner)), nullptr), + assetIn.get(), + owner, + j_); !isTesSuccess(err)) - { return true; - } } // It shouldn't matter from auth point of view whether it's sb diff --git a/src/libxrpl/tx/paths/MPTEndpointStep.cpp b/src/libxrpl/tx/paths/MPTEndpointStep.cpp index 0a0f6a9f27d..8e95564b836 100644 --- a/src/libxrpl/tx/paths/MPTEndpointStep.cpp +++ b/src/libxrpl/tx/paths/MPTEndpointStep.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -274,7 +275,7 @@ class MPTEndpointPaymentStep : public MPTEndpointStep // Not applicable for payment static TER - checkCreateMPT(ApplyView&, DebtDirection) + checkCreateMPTForStep(ApplyView&, DebtDirection) { return tesSUCCESS; } @@ -322,7 +323,7 @@ class MPTEndpointOfferCrossingStep : public MPTEndpointStep::revImp( return {beast::kZero, beast::kZero}; } - if (auto const err = static_cast(this)->checkCreateMPT(sb, srcDebtDir); + if (auto const err = static_cast(this)->checkCreateMPTForStep(sb, srcDebtDir); !isTesSuccess(err)) return {beast::kZero, beast::kZero}; @@ -624,7 +630,7 @@ MPTEndpointStep::fwdImp( return {beast::kZero, beast::kZero}; } - if (auto const err = static_cast(this)->checkCreateMPT(sb, srcDebtDir); + if (auto const err = static_cast(this)->checkCreateMPTForStep(sb, srcDebtDir); !isTesSuccess(err)) return {beast::kZero, beast::kZero}; diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp index fa29b45902a..677c87f8569 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipSet.cpp @@ -207,9 +207,8 @@ SponsorshipSet::doApply() bool const hasPositiveFeeAmount = feeAmount.has_value() && *feeAmount > beast::kZero; - auto reserveSponsorAccSle = getTxReserveSponsor(ctx_.getApplyViewContext()); - if (!reserveSponsorAccSle) - return reserveSponsorAccSle.error(); // LCOV_EXCL_LINE + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const reserveSponsorAccSle = applyViewContext.txReserveContext.sponsorSle; if (!sponsorshipSle) { @@ -229,7 +228,6 @@ SponsorshipSet::doApply() ctx_.getApplyViewContext(), sponsorAccSle, sponsorBalanceAfterFee.xrp(), - *reserveSponsorAccSle, {.ownerCountDelta = 1}, ctx_.journal); !isTesSuccess(ret)) @@ -269,8 +267,12 @@ SponsorshipSet::doApply() (*newSle)[sfSponseeNode] = *sponseePage; // NOLINTNEXTLINE(readability-suspicious-call-argument) - increaseOwnerCount(view(), sponsorAccSle, *reserveSponsorAccSle, 1, ctx_.journal); - addSponsorToLedgerEntry(newSle, *reserveSponsorAccSle); + increaseOwnerCount( + view(), + ReserveContext::makeFromAccount(view(), sponsorAccSle, reserveSponsorAccSle), + 1, + ctx_.journal); + addSponsorToLedgerEntry(newSle, reserveSponsorAccSle); ctx_.view().insert(newSle); return tesSUCCESS; @@ -296,7 +298,6 @@ SponsorshipSet::doApply() ctx_.getApplyViewContext(), sponsorAccSle, sponsorBalanceAfterFee.xrp(), - *reserveSponsorAccSle, {}, ctx_.journal); !isTesSuccess(ret)) diff --git a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp index d270c9e0190..d40577afdb6 100644 --- a/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp +++ b/src/libxrpl/tx/transactors/Sponsor/SponsorshipTransfer.cpp @@ -348,7 +348,6 @@ SponsorshipTransfer::doApply() ctx_.getApplyViewContext(), sponseeSle, sponseeSle->getFieldAmount(sfBalance), - newSponsorSle, {.ownerCountDelta = ownerCountDelta}, ctx_.journal); !isTesSuccess(ter)) @@ -401,7 +400,6 @@ SponsorshipTransfer::doApply() ctx_.getApplyViewContext(), sponseeSle, sponseeSle->getFieldAmount(sfBalance), - newSponsorSle, {.ownerCountDelta = ownerCountDelta}, ctx_.journal); !isTesSuccess(ter)) @@ -449,7 +447,6 @@ SponsorshipTransfer::doApply() ctx_.getApplyViewContext(), ownerSle, balanceBeforeFee(ownerSle), - SLE::pointer(), {.ownerCountDelta = ownerCountDelta}, ctx_.journal); !isTesSuccess(ter)) @@ -489,7 +486,6 @@ SponsorshipTransfer::doApply() ctx_.getApplyViewContext(), sponseeSle, sponseeSle->getFieldAmount(sfBalance), - newSponsorSle, {.accountCountDelta = 1}, ctx_.journal); !isTesSuccess(ter)) @@ -517,7 +513,6 @@ SponsorshipTransfer::doApply() ctx_.getApplyViewContext(), sponseeSle, sponseeSle->getFieldAmount(sfBalance), - newSponsorSle, {.accountCountDelta = 1}, ctx_.journal); !isTesSuccess(ter)) @@ -553,7 +548,6 @@ SponsorshipTransfer::doApply() ctx_.getApplyViewContext(), sponseeSle, balanceBeforeFee(sponseeSle), - SLE::pointer(), {.accountCountDelta = 1}, ctx_.journal); !isTesSuccess(ter)) diff --git a/src/libxrpl/tx/transactors/account/SignerListSet.cpp b/src/libxrpl/tx/transactors/account/SignerListSet.cpp index 457639195a9..748c5947309 100644 --- a/src/libxrpl/tx/transactors/account/SignerListSet.cpp +++ b/src/libxrpl/tx/transactors/account/SignerListSet.cpp @@ -319,14 +319,12 @@ SignerListSet::replaceSignerList() // We check the reserve against the starting balance because we want to // allow dipping into the reserve to pay fees. This behavior is consistent // with TicketCreate. - auto const sponsorSle = getTxReserveSponsor(ctx_.getApplyViewContext()); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const sponsorSle = applyViewContext.txReserveContext.sponsorSle; if (auto const ret = checkInsufficientReserve( ctx_.getApplyViewContext(), sle, preFeeBalance_, - *sponsorSle, {.ownerCountDelta = kAddedOwnerCount}, ctx_.journal); !isTesSuccess(ret)) @@ -352,8 +350,9 @@ SignerListSet::replaceSignerList() // If we succeeded, the new entry counts against the // creator's reserve. - increaseOwnerCount(view(), sle, *sponsorSle, kAddedOwnerCount, viewJ); - addSponsorToLedgerEntry(signerList, *sponsorSle); + increaseOwnerCount( + view(), ReserveContext::makeFromAccount(view(), sle, sponsorSle), kAddedOwnerCount, viewJ); + addSponsorToLedgerEntry(signerList, sponsorSle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp index 866ba5b6c3e..b5dca085570 100644 --- a/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp +++ b/src/libxrpl/tx/transactors/bridge/XChainBridge.cpp @@ -727,7 +727,8 @@ finalizeClaimHelper( // Remove the claim id from the ledger outerSb.erase(sleClaimID); - decreaseOwnerCount(outerSb, sleOwner, {}, 1, j); + decreaseOwnerCount( + outerSb, ReserveContext::makeFromAccount(outerSb, sleOwner, nullptr), 1, j); } } @@ -1136,7 +1137,7 @@ applyCreateAccountAttestations( return tecINTERNAL; // LCOV_EXCL_LINE // Reserve was already checked - increaseOwnerCount(psb, sleDoor, {}, 1, j); + increaseOwnerCount(psb, ReserveContext::makeFromAccount(psb, sleDoor, nullptr), 1, j); psb.insert(createdSleClaimID); psb.update(sleDoor); } @@ -1479,7 +1480,11 @@ XChainCreateBridge::doApply() (*sleBridge)[sfOwnerNode] = *page; } - increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal); + increaseOwnerCount( + ctx_.view(), + ReserveContext::makeFromAccount(ctx_.view(), sleAcct, nullptr), + 1, + ctx_.journal); ctx_.view().insert(sleBridge); ctx_.view().update(sleAcct); @@ -2040,7 +2045,11 @@ XChainCreateClaimID::doApply() (*sleClaimID)[sfOwnerNode] = *page; } - increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal); + increaseOwnerCount( + ctx_.view(), + ReserveContext::makeFromAccount(ctx_.view(), sleAcct, nullptr), + 1, + ctx_.journal); ctx_.view().insert(sleClaimID); ctx_.view().update(sleBridge); diff --git a/src/libxrpl/tx/transactors/check/CheckCancel.cpp b/src/libxrpl/tx/transactors/check/CheckCancel.cpp index ed60817224d..e5908c6d2fb 100644 --- a/src/libxrpl/tx/transactors/check/CheckCancel.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCancel.cpp @@ -91,7 +91,7 @@ CheckCancel::doApply() } // If we succeeded, update the check owner's reserve. - decreaseOwnerCountForObject(view(), srcId, sleCheck, 1, viewJ); + decreaseOwnerCountForObject(view(), view().peek(keylet::account(srcId)), sleCheck, 1, viewJ); // Remove check from ledger. view().erase(sleCheck); diff --git a/src/libxrpl/tx/transactors/check/CheckCash.cpp b/src/libxrpl/tx/transactors/check/CheckCash.cpp index 3597dd83589..ba183e5d425 100644 --- a/src/libxrpl/tx/transactors/check/CheckCash.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCash.cpp @@ -389,10 +389,7 @@ CheckCash::doApply() STAmount const flowDeliver{ optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)}; - auto applyViewContext = ApplyViewContext({.view = psb, .tx = ctx_.tx}); - auto const sponsorSle = getTxReserveSponsor(applyViewContext); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto applyViewContext = ApplyViewContext::makeFromTx(psb, ctx_.tx); // Check reserve. Return destination account SLE if enough reserve, // otherwise return nullptr. @@ -401,12 +398,7 @@ CheckCash::doApply() // Can the account cover the trust line's or MPT reserve? if (auto const ret = checkInsufficientReserve( - applyViewContext, - sleDst, - preFeeBalance_, - *sponsorSle, - {.ownerCountDelta = 1}, - j_); + applyViewContext, sleDst, preFeeBalance_, {.ownerCountDelta = 1}, j_); !isTesSuccess(ret)) { JLOG(j_.trace()) << "Trust line does not exist. " @@ -449,8 +441,10 @@ CheckCash::doApply() STAmount initialBalance(flowDeliver.asset()); initialBalance.get().account = noAccount(); + // The ApplyViewContext overload derives the sponsor + // (XLS-68: only when sleDst is the tx.Account). if (TER const ter = trustCreate( - psb, // payment sandbox + applyViewContext, // apply-view context destLow, // is dest low? deliverIssuer, // source accountID_, // destination @@ -464,7 +458,6 @@ CheckCash::doApply() Issue(currency, accountID_), // limit of zero 0, // quality in 0, // quality out - *sponsorSle, // sponsor viewJ); // journal !isTesSuccess(ter)) { @@ -511,8 +504,8 @@ CheckCash::doApply() if (sleDst == nullptr) return tecINSUFFICIENT_RESERVE; - if (auto const err = - checkCreateMPT(psb, mptID, accountID_, *sponsorSle, j_); + if (auto const err = checkCreateMPT( + psb, applyViewContext.txReserveContext, mptID, accountID_, j_); !isTesSuccess(err)) { return err; @@ -598,7 +591,7 @@ CheckCash::doApply() } // If we succeeded, update the check owner's reserve. - decreaseOwnerCountForObject(psb, srcId, sleCheck, 1, viewJ); + decreaseOwnerCountForObject(psb, psb.peek(keylet::account(srcId)), sleCheck, 1, viewJ); // Remove check from ledger. psb.erase(sleCheck); diff --git a/src/libxrpl/tx/transactors/check/CheckCreate.cpp b/src/libxrpl/tx/transactors/check/CheckCreate.cpp index 10d5b778fdf..08e6b2d9a5e 100644 --- a/src/libxrpl/tx/transactors/check/CheckCreate.cpp +++ b/src/libxrpl/tx/transactors/check/CheckCreate.cpp @@ -195,16 +195,10 @@ CheckCreate::doApply() // A check counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - auto const sponsorSle = getTxReserveSponsor(ctx_.getApplyViewContext()); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const sponsorSle = applyViewContext.txReserveContext.sponsorSle; if (auto const ret = checkInsufficientReserve( - ctx_.getApplyViewContext(), - sle, - preFeeBalance_, - *sponsorSle, - {.ownerCountDelta = 1}, - ctx_.journal); + ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, ctx_.journal); !isTesSuccess(ret)) return ret; // Note that we use the value from the sequence or ticket as the @@ -260,8 +254,8 @@ CheckCreate::doApply() } // If we succeeded, the new entry counts against the creator's reserve. - increaseOwnerCount(view(), sle, *sponsorSle, 1, viewJ); - addSponsorToLedgerEntry(sleCheck, *sponsorSle); + increaseOwnerCount(view(), ReserveContext::makeFromAccount(view(), sle, sponsorSle), 1, viewJ); + addSponsorToLedgerEntry(sleCheck, sponsorSle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp index 85befd51b13..150710b4e0a 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialAccept.cpp @@ -116,8 +116,8 @@ CredentialAccept::doApply() sleCred->setFieldU32(sfFlags, lsfAccepted); view().update(sleCred); - decreaseOwnerCount(view(), sleIssuer, {}, 1, j_); - increaseOwnerCount(view(), sleSubject, {}, 1, j_); + decreaseOwnerCount(view(), ReserveContext::makeFromAccount(view(), sleIssuer, nullptr), 1, j_); + increaseOwnerCount(view(), ReserveContext::makeFromAccount(view(), sleSubject, nullptr), 1, j_); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp index 57896aa025a..f70af71c952 100644 --- a/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp +++ b/src/libxrpl/tx/transactors/credentials/CredentialCreate.cpp @@ -152,7 +152,8 @@ CredentialCreate::doApply() return tecDIR_FULL; sleCred->setFieldU64(sfIssuerNode, *page); - increaseOwnerCount(view(), sleIssuer, {}, 1, j_); + increaseOwnerCount( + view(), ReserveContext::makeFromAccount(view(), sleIssuer, nullptr), 1, j_); } if (subject == accountID_) diff --git a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp index 9fc4a810291..ea27e8a3b65 100644 --- a/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp +++ b/src/libxrpl/tx/transactors/delegate/DelegateSet.cpp @@ -98,15 +98,12 @@ DelegateSet::doApply() auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); if (permissions.empty()) return tecINTERNAL; // LCOV_EXCL_LINE - - auto const sponsorSle = getTxReserveSponsor(ctx_.getApplyViewContext()); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const sponsorSle = applyViewContext.txReserveContext.sponsorSle; if (auto const ret = checkInsufficientReserve( ctx_.getApplyViewContext(), sleOwner, preFeeBalance_, - *sponsorSle, {.ownerCountDelta = 1}, ctx_.journal); !isTesSuccess(ret)) @@ -138,8 +135,12 @@ DelegateSet::doApply() (*sle)[sfDestinationNode] = *destPage; ctx_.view().insert(sle); - increaseOwnerCount(ctx_.view(), sleOwner, *sponsorSle, 1, ctx_.journal); - addSponsorToLedgerEntry(sle, *sponsorSle); + increaseOwnerCount( + ctx_.view(), + ReserveContext::makeFromAccount(ctx_.view(), sleOwner, sponsorSle), + 1, + ctx_.journal); + addSponsorToLedgerEntry(sle, sponsorSle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp index 7c7d35497a9..c54ca15fbbb 100644 --- a/src/libxrpl/tx/transactors/dex/AMMCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMCreate.cpp @@ -332,7 +332,9 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou return err; } - if (auto const err = createMPToken(sb, mptID, accountId, {}, flags); + // AMM is a pseudo-account, so it is never reserve-sponsored. + if (auto const err = createMPToken( + sb, mptID, ReserveContext::makeFromAccount(sb, acc, nullptr), flags); !isTesSuccess(err)) return err; // Don't adjust AMM owner count. diff --git a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp index 4d6524bba3a..4958c10b0c4 100644 --- a/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMWithdraw.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -685,11 +686,14 @@ AMMWithdraw::withdraw( !isTesSuccess(err)) return err; - if (auto const err = checkCreateMPT(view, mptIssue, account, {}, journal); + if (auto const err = checkCreateMPT( + view, + ReserveContext::makeFromAccount(view, view.peek(keylet::account(account)), {}), + mptIssue, + account, + journal); !isTesSuccess(err)) - { return err; - } } return tesSUCCESS; }; diff --git a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp index fb47cf0f971..0368c361986 100644 --- a/src/libxrpl/tx/transactors/dex/OfferCreate.cpp +++ b/src/libxrpl/tx/transactors/dex/OfferCreate.cpp @@ -867,7 +867,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel) } // Update owner count. - increaseOwnerCount(sb, sleCreator, {}, 1, viewJ); + increaseOwnerCount(sb, ReserveContext::makeFromAccount(sb, sleCreator, nullptr), 1, viewJ); JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.asset()) << " : " << to_string(saTakerGets.asset()) diff --git a/src/libxrpl/tx/transactors/did/DIDDelete.cpp b/src/libxrpl/tx/transactors/did/DIDDelete.cpp index 8c5d0362720..85866a25ad0 100644 --- a/src/libxrpl/tx/transactors/did/DIDDelete.cpp +++ b/src/libxrpl/tx/transactors/did/DIDDelete.cpp @@ -50,7 +50,7 @@ DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, b if (!sleOwner) return tecINTERNAL; // LCOV_EXCL_LINE - decreaseOwnerCount(view, sleOwner, {}, 1, j); + decreaseOwnerCount(view, ReserveContext::makeFromAccount(view, sleOwner, nullptr), 1, j); view.update(sleOwner); // Remove object from ledger diff --git a/src/libxrpl/tx/transactors/did/DIDSet.cpp b/src/libxrpl/tx/transactors/did/DIDSet.cpp index 2659460b8dd..636b055942b 100644 --- a/src/libxrpl/tx/transactors/did/DIDSet.cpp +++ b/src/libxrpl/tx/transactors/did/DIDSet.cpp @@ -90,7 +90,11 @@ addSLE(ApplyContext& ctx, SLE::ref sle, AccountID const& owner) return tecDIR_FULL; // LCOV_EXCL_LINE (*sle)[sfOwnerNode] = *page; } - increaseOwnerCount(ctx.view(), sleAccount, {}, 1, ctx.journal); + increaseOwnerCount( + ctx.view(), + ReserveContext::makeFromAccount(ctx.view(), sleAccount, nullptr), + 1, + ctx.journal); ctx.view().update(sleAccount); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp index 4b2f853b1bb..a3fc2848d69 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowCreate.cpp @@ -437,16 +437,15 @@ EscrowCreate::doApply() STAmount const amount{ctx_.tx[sfAmount]}; auto const balance = sle->getFieldAmount(sfBalance).xrp(); - auto const sponsorSle = getTxReserveSponsor(ctx_.getApplyViewContext()); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const sponsorSle = applyViewContext.txReserveContext.sponsorSle; // First check: whoever is on the hook for the new owner increment // can cover it. When sponsored this hits the sponsor branch and // validates the sponsor's reserve + remaining credit. When // unsponsored this hits the source branch and validates the // source's pre-lock balance against base + (currentOC+1)*increment. if (auto const ret = checkInsufficientReserve( - ctx_.getApplyViewContext(), sle, balance, *sponsorSle, {.ownerCountDelta = 1}, j_); + ctx_.getApplyViewContext(), sle, balance, {.ownerCountDelta = 1}, j_); !isTesSuccess(ret)) return ret; @@ -461,13 +460,11 @@ EscrowCreate::doApply() // - sponsored: adj=0 — sponsor covers the new owner increment, // so the source only owes its base reserve. // - unsponsored: adj=1 — source owes base + the new increment. - std::int32_t const ownerCountAdj = *sponsorSle ? 0 : 1; if (auto const ret = checkInsufficientReserve( ctx_.getApplyViewContext(), sle, balance - STAmount(amount).xrp(), - {}, - {.ownerCountDelta = ownerCountAdj}, + {.ownerCountDelta = sponsorSle ? 0 : 1}, j_); !isTesSuccess(ret)) return tecUNFUNDED; @@ -561,8 +558,12 @@ EscrowCreate::doApply() } // increment owner count - increaseOwnerCount(ctx_.view(), sle, *sponsorSle, 1, ctx_.journal); - addSponsorToLedgerEntry(slep, *sponsorSle); + increaseOwnerCount( + ctx_.view(), + ReserveContext::makeFromAccount(ctx_.view(), sle, sponsorSle), + 1, + ctx_.journal); + addSponsorToLedgerEntry(slep, sponsorSle); ctx_.view().update(sle); return tesSUCCESS; } diff --git a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp index 3483384869c..e76cbd60eff 100644 --- a/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp +++ b/src/libxrpl/tx/transactors/escrow/EscrowFinish.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -224,10 +223,6 @@ EscrowFinish::preclaim(PreclaimContext const& ctx) } } - auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx); - if (!sponsorSle) - return sponsorSle.error(); - return tesSUCCESS; } @@ -396,7 +391,8 @@ EscrowFinish::doApply() ctx_.view().update(sled); // Adjust source owner count - decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal); + decreaseOwnerCountForObject( + ctx_.view(), ctx_.view().peek(keylet::account(account)), slep, 1, ctx_.journal); // Remove escrow from ledger ctx_.view().erase(slep); diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp index 240a4e30161..40a0792eaf9 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerDelete.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -195,7 +196,8 @@ LoanBrokerDelete::doApply() // Decreases the owner count by two: one for the LoanBroker object, and // one for the pseudo-account. - decreaseOwnerCount(view(), owner, {}, 2, j_); + // LoanBroker object can be sponsored + decreaseOwnerCount(view(), ReserveContext::makeFromAccount(view(), owner, nullptr), 2, j_); } associateAsset(*broker, vaultAsset); diff --git a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp index e9c153404ca..71554f93fc3 100644 --- a/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanBrokerSet.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -238,7 +239,7 @@ LoanBrokerSet::doApply() // Increases the owner count by two: one for the LoanBroker object, and // one for the pseudo-account. - increaseOwnerCount(view, owner, {}, 2, j_); + increaseOwnerCount(view, ReserveContext::makeFromAccount(view, owner, nullptr), 2, j_); if (preFeeBalance_ < accountReserve(view, owner, j_)) return tecINSUFFICIENT_RESERVE; diff --git a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp index e5d3942d477..699bb1c43fc 100644 --- a/src/libxrpl/tx/transactors/lending/LoanDelete.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanDelete.cpp @@ -4,6 +4,7 @@ #include // IWYU pragma: keep #include #include +#include #include #include #include @@ -109,7 +110,7 @@ LoanDelete::doApply() // Decrement the LoanBroker's owner count. // The broker's owner count is solely for the number of outstanding loans, // and is distinct from the broker's pseudo-account's owner count - decreaseOwnerCount(view, brokerSle, {}, 1, j_); + decreaseOwnerCount(view, ReserveContext::makeFromAccount(view, brokerSle, nullptr), 1, j_); // If there are no loans left, then any remaining debt must be forgiven, // because there is no other way to pay it back. @@ -130,7 +131,7 @@ LoanDelete::doApply() } } // Decrement the borrower's owner count - decreaseOwnerCount(view, borrowerSle, {}, 1, j_); + decreaseOwnerCount(view, ReserveContext::makeFromAccount(view, borrowerSle, nullptr), 1, j_); // These associations shouldn't do anything, but do them just to be safe associateAsset(*loanSle, vaultAsset); diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index c22c1405a5f..7f477456351 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -519,7 +520,7 @@ LoanSet::doApply() } } - increaseOwnerCount(view, borrowerSle, {}, 1, j_); + increaseOwnerCount(view, ReserveContext::makeFromAccount(view, borrowerSle, nullptr), 1, j_); { auto const balance = @@ -646,7 +647,7 @@ LoanSet::doApply() adjustImpreciseNumber(brokerSle->at(sfDebtTotal), newDebtDelta, vaultAsset, vaultScale); // The broker's owner count is solely for the number of outstanding loans, // and is distinct from the broker's pseudo-account's owner count - increaseOwnerCount(view, brokerSle, {}, 1, j_); + increaseOwnerCount(view, ReserveContext::makeFromAccount(view, brokerSle, nullptr), 1, j_); loanSequenceProxy += 1; // The sequence should be extremely unlikely to roll over, but fail if it // does diff --git a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp index 41bb0517689..f3838ee67df 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenAcceptOffer.cpp @@ -373,7 +373,9 @@ NFTokenAcceptOffer::transferNFToken( std::uint32_t const buyerOwnerCountBefore = sleBuyer->getFieldU32(sfOwnerCount); - auto const insertRet = nft::insertToken(view(), buyer, std::move(tokenAndPage->token)); + auto applyViewContext = ctx_.getApplyViewContext(); + auto const insertRet = + nft::insertToken(applyViewContext, buyer, std::move(tokenAndPage->token)); // There was an issue where the buyer accepts a sell offer, the ledger // didn't check if the buyer has enough reserve, meaning that buyer can get diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index 9a158aafca6..cfb692e8ff5 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -305,7 +305,8 @@ NFTokenMint::doApply() object.setFieldVL(sfURI, *uri); }); - if (TER const ret = nft::insertToken(ctx_.view(), accountID_, std::move(newToken)); + auto applyViewContext = ctx_.getApplyViewContext(); + if (TER const ret = nft::insertToken(applyViewContext, accountID_, std::move(newToken)); !isTesSuccess(ret)) return ret; diff --git a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp index e1974a0bcdb..aae86d795a5 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleDelete.cpp @@ -71,7 +71,7 @@ OracleDelete::deleteOracle( return tecINTERNAL; // LCOV_EXCL_LINE std::uint32_t const count = sle->getFieldArray(sfPriceDataSeries).size() > 5 ? 2 : 1; - decreaseOwnerCount(view, sleOwner, {}, count, j); + decreaseOwnerCount(view, ReserveContext::makeFromAccount(view, sleOwner, nullptr), count, j); view.erase(sle); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp index 8cb87304802..629a13f4806 100644 --- a/src/libxrpl/tx/transactors/oracle/OracleSet.cpp +++ b/src/libxrpl/tx/transactors/oracle/OracleSet.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -185,15 +186,19 @@ adjustOracleOwnerCount(ApplyContext& ctx, int count) { if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) { + auto reserveCtx = ctx.getApplyViewContext().txReserveContext; + XRPL_ASSERT( + !reserveCtx.isSponsored(), + "OracleSet::adjustOracleOwnerCount : OracleSet is not reserve-sponsored"); if (count > 0) { increaseOwnerCount( - ctx.view(), sleAccount, {}, static_cast(count), ctx.journal); + ctx.view(), reserveCtx, static_cast(count), ctx.journal); } else if (count < 0) { decreaseOwnerCount( - ctx.view(), sleAccount, {}, static_cast(-count), ctx.journal); + ctx.view(), reserveCtx, static_cast(-count), ctx.journal); } return true; } diff --git a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp index 195e4d2df75..bac527245eb 100644 --- a/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp +++ b/src/libxrpl/tx/transactors/payment/DepositPreauth.cpp @@ -162,16 +162,10 @@ DepositPreauth::doApply() // A preauth counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - auto const sponsorSle = getTxReserveSponsor(applyViewContext); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const sponsorSle = applyViewContext.txReserveContext.sponsorSle; if (auto const ret = checkInsufficientReserve( - applyViewContext, - sleOwner, - preFeeBalance_, - *sponsorSle, - {.ownerCountDelta = 1}, - j_); + applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_); !isTesSuccess(ret)) return ret; @@ -197,8 +191,9 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - increaseOwnerCount(view(), sleOwner, *sponsorSle, 1, j_); - addSponsorToLedgerEntry(slePreauth, *sponsorSle); + increaseOwnerCount( + view(), ReserveContext::makeFromAccount(view(), sleOwner, sponsorSle), 1, j_); + addSponsorToLedgerEntry(slePreauth, sponsorSle); } else if (ctx_.tx.isFieldPresent(sfUnauthorize)) { @@ -215,16 +210,10 @@ DepositPreauth::doApply() // A preauth counts against the reserve of the issuing account, but we // check the starting balance because we want to allow dipping into the // reserve to pay fees. - auto const sponsorSle = getTxReserveSponsor(applyViewContext); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const sponsorSle = applyViewContext.txReserveContext.sponsorSle; if (auto const ret = checkInsufficientReserve( - applyViewContext, - sleOwner, - preFeeBalance_, - *sponsorSle, - {.ownerCountDelta = 1}, - j_); + applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_); !isTesSuccess(ret)) return ret; @@ -264,8 +253,9 @@ DepositPreauth::doApply() slePreauth->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - increaseOwnerCount(view(), sleOwner, *sponsorSle, 1, j_); - addSponsorToLedgerEntry(slePreauth, *sponsorSle); + increaseOwnerCount( + view(), ReserveContext::makeFromAccount(view(), sleOwner, sponsorSle), 1, j_); + addSponsorToLedgerEntry(slePreauth, sponsorSle); } else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) { diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp index 1b82fcf88f5..90334a0955e 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp @@ -135,9 +135,8 @@ PaymentChannelCreate::doApply() return tecEXPIRED; } - auto const sponsorSle = getTxReserveSponsor(ctx_.getApplyViewContext()); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const sponsorSle = applyViewContext.txReserveContext.sponsorSle; if (ctx_.view().rules().enabled(featureSponsor)) { @@ -147,12 +146,7 @@ PaymentChannelCreate::doApply() // unsponsored this hits the source branch and validates the // source's pre-lock balance against base + (currentOC+1)*increment. if (auto const ret = checkInsufficientReserve( - ctx_.getApplyViewContext(), - sle, - preFeeBalance_, - *sponsorSle, - {.ownerCountDelta = 1}, - j_); + ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, j_); !isTesSuccess(ret)) return ret; @@ -164,12 +158,11 @@ PaymentChannelCreate::doApply() // - sponsored: adj=0 — sponsor covers the new owner increment, // so the source only owes its base reserve. // - unsponsored: adj=1 — source owes base + the new increment. - std::int32_t const ownerCountAdj = *sponsorSle ? 0 : 1; + std::int32_t const ownerCountAdj = sponsorSle ? 0 : 1; if (auto const ret = checkInsufficientReserve( ctx_.getApplyViewContext(), sle, preFeeBalance_ - ctx_.tx[sfAmount].xrp(), - {}, {.ownerCountDelta = ownerCountAdj}, j_); !isTesSuccess(ret)) @@ -223,8 +216,12 @@ PaymentChannelCreate::doApply() // Deduct owner's balance, increment owner count (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; - increaseOwnerCount(ctx_.view(), sle, *sponsorSle, 1, ctx_.journal); - addSponsorToLedgerEntry(slep, *sponsorSle); + increaseOwnerCount( + ctx_.view(), + ReserveContext::makeFromAccount(ctx_.view(), sle, sponsorSle), + 1, + ctx_.journal); + addSponsorToLedgerEntry(slep, sponsorSle); ctx_.view().update(sle); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp index ab0635068b7..ab5e8b903ea 100644 --- a/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp +++ b/src/libxrpl/tx/transactors/payment_channel/PaymentChannelFund.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -90,16 +89,14 @@ PaymentChannelFund::doApply() { // Check reserve and funds availability auto const balance = (*sle)[sfBalance]; - auto const sponsorSle = getTxReserveSponsor(ctx_.getApplyViewContext()); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE - if (auto const ret = checkInsufficientReserve( - ctx_.getApplyViewContext(), sle, balance, *sponsorSle, {}, j_); + auto const applyViewContext = ctx_.getApplyViewContext(); + if (auto const ret = + checkInsufficientReserve(ctx_.getApplyViewContext(), sle, balance, {}, j_); !isTesSuccess(ret)) return ret; if (auto const ret = checkInsufficientReserve( - ctx_.getApplyViewContext(), sle, balance - ctx_.tx[sfAmount], {}, {}, j_); + ctx_.getApplyViewContext(), sle, balance - ctx_.tx[sfAmount], {}, j_); !isTesSuccess(ret)) return tecUNFUNDED; } diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp index ccbf95be75a..fa7d9752cbb 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -65,7 +66,8 @@ PermissionedDomainDelete::doApply() XRPL_ASSERT( ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0, "xrpl::PermissionedDomainDelete::doApply : nonzero owner count"); - decreaseOwnerCount(view(), ownerSle, {}, 1, ctx_.journal); + decreaseOwnerCount( + view(), ReserveContext::makeFromAccount(view(), ownerSle, nullptr), 1, ctx_.journal); view().erase(slePd); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp index c2df114e486..dc4fbe46954 100644 --- a/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp +++ b/src/libxrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -126,7 +127,8 @@ PermissionedDomainSet::doApply() slePd->setFieldU64(sfOwnerNode, *page); // If we succeeded, the new entry counts against the creator's reserve. - increaseOwnerCount(view(), ownerSle, {}, 1, ctx_.journal); + increaseOwnerCount( + view(), ReserveContext::makeFromAccount(view(), ownerSle, nullptr), 1, ctx_.journal); view().insert(slePd); } diff --git a/src/libxrpl/tx/transactors/system/TicketCreate.cpp b/src/libxrpl/tx/transactors/system/TicketCreate.cpp index d1cda85f4ee..7db54f06e66 100644 --- a/src/libxrpl/tx/transactors/system/TicketCreate.cpp +++ b/src/libxrpl/tx/transactors/system/TicketCreate.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -125,7 +126,11 @@ TicketCreate::doApply() sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount); // Every added Ticket counts against the creator's reserve. - increaseOwnerCount(view(), accountID_, {}, ticketCount, viewJ); + increaseOwnerCount( + view(), + ReserveContext::makeFromAccount(view(), view().peek(keylet::account(accountID_)), nullptr), + ticketCount, + viewJ); // TicketCreate is the only transaction that can cause an account root's // Sequence field to increase by more than one. October 2018. diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index a1a75ac5311..88f6e6b16ba 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -24,7 +24,6 @@ #include #include #include -#include namespace xrpl { @@ -124,19 +123,12 @@ MPTokenIssuanceCreate::create( if (!acct) return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE - SLE::pointer sponsorSle; - if (!isPseudoAccount(acct)) - { - auto sle = getTxReserveSponsor(ctx); - if (!sle) - return std::unexpected(sle.error()); - sponsorSle = std::move(*sle); - } + SLE::pointer sponsorSle = isPseudoAccount(acct) ? nullptr : ctx.txReserveContext.sponsorSle; if (args.priorBalance) { if (auto const ret = checkInsufficientReserve( - ctx, acct, *(args.priorBalance), sponsorSle, {.ownerCountDelta = 1}, journal); + ctx, acct, *(args.priorBalance), {.ownerCountDelta = 1}, journal); !isTesSuccess(ret)) return std::unexpected(ret); // tecINSUFFICIENT_RESERVE } @@ -199,7 +191,8 @@ MPTokenIssuanceCreate::create( } // Update owner count. - increaseOwnerCount(ctx.view, acct, sponsorSle, 1, journal); + increaseOwnerCount( + ctx.view, ReserveContext::makeFromAccount(ctx.view, acct, sponsorSle), 1, journal); return mptId; } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp index 05d39c3596e..c99e783cb5b 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceDestroy.cpp @@ -49,7 +49,7 @@ MPTokenIssuanceDestroy::doApply() if (!view().dirRemove(keylet::ownerDir(accountID_), (*mpt)[sfOwnerNode], mpt->key(), false)) return tefBAD_LEDGER; // LCOV_EXCL_LINE - decreaseOwnerCountForObject(view(), accountID_, mpt, 1, j_); + decreaseOwnerCountForObject(view(), view().peek(keylet::account(accountID_)), mpt, 1, j_); view().erase(mpt); return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/token/TrustSet.cpp b/src/libxrpl/tx/transactors/token/TrustSet.cpp index cf3f05152d4..a9f0655de31 100644 --- a/src/libxrpl/tx/transactors/token/TrustSet.cpp +++ b/src/libxrpl/tx/transactors/token/TrustSet.cpp @@ -327,17 +327,15 @@ TrustSet::doApply() // but the incremental reserve for the trust line as // well. A person with no intention of using the gateway // could use the extra XRP for their own purposes. + auto const applyViewContext = ctx_.getApplyViewContext(); + auto const sponsorSle = applyViewContext.txReserveContext.sponsorSle; - auto const sponsorSle = getTxReserveSponsor(ctx_.getApplyViewContext()); - if (!sponsorSle) - return sponsorSle.error(); // LCOV_EXCL_LINE - - std::uint32_t const uOwnerCount = ownerCount(*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 // prefunded budget), so the reserve check always runs. - bool const freeTrustLine = uOwnerCount < 2 && !*sponsorSle; + bool const freeTrustLine = uOwnerCount < 2 && !sponsorSle; std::uint32_t const uQualityIn(bQualityIn ? ctx_.tx.getFieldU32(sfQualityIn) : 0); std::uint32_t uQualityOut(bQualityOut ? ctx_.tx.getFieldU32(sfQualityOut) : 0); @@ -542,17 +540,20 @@ TrustSet::doApply() ctx_.getApplyViewContext(), sleLowAccount, preFeeBalance_, - *sponsorSle, {.ownerCountDelta = 1}, j_); - *sponsorSle && !isTesSuccess(ret)) + sponsorSle && !isTesSuccess(ret)) return tecINSUF_RESERVE_LINE; // Set reserve for low account. - increaseOwnerCount(view(), sleLowAccount, *sponsorSle, 1, viewJ); + increaseOwnerCount( + view(), + ReserveContext::makeFromAccount(view(), sleLowAccount, sponsorSle), + 1, + viewJ); uFlagsOut |= lsfLowReserve; - addSponsorToLedgerEntry(sleRippleState, *sponsorSle, sfLowSponsor); + addSponsorToLedgerEntry(sleRippleState, sponsorSle, sfLowSponsor); if (!bHigh) bReserveIncrease = true; @@ -561,7 +562,11 @@ TrustSet::doApply() if (bLowReserveClear && bLowReserved) { // Clear reserve for low account. - decreaseOwnerCount(view(), sleLowAccount, currentLowSponsor, 1, viewJ); + decreaseOwnerCount( + view(), + ReserveContext::makeFromAccount(view(), sleLowAccount, currentLowSponsor), + 1, + viewJ); uFlagsOut &= ~lsfLowReserve; removeSponsorFromLedgerEntry(sleRippleState, sfLowSponsor); @@ -576,17 +581,20 @@ TrustSet::doApply() ctx_.getApplyViewContext(), sleHighAccount, preFeeBalance_, - *sponsorSle, {.ownerCountDelta = 1}, j_); - *sponsorSle && !isTesSuccess(ret)) + sponsorSle && !isTesSuccess(ret)) return tecINSUF_RESERVE_LINE; // Set reserve for high account. - increaseOwnerCount(view(), sleHighAccount, *sponsorSle, 1, viewJ); + increaseOwnerCount( + view(), + ReserveContext::makeFromAccount(view(), sleHighAccount, sponsorSle), + 1, + viewJ); uFlagsOut |= lsfHighReserve; - addSponsorToLedgerEntry(sleRippleState, *sponsorSle, sfHighSponsor); + addSponsorToLedgerEntry(sleRippleState, sponsorSle, sfHighSponsor); if (bHigh) bReserveIncrease = true; @@ -595,7 +603,11 @@ TrustSet::doApply() if (bHighReserveClear && bHighReserved) { // Clear reserve for high account. - decreaseOwnerCount(view(), sleHighAccount, currentHighSponsor, 1, viewJ); + decreaseOwnerCount( + view(), + ReserveContext::makeFromAccount(view(), sleHighAccount, currentHighSponsor), + 1, + viewJ); uFlagsOut &= ~lsfHighReserve; removeSponsorFromLedgerEntry(sleRippleState, sfHighSponsor); @@ -612,8 +624,8 @@ TrustSet::doApply() } // Reserve is not scaled by load. else if ( - auto const ret = checkInsufficientReserve( - ctx_.getApplyViewContext(), sle, preFeeBalance_, *sponsorSle, {}, j_); + auto const ret = + checkInsufficientReserve(ctx_.getApplyViewContext(), sle, preFeeBalance_, {}, j_); !freeTrustLine && bReserveIncrease && !isTesSuccess(ret)) { JLOG(j_.trace()) << "Delay transaction: Insufficent reserve to " @@ -647,7 +659,6 @@ TrustSet::doApply() ctx_.getApplyViewContext(), sle, preFeeBalance_, - *sponsorSle, {.ownerCountDelta = 1}, j_); !freeTrustLine && !isTesSuccess(ret)) // Reserve is not scaled by load. @@ -670,7 +681,7 @@ TrustSet::doApply() // Create a new ripple line. terResult = trustCreate( - view(), + applyViewContext, bHigh, accountID_, uDstAccountID, @@ -684,7 +695,6 @@ TrustSet::doApply() saLimitAllow, // Limit for who is being charged. uQualityIn, uQualityOut, - *sponsorSle, viewJ); } diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index e1f5873a897..832a46d1e07 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -157,7 +158,7 @@ VaultCreate::doApply() if (auto ter = dirLink(view(), accountID_, vault)) return ter; // We will create Vault and PseudoAccount, hence increase OwnerCount by 2 - increaseOwnerCount(view(), owner, {}, 2, j_); + increaseOwnerCount(view(), ReserveContext::makeFromAccount(view(), owner, nullptr), 2, j_); if (preFeeBalance_ < accountReserve(view(), owner, j_)) return tecINSUFFICIENT_RESERVE; diff --git a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp index 551e3501fa0..3365c429b58 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDelete.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDelete.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -154,7 +155,7 @@ VaultDelete::doApply() return tefBAD_LEDGER; // LCOV_EXCL_STOP } - decreaseOwnerCount(view(), pseudoAcct, {}, 1, j_); + decreaseOwnerCount(view(), ReserveContext::makeFromAccount(view(), pseudoAcct, nullptr), 1, j_); view().erase(mpt); @@ -213,7 +214,7 @@ VaultDelete::doApply() } // We are destroying Vault and PseudoAccount, hence decrease by 2 - decreaseOwnerCount(view(), owner, {}, 2, j_); + decreaseOwnerCount(view(), ReserveContext::makeFromAccount(view(), owner, nullptr), 2, j_); // Destroy the vault. view().erase(vault); diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index ff1a46f6382..774cd3f41bb 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -337,7 +337,11 @@ class Invariants_test : public beast::unit_test::Suite // check. sleA1->at(sfBalance) = beast::kZero; BEAST_EXPECT(sleA1->at(sfOwnerCount) == 0); - increaseOwnerCount(ac.view(), sleA1, {}, 1, ac.journal); + increaseOwnerCount( + ac.view(), + ReserveContext::makeFromAccount(ac.view(), sleA1, nullptr), + 1, + ac.journal); ac.view().erase(sleA1); diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 7e6b2612d18..9e389b476ba 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -5930,8 +5930,11 @@ class Vault_test : public beast::unit_test::Suite auto const dummyTx = *env.jt(noop(holder)).stx; BEAST_EXPECT( - removeEmptyHolding({sb, dummyTx}, holder.id(), MPTIssue(mpt.issuanceID()), j) == - tecHAS_OBLIGATIONS); + removeEmptyHolding( + ApplyViewContext::makeFromTx(sb, dummyTx), + holder.id(), + MPTIssue(mpt.issuanceID()), + j) == tecHAS_OBLIGATIONS); BEAST_EXPECT(sb.peek(tokenKeylet) != nullptr); } return true;