Compare commits

...
Author SHA1 Message Date
Mayukha Vadari 16389dcb2e refactor: Move decreaseOwnerCount onto AccountRootEntry
Replace the entry-taking decreaseOwnerCount free function with a
writable member of AccountRootEntry, and drop the temporary friend
declaration from the previous change. The member uses the entry's view
and journal, so call sites pass their journal to the entry constructor.
The AccountID-based overload stays as a shim.
2026-10-05 15:46:48 +01:00
Mayukha Vadari 97a780b29f refactor: Move increaseOwnerCount onto AccountRootEntry
Replace both entry-taking increaseOwnerCount free functions with
writable members of AccountRootEntry. The members use the entry's view
and journal, so call sites pass their journal to the entry constructor.
The AccountID-based overload stays as a shim.

adjustOwnerCountSigned becomes a private member. decreaseOwnerCount is
a temporary friend so it can still call it until it moves too.
adjustOwnerCountImpl moves to namespace detail, because
adjustLoanBrokerOwnerCount also uses it.
2026-10-05 15:46:44 +01:00
Mayukha Vadari 654edc9a08 refactor: Move checkReserve onto AccountRootEntry
Replace both entry-taking checkReserve free functions with const members
of AccountRootEntry. The members keep the ApplyViewContext parameter,
because the check needs the transaction, and drop the journal parameter:
call sites pass their journal to the entry constructor instead.
2026-10-05 15:46:39 +01:00
Mayukha Vadari 658d04f694 Document confineOwnerCount parameters 2026-10-05 15:46:31 +01:00
Mayukha Vadari 93498952b1 refactor: Move ownerCount onto AccountRootEntry
Replace the entry-taking ownerCount(sle, j, adj) free function with the
const member AccountRootEntry::ownerCount(adj). The member uses the
entry's journal, so call sites pass their journal to the entry
constructor.

confineOwnerCount moves from the anonymous namespace to namespace
detail, because both the member and the owner-count helpers use it.
2026-10-05 15:45:43 +01:00
Mayukha Vadari 269f79e568 Include Keylet.h in the SLEBase test
(cherry picked from commit 3cca9d3266)
2026-10-05 15:45:38 +01:00
Mayukha Vadari e7a8e0336a test: Drop extern template declarations from SLEBase test
An explicit instantiation declaration stops clang from emitting the
inline constructors of the header-only entries at -O0, so the debug
builds fail to link xrpl_tests (undefined AccountRootEntry constructors).
Naming each class in a static_assert already instantiates its definition,
which is all the exhaustiveness check needs.

(cherry picked from commit 611415e1f9)
2026-10-05 15:45:38 +01:00
Mayukha Vadari 1d7e40777a refactor: Move accountReserve onto AccountRootEntry
Replace the entry-taking accountReserve(view, sle, j, adj) free function
with the const member AccountRootEntry::reserve(adj). The member uses the
entry's own view and journal, so call sites now pass their journal to the
entry constructor. The AccountID-based accountReserve overload stays as a
one-line shim.

Adjustment moves to AccountRootEntry.h, since the member takes it.
accountCountImpl moves from the anonymous namespace to namespace detail,
because both reserve() and xrpLiquid() use it.
2026-10-05 15:45:38 +01:00
Mayukha Vadari 7bb43a243b test: Declare, not define, entry instantiations in SLEBase test
The exhaustive per-entry-type check in the SLEBase gtest used explicit
instantiation definitions. Once an entry class gets out-of-line member
bodies in its own .cpp (with the explicit instantiation definition
there), the test would instantiate the class a second time, and MSVC
rejects it with C4661 because those member bodies are not visible in
the test translation unit. Explicit instantiation declarations still
instantiate the class definition, which is all the check needs.

(cherry picked from commit 8b07abc3cd)
2026-10-05 15:45:37 +01:00
Mayukha Vadari d697b7843c Document escrowDestNotAccountResult and isPseudoAccountSle parameters and return value 2026-10-05 15:45:24 +01:00
Mayukha Vadari 2de5140f20 Drop unused include from SponsorshipTransfer.cpp 2026-10-05 13:33:31 +01:00
Mayukha Vadari edf085a697 Declare canWithdraw with AccountRootEntryR in View.h 2026-10-05 13:33:31 +01:00
Mayukha Vadari c35ac1339b fix: Do not adopt the escrow SLE into an AccountRootEntry
Before fixCleanup3_2_0, EscrowCancel passes the escrow SLE to
escrowUnlockApplyHelper as sleDest. Wrapping it in AccountRootEntryR
fires the "adopted SLE matches bound entry type" assertion in Debug
builds (xrpl.app.EscrowToken "IOU Cancel DoApply").

When sleDest is not an AccountRoot, return what the old code returned
for it: the tx reserve sponsor's error if the escrow owner submitted
the tx, else tefINTERNAL from checkReserve's type check.

(cherry picked from commit 90bb3930be)
2026-10-05 13:33:31 +01:00
Mayukha Vadari dc1cfbc474 Drop unused include from AccountRootHelpers.h 2026-10-05 13:33:30 +01:00
Mayukha Vadari 3044073401 refactor: Use AccountRootEntry everywhere
Replace raw AccountRoot SLE pointers with the AccountRootEntryR and
AccountRootEntryW wrappers in src/libxrpl and src/xrpld/app.

- view.read(keylet::account(...)) becomes AccountRootEntryR and
  view.peek(keylet::account(...)) becomes AccountRootEntryW.
- AccountRoot helpers (accountReserve, checkReserve, ownerCount,
  increase/decreaseOwnerCount, isPseudoAccount,
  checkDestinationAndTag) and the sponsor helpers
  (getTxReserveSponsor, getEffectiveTxReserveSponsor,
  getLedgerEntryReserveSponsor) take or return the wrappers. A missing
  sponsor is std::nullopt instead of a null pointer.
- Generic functions that are not migrated yet (trustCreate, dirLink,
  addSponsorToLedgerEntry, ...) get rawSle() or mutableRawSle().
- Invariant checks cannot build a view-bound entry, so they use
  isPseudoAccountSle(), now declared in InvariantCheck.h.

This changes pointers only. No helper moves onto the wrapper. It is a
pure refactor with no behavior change and no amendment.
2026-10-05 13:33:30 +01:00
BartandBart 651bb207b4 refactor: Build Throw messages with std::format (#8474)
Co-authored-by: Bart <[email protected]>
2026-10-03 16:45:34 +00:00
Peter Chen c45363fd8b feat: Implement Confidential mpt holder key update (#8266) 2026-10-02 22:54:38 +00:00
Chenna Keshava B S f1744cb76e fix: Do not block MPToken deletion on unrelated confidential balances (#8209) 2026-10-02 22:54:31 +00:00
132 changed files with 3601 additions and 1308 deletions

No files matched your search

+1
View File
@@ -255,6 +255,7 @@ words:
- queuable - queuable
- Raphson - Raphson
- rcflags - rcflags
- reencrypted
- replayer - replayer
- repodata - repodata
- repomd - repomd
+2 -1
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h> #include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
@@ -211,7 +212,7 @@ canWithdraw(
ReadView const& view, ReadView const& view,
AccountID const& from, AccountID const& from,
AccountID const& to, AccountID const& to,
SLE::ConstRef toSle, AccountRootEntryR const& toSle,
STAmount const& amount, STAmount const& amount,
bool hasDestinationTag, bool hasDestinationTag,
std::optional<std::vector<UInt256>> const& credentialIDs = std::nullopt); std::optional<std::vector<UInt256>> const& credentialIDs = std::nullopt);
@@ -7,9 +7,20 @@
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <optional>
namespace xrpl { namespace xrpl {
struct Adjustment
{
std::int32_t ownerCountDelta = 0;
std::int32_t accountCountDelta = 0;
};
template <typename ViewT> template <typename ViewT>
class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT> class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT>
{ {
@@ -27,6 +38,123 @@ public:
: Base(keylet::account(id), view, j) : Base(keylet::account(id), view, j)
{ {
} }
/**
* Returns the account reserve, in drops.
*
* 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 adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative
* to subtract.
* @return The account reserve amount in drops
*/
[[nodiscard]] XRPAmount
reserve(Adjustment adj = {}) const;
/**
* Return number of the objects which reserve is covered by the account (so called "owner
* count"). Actual owner count can be adjusted by delta in ownerCountAdj.
*
* @param ownerCountAdj Adjustment to the owner count (default: 0)
* @return The adjusted owner count
*/
[[nodiscard]] std::uint32_t
ownerCount(std::int32_t ownerCountAdj = 0) const;
/**
* Check if this account has sufficient reserve.
*
* @param ctx The apply-view context (view + tx)
* @param accBalance The account's balance
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative
* to subtract.
* @param insufReserveCode The transaction result code to return if the reserve is
* insufficient (default: tecINSUFFICIENT_RESERVE).
* @return Transaction result code
*/
[[nodiscard]] TER
checkReserve(
ApplyViewContext ctx,
XRPAmount accBalance,
std::optional<AccountRootEntry<ReadView>> const& sponsorSle,
Adjustment adj,
TER insufReserveCode = tecINSUFFICIENT_RESERVE) const;
/**
* Check if this account has sufficient reserve, deriving the sponsor internally.
*
* Equivalent to the overload above, but resolves the sponsor via
* getEffectiveTxReserveSponsor(ctx, *this) instead of taking it explicitly. Use this
* in the common case where the sponsor is simply the transaction's reserve
* sponsor for this account. Callers that must force the account's-own-reserve branch
* (passing a null sponsor) or supply a different sponsor should use the
* explicit overload above.
*
* @param ctx The apply-view context (view + tx)
* @param accBalance The account's balance
* @param adj Reserve adjustments (owner/account count deltas)
* @return Transaction result code
*/
[[nodiscard]] TER
checkReserve(ApplyViewContext ctx, XRPAmount accBalance, Adjustment adj) const;
/**
* Increase owner-count fields when the caller supplies the sponsor.
*
* This helper does not create a ledger object. It updates reserve accounting
* after the caller has created/updated an object.
* If sponsorSle is provided, this also adjusts the account's sponsored count
* and the sponsor's sponsoring count.
*
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param count Amount to add to the owner count
*/
void
increaseOwnerCount(std::optional<AccountRootEntry<ApplyView>>& sponsorSle, std::uint32_t count)
requires Base::kIsWritable;
/**
* Increase owner-count fields, deriving the tx reserve sponsor internally.
*
* Equivalent to the overload above, but resolves the sponsor via
* getEffectiveTxReserveSponsor(ctx, *this) instead of taking it explicitly. Use
* this when the sponsor is the transaction's reserve sponsor for this account
* (the common create path). Deletion paths, which derive the sponsor from an
* object's sfSponsor field, should keep using the explicit overload.
*
* @param ctx The apply-view context (view + tx)
* @param count Amount to add to the owner count
*/
void
increaseOwnerCount(ApplyViewContext ctx, std::uint32_t count)
requires Base::kIsWritable;
/**
* Decrease owner-count fields when the caller supplies the sponsor.
*
* This helper does not delete a ledger object. It updates reserve accounting
* after the caller has removed an owner-counted reserve, or for special
* owner-count changes whose sponsor cannot be derived from an object's
* sfSponsor field.
*
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param count Amount to remove from the owner count
*/
void
decreaseOwnerCount(std::optional<AccountRootEntry<ApplyView>>& sponsorSle, std::uint32_t count)
requires Base::kIsWritable;
private:
void
adjustOwnerCountSigned(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::int32_t adjustment)
requires Base::kIsWritable;
}; };
using AccountRootEntryR = AccountRootEntry<ReadView>; using AccountRootEntryR = AccountRootEntry<ReadView>;
+60 -164
View File
@@ -4,8 +4,8 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Rate.h> #include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
@@ -46,30 +46,55 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer);
[[nodiscard]] XRPAmount [[nodiscard]] XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
struct Adjustment namespace detail {
{
std::int32_t ownerCountDelta = 0;
std::int32_t accountCountDelta = 0;
};
/** /**
* Returns the account reserve, in drops. * An owner count cannot be negative. If adjustment would cause a negative
* owner count, clamp the owner count at 0. Similarly for overflow. This
* adjustment allows the ownerCount to be adjusted up or down in multiple steps.
* If id != std::nullopt, then do error reporting.
* *
* Actual owner count can be adjusted by delta in ownerCountAdj * Shared by AccountRootEntry::ownerCount() and the owner-count helpers.
* 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 currentOwnerCount The owner count before adjustment
* @param sle The ledger entry for the account * @param ownerCountAdj Positive to add to count, negative to reduce count
* @param id The account ID to use for error reporting, or std::nullopt to
* skip error reporting
* @param j Journal for logging * @param j Journal for logging
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to * @return The adjusted owner count
* subtract.
* @return The account reserve amount in drops
*/ */
[[nodiscard]] XRPAmount std::uint32_t
accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjustment adj = {}); confineOwnerCount(
std::uint32_t currentOwnerCount,
std::int32_t ownerCountAdj,
std::optional<AccountID> const& id = std::nullopt,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
/**
* Clamps and writes an owner-count field of sle, then updates sle in view.
*
* Shared by AccountRootEntry and adjustLoanBrokerOwnerCount().
*
* @return The adjusted owner count
*/
std::uint32_t
adjustOwnerCountImpl(
ApplyView& view,
SLE::Ref sle,
SF_UINT32 const& sfield,
AccountID const& accID,
std::int32_t ownerCountAdj,
beast::Journal j);
/**
* Returns the number of account reserves funded by this account: 1 for itself
* (0 if sponsored by another account) plus the count of accounts it sponsors.
* Shared by AccountRootEntry::reserve() and xrpLiquid().
*/
std::uint32_t
accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, beast::Journal j);
} // namespace detail
/** /**
* Convenience overload that accepts AccountID instead of SLE. * Convenience overload that accepts AccountID instead of SLE.
@@ -84,114 +109,9 @@ accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjust
[[nodiscard]] inline XRPAmount [[nodiscard]] inline XRPAmount
accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {}) accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {})
{ {
return accountReserve(view, view.read(keylet::account(id)), j, adj); return AccountRootEntryR(id, view, j).reserve(adj);
} }
/**
* Check if an account has sufficient 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 adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to
* subtract.
* @param j Journal for logging (default: null sink)
* @param insufReserveCode The transaction result code to return if the reserve is insufficient
* (default: tecINSUFFICIENT_RESERVE).
* @return Transaction result code
*/
[[nodiscard]] TER
checkReserve(
ApplyViewContext ctx,
SLE::ConstRef accSle,
XRPAmount accBalance,
SLE::ConstRef sponsorSle,
Adjustment adj,
beast::Journal j,
TER insufReserveCode = tecINSUFFICIENT_RESERVE);
/**
* Check if an account has sufficient reserve, deriving the sponsor internally.
*
* Equivalent to the overload above, but resolves the sponsor via
* getEffectiveTxReserveSponsor(ctx, accSle) instead of taking it explicitly. Use this
* in the common case where the sponsor is simply the transaction's reserve
* sponsor for accSle. Callers that must force the account's-own-reserve branch
* (passing a null sponsor) or supply a different sponsor should use the
* explicit overload above.
*
* @param ctx The apply-view context (view + tx)
* @param accSle The account's ledger entry
* @param accBalance The account's balance
* @param adj Reserve adjustments (owner/account count deltas)
* @param j Journal for logging (default: null sink)
* @return Transaction result code
*/
[[nodiscard]] TER
checkReserve(
ApplyViewContext ctx,
SLE::ConstRef accSle,
XRPAmount accBalance,
Adjustment adj,
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(SLE::ConstRef sle, beast::Journal j, std::int32_t ownerCountAdj = 0);
/**
* Increase owner-count fields when the caller supplies the sponsor.
*
* This helper does not create a ledger object. It updates reserve accounting
* after the caller has created/updated an object.
* If sponsorSle is provided, this also adjusts the account's sponsored count
* 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 count Amount to add to the owner count
* @param j Journal for logging
*/
void
increaseOwnerCount(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::uint32_t count,
beast::Journal j);
/**
* Increase owner-count fields, deriving the tx reserve sponsor internally.
*
* Equivalent to the overload above, but resolves the sponsor via
* getEffectiveTxReserveSponsor(ctx, accountSle) instead of taking it explicitly. Use
* this when the sponsor is the transaction's reserve sponsor for accountSle
* (the common create path). Deletion paths, which derive the sponsor from an
* object's sfSponsor field, should keep using the explicit overload.
*
* @param ctx The apply-view context (view + tx)
* @param accountSle The account's ledger entry
* @param count Amount to add to the owner count
* @param j Journal for logging
*/
void
increaseOwnerCount(
ApplyViewContext ctx,
SLE::Ref accountSle,
std::uint32_t count,
beast::Journal j);
/** /**
* Convenience overload that accepts AccountID instead of SLE references. * Convenience overload that accepts AccountID instead of SLE references.
* *
@@ -209,36 +129,13 @@ increaseOwnerCount(
std::uint32_t count, std::uint32_t count,
beast::Journal j) beast::Journal j)
{ {
increaseOwnerCount( AccountRootEntryW accountSle(account, view, j);
view, std::optional<AccountRootEntryW> sponsorSle;
view.peek(keylet::account(account)), if (sponsor)
sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), sponsorSle.emplace(*sponsor, view);
count, accountSle.increaseOwnerCount(sponsorSle, count);
j);
} }
/**
* Decrease owner-count fields when the caller supplies the sponsor.
*
* This helper does not delete a ledger object. It updates reserve accounting
* after the caller has removed an owner-counted reserve, or for special
* owner-count changes whose sponsor cannot be derived from an object's
* 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 count Amount to remove from the owner count
* @param j Journal for logging
*/
void
decreaseOwnerCount(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::uint32_t count,
beast::Journal j);
/** /**
* Convenience overload that accepts AccountID instead of SLE references. * Convenience overload that accepts AccountID instead of SLE references.
* *
@@ -256,12 +153,11 @@ decreaseOwnerCount(
std::uint32_t count, std::uint32_t count,
beast::Journal j) beast::Journal j)
{ {
decreaseOwnerCount( AccountRootEntryW accountSle(account, view, j);
view, std::optional<AccountRootEntryW> sponsorSle;
view.peek(keylet::account(account)), if (sponsor)
sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(), sponsorSle.emplace(*sponsor, view);
count, accountSle.decreaseOwnerCount(sponsorSle, count);
j);
} }
/** /**
@@ -281,7 +177,7 @@ decreaseOwnerCount(
void void
decreaseOwnerCountForObject( decreaseOwnerCountForObject(
ApplyView& view, ApplyView& view,
SLE::Ref accountSle, AccountRootEntryW& accountSle,
SLE::Ref objectSle, SLE::Ref objectSle,
std::uint32_t count, std::uint32_t count,
beast::Journal j); beast::Journal j);
@@ -303,7 +199,7 @@ decreaseOwnerCountForObject(
std::uint32_t count, std::uint32_t count,
beast::Journal j) beast::Journal j)
{ {
SLE::Ref accountSle = view.peek(keylet::account(account)); AccountRootEntryW accountSle(account, view);
decreaseOwnerCountForObject(view, accountSle, objectSle, count, j); decreaseOwnerCountForObject(view, accountSle, objectSle, count, j);
} }
@@ -367,7 +263,7 @@ getPseudoAccountFields();
* - null pointer * - null pointer
*/ */
[[nodiscard]] bool [[nodiscard]] bool
isPseudoAccount(SLE::const_pointer sleAcct); isPseudoAccount(AccountRootEntryR const& sleAcct);
/** /**
* Convenience overload that reads the account from the view. * Convenience overload that reads the account from the view.
@@ -375,7 +271,7 @@ isPseudoAccount(SLE::const_pointer sleAcct);
[[nodiscard]] inline bool [[nodiscard]] inline bool
isPseudoAccount(ReadView const& view, AccountID const& accountId) isPseudoAccount(ReadView const& view, AccountID const& accountId)
{ {
return isPseudoAccount(view.read(keylet::account(accountId))); return isPseudoAccount(AccountRootEntryR(accountId, view));
} }
/** /**
@@ -396,6 +292,6 @@ createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const
* - If the SLE requires a destination tag, checks that there is a tag. * - If the SLE requires a destination tag, checks that there is a tag.
*/ */
[[nodiscard]] TER [[nodiscard]] TER
checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag); checkDestinationAndTag(AccountRootEntryR const& toSle, bool hasDestinationTag);
} // namespace xrpl } // namespace xrpl
+62 -31
View File
@@ -4,7 +4,7 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -27,6 +27,32 @@
namespace xrpl { namespace xrpl {
namespace detail {
/**
* Before fixCleanup3_2_0, EscrowCancel passes the escrow SLE as sleDest.
* Return what the old reserve check returned for it, without adopting a
* non-AccountRoot SLE into an AccountRootEntry: the tx reserve sponsor's
* error if the escrow owner submitted the tx, else tefINTERNAL.
*
* @param ctx the apply view context for the escrow transaction.
* @param sleDest the non-AccountRoot SLE that was passed as the destination.
* @return the tx reserve sponsor's error code if the escrow owner submitted
* the tx, else tefINTERNAL.
*/
inline TER
escrowDestNotAccountResult(ApplyViewContext ctx, SLE::ConstRef sleDest)
{
if (sleDest->getAccountID(sfAccount) == ctx.tx[sfAccount])
{
if (auto const sponsor = getTxReserveSponsor(ctx); !sponsor)
return sponsor.error(); // LCOV_EXCL_LINE
}
return tefINTERNAL;
}
} // namespace detail
template <ValidIssueType T> template <ValidIssueType T>
TER TER
escrowUnlockApplyHelper( escrowUnlockApplyHelper(
@@ -70,18 +96,17 @@ escrowUnlockApplyHelper<Issue>(
if (!ctx.view.exists(trustLineKey) && createAsset) if (!ctx.view.exists(trustLineKey) && createAsset)
{ {
// Can the account cover the trust line's reserve? // Can the account cover the trust line's reserve?
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest); if (sleDest->getType() != ltACCOUNT_ROOT)
return detail::escrowDestNotAccountResult(ctx, sleDest);
auto const destSle = AccountRootEntryR(sleDest, ctx.view, journal);
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, destSle);
if (!sponsorSle) if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE return sponsorSle.error(); // LCOV_EXCL_LINE
if (auto const ret = checkReserve( auto& sponsor = *sponsorSle;
ctx,
sleDest, if (auto const ret = destSle.checkReserve(
xrpBalance, ctx, xrpBalance, sponsor, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE);
*sponsorSle,
{.ownerCountDelta = 1},
journal,
tecNO_LINE_INSUF_RESERVE);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
JLOG(journal.trace()) << "Trust line does not exist. " JLOG(journal.trace()) << "Trust line does not exist. "
@@ -94,22 +119,22 @@ escrowUnlockApplyHelper<Issue>(
initialBalance.get<Issue>().account = noAccount(); initialBalance.get<Issue>().account = noAccount();
if (TER const ter = trustCreate( if (TER const ter = trustCreate(
ctx.view, // payment sandbox ctx.view, // payment sandbox
recvLow, // is dest low? recvLow, // is dest low?
issuer, // source issuer, // source
receiver, // destination receiver, // destination
trustLineKey.key, // ledger index trustLineKey.key, // ledger index
sleDest, // Account to add to sleDest, // Account to add to
false, // authorize account false, // authorize account
!sleDest->isFlag(lsfDefaultRipple), // !sleDest->isFlag(lsfDefaultRipple), //
false, // freeze trust line false, // freeze trust line
false, // deep freeze trust line false, // deep freeze trust line
initialBalance, // zero initial balance initialBalance, // zero initial balance
Issue(currency, receiver), // limit of zero Issue(currency, receiver), // limit of zero
0, // quality in 0, // quality in
0, // quality out 0, // quality out
*sponsorSle, // sponsor sponsor ? sponsor->mutableRawSle() : SLE::pointer(), // sponsor
journal); // journal journal); // journal
!isTesSuccess(ter)) !isTesSuccess(ter))
{ {
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
@@ -204,23 +229,29 @@ escrowUnlockApplyHelper<MPTIssue>(
auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver); auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver);
if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer) if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer)
{ {
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest); if (sleDest->getType() != ltACCOUNT_ROOT)
return detail::escrowDestNotAccountResult(ctx, sleDest);
auto const destSle = AccountRootEntryR(sleDest, ctx.view, journal);
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, destSle);
if (!sponsorSle) if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE return sponsorSle.error(); // LCOV_EXCL_LINE
if (auto const ret = checkReserve( auto& sponsor = *sponsorSle;
ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal);
if (auto const ret = destSle.checkReserve(ctx, xrpBalance, sponsor, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0); if (auto const ter = createMPToken(
ctx.view, mptID, receiver, sponsor ? sponsor->mutableRawSle() : SLE::pointer(), 0);
!isTesSuccess(ter)) !isTesSuccess(ter))
{ {
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
// update owner count. // update owner count.
increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal); AccountRootEntryW receiverSle(receiver, ctx.view, journal);
receiverSle.increaseOwnerCount(sponsor, 1);
} }
if (!ctx.view.exists(mptKeylet) && !receiverIssuer) if (!ctx.view.exists(mptKeylet) && !receiverIssuer)
+12 -11
View File
@@ -2,6 +2,7 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
@@ -54,11 +55,11 @@ getTxReserveSponsorID(STTx const& tx);
* Return a mutable SLE for the transaction's reserve sponsor account. * Return a mutable SLE for the transaction's reserve sponsor account.
* *
* @param ctx The apply-view context (view + tx) * @param ctx The apply-view context (view + tx)
* @return The sponsor account SLE, a null pointer if the tx is not * @return The sponsor account entry, nullopt if the tx is not
* reserve-sponsored, or tecINTERNAL if the sponsor account cannot * reserve-sponsored, or tecINTERNAL if the sponsor account cannot
* be loaded (an already-checked invariant). * be loaded (an already-checked invariant).
*/ */
std::expected<SLE::pointer, TER> std::expected<std::optional<AccountRootEntryW>, TER>
getTxReserveSponsor(ApplyViewContext ctx); getTxReserveSponsor(ApplyViewContext ctx);
/** /**
@@ -66,29 +67,29 @@ getTxReserveSponsor(ApplyViewContext ctx);
* *
* @param view The ledger read view * @param view The ledger read view
* @param tx The transaction to inspect * @param tx The transaction to inspect
* @return The sponsor account SLE, a null pointer if the tx is not * @return The sponsor account entry, nullopt if the tx is not
* reserve-sponsored, or tecINTERNAL if the sponsor account cannot * reserve-sponsored, or tecINTERNAL if the sponsor account cannot
* be loaded (an already-checked invariant). * be loaded (an already-checked invariant).
*/ */
std::expected<SLE::const_pointer, TER> std::expected<std::optional<AccountRootEntryR>, TER>
getTxReserveSponsor(ReadView const& view, STTx const& tx); getTxReserveSponsor(ReadView const& view, STTx const& tx);
/** /**
* The transaction's reserve sponsor for the given account, if applicable. * The transaction's reserve sponsor for the given account, if applicable.
* *
* A reserve sponsor only covers the transaction submitter's own objects, so * A reserve sponsor only covers the transaction submitter's own objects, so
* this returns the tx reserve sponsor SLE only when accountSle is the tx's own * this returns the tx reserve sponsor entry only when accountSle is the tx's
* (non-pseudo) account; otherwise it returns a null sponsor pointer. This is * own (non-pseudo) account; otherwise it returns nullopt. This is
* the single source of truth for the "sponsor applies to tx.Account only" rule * the single source of truth for the "sponsor applies to tx.Account only" rule
* that the sponsor-deriving helper overloads in AccountRootHelpers rely on. * that the sponsor-deriving helper overloads in AccountRootHelpers rely on.
* *
* @param ctx The apply-view context (view + tx) * @param ctx The apply-view context (view + tx)
* @param accountSle The account whose sponsor is being resolved * @param accountSle The account whose sponsor is being resolved
* @return The sponsor SLE (nullptr if unsponsored), or tecINTERNAL if the * @return The sponsor entry (nullopt if unsponsored), or tecINTERNAL if the
* sponsor account cannot be loaded (an already-checked invariant) * sponsor account cannot be loaded (an already-checked invariant)
*/ */
[[nodiscard]] std::expected<SLE::pointer, TER> [[nodiscard]] std::expected<std::optional<AccountRootEntryW>, TER>
getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle); getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle);
/** /**
* Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent. * Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent.
@@ -105,9 +106,9 @@ getLedgerEntryReserveSponsorID(SLE::ConstRef sle, SF_ACCOUNT const& field = sfSp
* @param view The mutable apply view * @param view The mutable apply view
* @param sle The ledger entry whose sponsor field is inspected * @param sle The ledger entry whose sponsor field is inspected
* @param field The field that holds the sponsor AccountID (defaults to sfSponsor) * @param field The field that holds the sponsor AccountID (defaults to sfSponsor)
* @return The sponsor account SLE, or a null pointer if the entry is unsponsored. * @return The sponsor account entry, or nullopt if the entry is unsponsored.
*/ */
SLE::pointer std::optional<AccountRootEntryW>
getLedgerEntryReserveSponsor( getLedgerEntryReserveSponsor(
ApplyView& view, ApplyView& view,
SLE::ConstRef sle, SLE::ConstRef sle,
+6
View File
@@ -238,6 +238,12 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfSponsorshipEnd, 0x00010000) \ TF_FLAG(tfSponsorshipEnd, 0x00010000) \
TF_FLAG(tfSponsorshipCreate, 0x00020000) \ TF_FLAG(tfSponsorshipCreate, 0x00020000) \
TF_FLAG(tfSponsorshipReassign, 0x00040000), \ TF_FLAG(tfSponsorshipReassign, 0x00040000), \
MASK_ADJ(0)) \
\
TRANSACTION(ConfidentialMPTHolderKeyUpdate, \
TF_FLAG(tfHolderKeyRotation, 0x00010000) \
TF_FLAG(tfHolderKeyRecovery, 0x00020000) \
TF_FLAG(tfCancelRecovery, 0x00040000), \
MASK_ADJ(0)) MASK_ADJ(0))
// clang-format on // clang-format on
@@ -439,6 +439,7 @@ LEDGER_ENTRY(ltMPTOKEN, 0x007f, MPToken, mptoken, ({
{sfIssuerKeyMirrorEpoch, SoeOptional}, {sfIssuerKeyMirrorEpoch, SoeOptional},
{sfAuditorKeyMirrorEpoch, SoeOptional}, {sfAuditorKeyMirrorEpoch, SoeOptional},
{sfHolderEncryptionKey, SoeOptional}, {sfHolderEncryptionKey, SoeOptional},
{sfRecoveryKey, SoeOptional},
})) }))
/** A ledger object which tracks Oracle /** A ledger object which tracks Oracle
@@ -330,6 +330,7 @@ TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
TYPED_SFIELD(sfAmountCommitment, VL, 45) TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46) TYPED_SFIELD(sfBalanceCommitment, VL, 46)
TYPED_SFIELD(sfBytecode, VL, 47) TYPED_SFIELD(sfBytecode, VL, 47)
TYPED_SFIELD(sfRecoveryKey, VL, 48)
// account (common) // account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1) TYPED_SFIELD(sfAccount, ACCOUNT, 1)
@@ -1147,12 +1147,27 @@ TRANSACTION(ttCONFIDENTIAL_MPT_MIRROR_UPDATE, 92, ConfidentialMPTMirrorUpdate,
{sfZKProof, SoeRequired}, {sfZKProof, SoeRequired},
})) }))
/** This transaction rotates or recovers a confidential MPT holder's ElGamal encryption key,
or cancels a recovery. */
#if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/token/ConfidentialMPTHolderKeyUpdate.h>
#endif
TRANSACTION(ttCONFIDENTIAL_MPT_HOLDER_KEY_UPDATE, 93, ConfidentialMPTHolderKeyUpdate,
({.amendment = featureConfidentialMPTKeyRotation}),
({
{sfMPTokenIssuanceID, SoeRequired},
{sfHolderEncryptionKey, SoeOptional},
{sfConfidentialBalanceSpending, SoeOptional},
{sfConfidentialBalanceInbox, SoeOptional},
{sfZKProof, SoeOptional},
}))
/** This transaction posts an unsigned transaction on-ledger as a /** This transaction posts an unsigned transaction on-ledger as a
TransactionProposal, pending multi-signature collection. */ TransactionProposal, pending multi-signature collection. */
#if TRANSACTION_INCLUDE #if TRANSACTION_INCLUDE
# include <xrpl/tx/transactors/proposal/TransactionProposalCreate.h> # include <xrpl/tx/transactors/proposal/TransactionProposalCreate.h>
#endif #endif
TRANSACTION(ttTRANSACTION_PROPOSAL_CREATE, 93, TransactionProposalCreate, TRANSACTION(ttTRANSACTION_PROPOSAL_CREATE, 95, TransactionProposalCreate,
({.amendment = featureCosign}), ({.amendment = featureCosign}),
({ ({
{sfProposedTransaction, SoeRequired}, {sfProposedTransaction, SoeRequired},
@@ -339,6 +339,30 @@ public:
{ {
return this->sle_->isFieldPresent(sfHolderEncryptionKey); return this->sle_->isFieldPresent(sfHolderEncryptionKey);
} }
/**
* @brief Get sfRecoveryKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getRecoveryKey() const
{
if (hasRecoveryKey())
return this->sle_->at(sfRecoveryKey);
return std::nullopt;
}
/**
* @brief Check if sfRecoveryKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasRecoveryKey() const
{
return this->sle_->isFieldPresent(sfRecoveryKey);
}
}; };
/** /**
@@ -552,6 +576,17 @@ public:
return *this; return *this;
} }
/**
* @brief Set sfRecoveryKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenBuilder&
setRecoveryKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfRecoveryKey] = value;
return *this;
}
/** /**
* @brief Build and return the completed MPToken wrapper. * @brief Build and return the completed MPToken wrapper.
* @param index The ledger entry index. * @param index The ledger entry index.
@@ -0,0 +1,279 @@
// This file is auto-generated. Do not edit.
#pragma once
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/protocol/jss.h>
#include <xrpl/protocol_autogen/TransactionBase.h>
#include <xrpl/protocol_autogen/TransactionBuilderBase.h>
#include <xrpl/json/json_value.h>
#include <stdexcept>
#include <optional>
namespace xrpl::transactions {
class ConfidentialMPTHolderKeyUpdateBuilder;
/**
* @brief Transaction: ConfidentialMPTHolderKeyUpdate
*
* Type: ttCONFIDENTIAL_MPT_HOLDER_KEY_UPDATE (93)
* Delegable: Delegation::NotDelegable
* Amendment: featureConfidentialMPTKeyRotation
* Privileges: Privilege::NoPriv
*
* Immutable wrapper around STTx providing type-safe field access.
* Use ConfidentialMPTHolderKeyUpdateBuilder to construct new transactions.
*/
class ConfidentialMPTHolderKeyUpdate : public TransactionBase
{
public:
static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_HOLDER_KEY_UPDATE;
/**
* @brief Construct a ConfidentialMPTHolderKeyUpdate transaction wrapper from an existing STTx object.
* @throws std::runtime_error if the transaction type doesn't match.
*/
explicit ConfidentialMPTHolderKeyUpdate(std::shared_ptr<STTx const> tx)
: TransactionBase(std::move(tx))
{
// Verify transaction type
if (tx_->getTxnType() != txType)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTHolderKeyUpdate");
}
}
// Transaction-specific field getters
/**
* @brief Get sfMPTokenIssuanceID (SoeRequired)
* @return The field value.
*/
[[nodiscard]]
SF_UINT192::type::value_type
getMPTokenIssuanceID() const
{
return this->tx_->at(sfMPTokenIssuanceID);
}
/**
* @brief Get sfHolderEncryptionKey (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getHolderEncryptionKey() const
{
if (hasHolderEncryptionKey())
{
return this->tx_->at(sfHolderEncryptionKey);
}
return std::nullopt;
}
/**
* @brief Check if sfHolderEncryptionKey is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasHolderEncryptionKey() const
{
return this->tx_->isFieldPresent(sfHolderEncryptionKey);
}
/**
* @brief Get sfConfidentialBalanceSpending (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getConfidentialBalanceSpending() const
{
if (hasConfidentialBalanceSpending())
{
return this->tx_->at(sfConfidentialBalanceSpending);
}
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceSpending is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceSpending() const
{
return this->tx_->isFieldPresent(sfConfidentialBalanceSpending);
}
/**
* @brief Get sfConfidentialBalanceInbox (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getConfidentialBalanceInbox() const
{
if (hasConfidentialBalanceInbox())
{
return this->tx_->at(sfConfidentialBalanceInbox);
}
return std::nullopt;
}
/**
* @brief Check if sfConfidentialBalanceInbox is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasConfidentialBalanceInbox() const
{
return this->tx_->isFieldPresent(sfConfidentialBalanceInbox);
}
/**
* @brief Get sfZKProof (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getZKProof() const
{
if (hasZKProof())
{
return this->tx_->at(sfZKProof);
}
return std::nullopt;
}
/**
* @brief Check if sfZKProof is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasZKProof() const
{
return this->tx_->isFieldPresent(sfZKProof);
}
};
/**
* @brief Builder for ConfidentialMPTHolderKeyUpdate transactions.
*
* Provides a fluent interface for constructing transactions with method chaining.
* Uses STObject internally for flexible transaction construction.
* Inherits common field setters from TransactionBuilderBase.
*/
class ConfidentialMPTHolderKeyUpdateBuilder : public TransactionBuilderBase<ConfidentialMPTHolderKeyUpdateBuilder>
{
public:
/**
* @brief Construct a new ConfidentialMPTHolderKeyUpdateBuilder with required fields.
* @param account The account initiating the transaction.
* @param mPTokenIssuanceID The sfMPTokenIssuanceID field value.
* @param sequence Optional sequence number for the transaction.
* @param fee Optional fee for the transaction.
*/
ConfidentialMPTHolderKeyUpdateBuilder(SF_ACCOUNT::type::value_type account,
std::decay_t<typename SF_UINT192::type::value_type> const& mPTokenIssuanceID, std::optional<SF_UINT32::type::value_type> sequence = std::nullopt,
std::optional<SF_AMOUNT::type::value_type> fee = std::nullopt
)
: TransactionBuilderBase<ConfidentialMPTHolderKeyUpdateBuilder>(ttCONFIDENTIAL_MPT_HOLDER_KEY_UPDATE, account, sequence, fee)
{
setMPTokenIssuanceID(mPTokenIssuanceID);
}
/**
* @brief Construct a ConfidentialMPTHolderKeyUpdateBuilder from an existing STTx object.
* @param tx The existing transaction to copy from.
* @throws std::runtime_error if the transaction type doesn't match.
*/
ConfidentialMPTHolderKeyUpdateBuilder(std::shared_ptr<STTx const> tx)
{
if (tx->getTxnType() != ttCONFIDENTIAL_MPT_HOLDER_KEY_UPDATE)
{
throw std::runtime_error("Invalid transaction type for ConfidentialMPTHolderKeyUpdateBuilder");
}
object_ = *tx;
}
/**
* @brief Transaction-specific field setters
*/
/**
* @brief Set sfMPTokenIssuanceID (SoeRequired)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTHolderKeyUpdateBuilder&
setMPTokenIssuanceID(std::decay_t<typename SF_UINT192::type::value_type> const& value)
{
object_[sfMPTokenIssuanceID] = value;
return *this;
}
/**
* @brief Set sfHolderEncryptionKey (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTHolderKeyUpdateBuilder&
setHolderEncryptionKey(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfHolderEncryptionKey] = value;
return *this;
}
/**
* @brief Set sfConfidentialBalanceSpending (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTHolderKeyUpdateBuilder&
setConfidentialBalanceSpending(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfConfidentialBalanceSpending] = value;
return *this;
}
/**
* @brief Set sfConfidentialBalanceInbox (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTHolderKeyUpdateBuilder&
setConfidentialBalanceInbox(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfConfidentialBalanceInbox] = value;
return *this;
}
/**
* @brief Set sfZKProof (SoeOptional)
* @return Reference to this builder for method chaining.
*/
ConfidentialMPTHolderKeyUpdateBuilder&
setZKProof(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfZKProof] = value;
return *this;
}
/**
* @brief Build and return the ConfidentialMPTHolderKeyUpdate wrapper.
* @param publicKey The public key for signing.
* @param secretKey The secret key for signing.
* @return The constructed transaction wrapper.
*/
ConfidentialMPTHolderKeyUpdate
build(PublicKey const& publicKey, SecretKey const& secretKey)
{
sign(publicKey, secretKey);
return ConfidentialMPTHolderKeyUpdate{std::make_shared<STTx>(std::move(object_))};
}
};
} // namespace xrpl::transactions
@@ -18,7 +18,7 @@ class TransactionProposalCreateBuilder;
/** /**
* @brief Transaction: TransactionProposalCreate * @brief Transaction: TransactionProposalCreate
* *
* Type: ttTRANSACTION_PROPOSAL_CREATE (93) * Type: ttTRANSACTION_PROPOSAL_CREATE (95)
* Delegable: Delegation::NotDelegable * Delegable: Delegation::NotDelegable
* Amendment: featureCosign * Amendment: featureCosign
* Privileges: Privilege::NoPriv * Privileges: Privilege::NoPriv
@@ -27,6 +27,18 @@
namespace xrpl { namespace xrpl {
/**
* Returns true if sleAcct is an AccountRoot carrying a pseudo-account field.
*
* isPseudoAccount() takes a view-bound AccountRootEntry, which is not
* available while visiting entries, so invariant checks use this instead.
*
* @param sleAcct the SLE to check.
* @return true if sleAcct is an AccountRoot carrying a pseudo-account field.
*/
[[nodiscard]] bool
isPseudoAccountSle(SLE::ConstRef sleAcct);
#if GENERATING_DOCS #if GENERATING_DOCS
/** /**
* @brief Prototype for invariant check implementations. * @brief Prototype for invariant check implementations.
+8 -2
View File
@@ -46,6 +46,9 @@ class ValidMPTIssuance
* MPTokens and RippleStates deleted during apply. finalize() checks each * MPTokens and RippleStates deleted during apply. finalize() checks each
* holder's AccountRoot to detect vault pseudo-account holdings deleted * holder's AccountRoot to detect vault pseudo-account holdings deleted
* outside VaultDelete. All these checks are gated on fixCleanup3_2_0. * outside VaultDelete. All these checks are gated on fixCleanup3_2_0.
*
* Under fixCleanup3_5_0, finalize() also rejects any MPToken erased with
* a non-zero sfMPTAmount.
*/ */
std::vector<std::shared_ptr<SLE const>> deletedHoldings_; std::vector<std::shared_ptr<SLE const>> deletedHoldings_;
@@ -138,8 +141,9 @@ public:
* - Convert/ConvertBack symmetry: * - Convert/ConvertBack symmetry:
* Regular MPToken balance change (±X) == COA (Confidential Outstanding Amount) change (∓X) * Regular MPToken balance change (±X) == COA (Confidential Outstanding Amount) change (∓X)
* - Cannot delete MPToken with non-zero confidential state: * - Cannot delete MPToken with non-zero confidential state:
* Cannot delete if sfIssuerEncryptedBalance exists * Cannot delete if any of sfConfidentialBalanceSpending, sfConfidentialBalanceInbox,
* Cannot delete if sfConfidentialBalanceInbox and sfConfidentialBalanceSpending exist * sfIssuerEncryptedBalance or sfAuditorEncryptedBalance is present, and the issuance's
* sfConfidentialOutstandingAmount is non-zero. Mirrors MPTokenAuthorize::preclaim.
* - Privacy flag consistency: * - Privacy flag consistency:
* MPToken confidential balance fields can only be created or changed if * MPToken confidential balance fields can only be created or changed if
* lsfMPTCanHoldConfidentialBalance is set on the issuance. * lsfMPTCanHoldConfidentialBalance is set on the issuance.
@@ -162,6 +166,8 @@ class ValidConfidentialMPToken
std::int64_t outstandingDelta = 0; std::int64_t outstandingDelta = 0;
SLE::const_pointer issuance; SLE::const_pointer issuance;
bool deletedWithEncrypted = false; bool deletedWithEncrypted = false;
// True when an erased MPToken had a non-zero pre-tx public balance.
bool deletedWithBalanceBefore = false;
bool badConsistency = false; bool badConsistency = false;
bool badCOA = false; bool badCOA = false;
bool changesConfidentialFields = false; bool changesConfidentialFields = false;
+3 -2
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -25,7 +26,7 @@ checkFreeze(
XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts"); XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts");
// check freeze // check freeze
if (auto sle = view.read(keylet::account(dst))) if (auto sle = AccountRootEntryR(dst, view))
{ {
if (sle->isFlag(lsfGlobalFreeze)) if (sle->isFlag(lsfGlobalFreeze))
{ {
@@ -49,7 +50,7 @@ checkFreeze(
if (view.rules().enabled(fixFrozenLPTokenTransfer)) if (view.rules().enabled(fixFrozenLPTokenTransfer))
{ {
if (auto const sleDst = view.read(keylet::account(dst)); if (auto const sleDst = AccountRootEntryR(dst, view);
sleDst && sleDst->isFieldPresent(sfAMMID)) sleDst && sleDst->isFieldPresent(sfAMMID))
{ {
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID])); auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
@@ -2,6 +2,7 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
@@ -12,6 +13,7 @@
#include <xrpl/tx/Transactor.h> #include <xrpl/tx/Transactor.h>
#include <cstdint> #include <cstdint>
#include <optional>
namespace xrpl { namespace xrpl {
@@ -56,8 +58,8 @@ private:
Keylet const& sponsorshipKeylet, Keylet const& sponsorshipKeylet,
AccountID const& sponsorID, AccountID const& sponsorID,
AccountID const& sponseeID, AccountID const& sponseeID,
SLE::Ref sponsorAccSle, AccountRootEntryW& sponsorAccSle,
SLE::Ref reserveSponsorAccSle); std::optional<AccountRootEntryW>& reserveSponsorAccSle);
}; };
} // namespace xrpl } // namespace xrpl
@@ -0,0 +1,84 @@
#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/Transactor.h>
#include <cstdint>
namespace xrpl {
/**
* @brief Allows a confidential MPT holder to rotate or recover their ElGamal
* encryption key, or to cancel a pending recovery.
*
* Submitted by the holder. Exactly one of three modes must be selected via
* the transaction flags:
*
* - Rotation (tfHolderKeyRotation): the holder still has their current
* ElGamal private key. They provide a new public key along with their
* current spending/inbox balances re-encrypted under that new key. The
* holder's encryption key and confidential balances are updated
* immediately.
*
* - Recovery (tfHolderKeyRecovery): the holder has lost their current
* private key. They provide a new public key but cannot provide
* re-encrypted balances. The new key is recorded as a pending
* sfRecoveryKey; the confidential balances are left untouched. Completing
* the recovery (rewriting the balances) is done separately by the issuer
* via ConfidentialMPTRecoverBalance.
*
* - Cancel (tfCancelRecovery): the holder revokes a pending recovery
* authorization created by a prior Recovery-mode transaction. No key,
* balances, or proof are carried; authorization is via the holder's
* ordinary XRPL signature. sfRecoveryKey is removed and everything else
* is left untouched. Fails if no recovery is pending.
*
* @note Zero-knowledge proof verification for Rotation and Recovery is
* deferred to a follow-up once the mpt-crypto constructions are finalized.
*/
class ConfidentialMPTHolderKeyUpdate : public Transactor
{
public:
static constexpr auto kConsequencesFactory = ConsequencesFactoryType::Normal;
explicit ConfidentialMPTHolderKeyUpdate(ApplyContext& ctx) : Transactor(ctx)
{
}
static bool
checkExtraFeatures(PreflightContext const& ctx);
static std::uint32_t
getFlagsMask(PreflightContext const& ctx);
static NotTEC
preflight(PreflightContext const& ctx);
static XRPAmount
calculateBaseFee(ReadView const& view, STTx const& tx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
void
visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override;
[[nodiscard]] bool
finalizeInvariants(
STTx const& tx,
TER result,
XRPAmount fee,
ReadView const& view,
beast::Journal const& j) override;
};
} // namespace xrpl
+4 -2
View File
@@ -5,6 +5,7 @@
#include <cerrno> #include <cerrno>
#include <cstddef> #include <cstddef>
#include <filesystem> #include <filesystem>
#include <format>
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <ios> #include <ios>
@@ -99,12 +100,13 @@ uniqueRandomPath(
if (ec) if (ec)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(
"Unable to check path '" + candidate.string() + "': " + ec.message()); std::format("Unable to check path '{}': {}", candidate.string(), ec.message()));
} }
if (!exists) if (!exists)
return candidate; return candidate;
} }
Throw<std::runtime_error>("Unable to generate a unique path under '" + base.string() + "'"); Throw<std::runtime_error>(
std::format("Unable to generate a unique path under '{}'", base.string()));
} }
TempDir::TempDir() : path_(uniqueRandomPath(std::filesystem::temp_directory_path())) TempDir::TempDir() : path_(uniqueRandomPath(std::filesystem::temp_directory_path()))
+2 -1
View File
@@ -16,6 +16,7 @@
#include <boost/container/pmr/monotonic_buffer_resource.hpp> #include <boost/container/pmr/monotonic_buffer_resource.hpp>
#include <cstddef> #include <cstddef>
#include <format>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>
@@ -264,7 +265,7 @@ OpenView::rawTxInsert(
auto const result = txs_.emplace( auto const result = txs_.emplace(
std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(txn, metaData)); std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(txn, metaData));
if (!result.second) if (!result.second)
Throw<std::logic_error>("rawTxInsert: duplicate TX id: " + to_string(key)); Throw<std::logic_error>(std::format("rawTxInsert: duplicate TX id: {}", to_string(key)));
} }
} // namespace xrpl } // namespace xrpl
+18 -9
View File
@@ -9,6 +9,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -115,8 +116,8 @@ isVaultPseudoAccountFrozenForIssuance(
view, {issuer, account}, assetOfHolding(issuanceSle, *sleHolding), depth + 1); view, {issuer, account}, assetOfHolding(issuanceSle, *sleHolding), depth + 1);
} }
auto const mptIssuer = view.read(keylet::account(issuer)); auto const mptIssuer = AccountRootEntryR(issuer, view);
if (mptIssuer == nullptr) if (!mptIssuer.exists())
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer"); UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
@@ -189,7 +190,7 @@ canTransferLPToken(
{ {
// Only AMM-issued LPTokens are subject to this check. The LPToken's issuer // Only AMM-issued LPTokens are subject to this check. The LPToken's issuer
// is the AMM account; if it is not an AMM, this is not an LPToken. // is the AMM account; if it is not an AMM, this is not an LPToken.
auto const sleIssuer = view.read(keylet::account(lpTokenIssuer)); auto const sleIssuer = AccountRootEntryR(lpTokenIssuer, view);
if (!sleIssuer || !sleIssuer->isFieldPresent(sfAMMID)) if (!sleIssuer || !sleIssuer->isFieldPresent(sfAMMID))
return tesSUCCESS; return tesSUCCESS;
@@ -466,7 +467,7 @@ canWithdraw(
ReadView const& view, ReadView const& view,
AccountID const& from, AccountID const& from,
AccountID const& to, AccountID const& to,
SLE::ConstRef toSle, AccountRootEntryR const& toSle,
STAmount const& amount, STAmount const& amount,
bool hasDestinationTag, bool hasDestinationTag,
std::optional<std::vector<UInt256>> const& credentialIDs) std::optional<std::vector<UInt256>> const& credentialIDs)
@@ -516,7 +517,7 @@ canWithdraw(
bool hasDestinationTag, bool hasDestinationTag,
std::optional<std::vector<UInt256>> const& credentialIDs) std::optional<std::vector<UInt256>> const& credentialIDs)
{ {
auto const toSle = view.read(keylet::account(to)); auto const toSle = AccountRootEntryR(to, view);
return canWithdraw(view, from, to, toSle, amount, hasDestinationTag, credentialIDs); return canWithdraw(view, from, to, toSle, amount, hasDestinationTag, credentialIDs);
} }
@@ -541,7 +542,7 @@ doWithdraw(
STAmount const& amount, STAmount const& amount,
beast::Journal j) beast::Journal j)
{ {
auto const dstSle = ctx.view.read(keylet::account(dstAcct)); auto dstSle = AccountRootEntryW(dstAcct, ctx.view);
// Create a trust line or MPToken for a self-destination only when there // Create a trust line or MPToken for a self-destination only when there
// is a payout to credit. Post-fixCleanup3_4_0, a zero-value withdraw // is a payout to credit. Post-fixCleanup3_4_0, a zero-value withdraw
@@ -559,7 +560,8 @@ doWithdraw(
} }
else else
{ {
if (auto err = verifyDepositPreauth(ctx.tx, ctx.view, senderAcct, dstAcct, dstSle, j)) if (auto err =
verifyDepositPreauth(ctx.tx, ctx.view, senderAcct, dstAcct, dstSle.rawSle(), j))
return err; return err;
} }
@@ -582,14 +584,21 @@ doWithdraw(
// sponsor against the destination. accountSend can auto-create a holding // sponsor against the destination. accountSend can auto-create a holding
// for dstAcct; keying on the destination ensures a third-party destination's // for dstAcct; keying on the destination ensures a third-party destination's
// holding is never stamped with the tx's reserve sponsor. // holding is never stamped with the tx's reserve sponsor.
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle); auto sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle);
if (!sponsorSle) if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE return sponsorSle.error(); // LCOV_EXCL_LINE
// Move the funds directly from the broker's pseudo-account to the // Move the funds directly from the broker's pseudo-account to the
// dstAcct // dstAcct
auto& sponsor = *sponsorSle;
return accountSend( return accountSend(
ctx.view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes); ctx.view,
sourceAcct,
dstAcct,
amount,
j,
sponsor ? sponsor->mutableRawSle() : SLE::pointer{},
WaiveTransferFee::Yes);
} }
TER TER
@@ -0,0 +1,322 @@
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <limits>
#include <optional>
#include <utility>
namespace xrpl {
template <typename ViewT>
XRPAmount
AccountRootEntry<ViewT>::reserve(Adjustment adj) const
{
auto const& view = this->readView();
auto const j = this->journal();
XRPL_ASSERT(
this->exists() && (*this)->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle");
if (!view.rules().enabled(featureSponsor))
{
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::accountReserve : no account count delta");
return view.fees().accountReserve(
(*this)->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
}
std::uint32_t const currentOwnerCount = ownerCount(adj.ownerCountDelta);
std::uint32_t const currentAccountCount =
detail::accountCountImpl(*this, adj.accountCountDelta, j);
return view.fees().accountReserve(currentOwnerCount, currentAccountCount);
}
template <typename ViewT>
std::uint32_t
AccountRootEntry<ViewT>::ownerCount(std::int32_t ownerCountAdj) const
{
auto const j = this->journal();
XRPL_ASSERT(
this->exists() && (*this)->getType() == ltACCOUNT_ROOT,
"xrpl::ownerCount : sle is account root");
AccountID const id = (*this)->getAccountID(sfAccount);
std::uint32_t const currentOwnerCount = (*this)->at(sfOwnerCount);
std::uint32_t const sponsoredOwnerCount = (*this)->at(sfSponsoredOwnerCount);
std::uint32_t const sponsoringOwnerCount = (*this)->at(sfSponsoringOwnerCount);
XRPL_ASSERT(
currentOwnerCount >= sponsoredOwnerCount,
"xrpl::ownerCount : OwnerCount must be greater than or equal to SponsoredOwnerCount");
std::int64_t deltaCount =
static_cast<std::int64_t>(ownerCountAdj) - sponsoredOwnerCount + sponsoringOwnerCount;
if (deltaCount > std::numeric_limits<std::int32_t>::max())
{
// LCOV_EXCL_START
deltaCount = std::numeric_limits<std::int32_t>::max();
JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, "
<< "adjustment: " << ownerCountAdj
<< ", sponsoredCount: " << sponsoredOwnerCount
<< ", sponsoringOwnerCount: " << sponsoringOwnerCount;
// LCOV_EXCL_STOP
}
else if (deltaCount < std::numeric_limits<std::int32_t>::min())
{
// LCOV_EXCL_START
deltaCount = std::numeric_limits<std::int32_t>::min();
JLOG(j.fatal()) << "Account " << id << " delta count is below min, "
<< "adjustment: " << ownerCountAdj
<< ", sponsoredCount: " << sponsoredOwnerCount
<< ", sponsoringCount: " << sponsoringOwnerCount;
// LCOV_EXCL_STOP
}
return detail::confineOwnerCount(currentOwnerCount, deltaCount);
}
template <typename ViewT>
TER
AccountRootEntry<ViewT>::checkReserve(
ApplyViewContext ctx,
XRPAmount accBalance,
std::optional<AccountRootEntry<ReadView>> const& sponsorSle,
Adjustment adj,
TER insufReserveCode) const
{
// TODO: swap to assert after fixCleanup3_2_0 is retired
if (!this->exists() || (*this)->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
XRPL_ASSERT(
!isTesSuccess(insufReserveCode), "xrpl::checkReserve : insufReserveCode is not tesSUCCESS");
if (ctx.view.rules().enabled(featureSponsor))
{
if (sponsorSle)
{
if ((*sponsorSle)->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sle = ctx.view.read(
keylet::sponsorship(
(*sponsorSle)->getAccountID(sfAccount), (*this)->getAccountID(sfAccount)));
// A reserve-sponsored tx must carry a sponsor signature
// (cosigning path) and/or have a pre-existing sponsorship SLE
// (prefunded path). Absence of both is an internal invariant break.
if (isReserveSponsored(ctx.tx) && !sle && !ctx.tx.isFieldPresent(sfSponsorSignature))
return tecINTERNAL; // LCOV_EXCL_LINE
if (sle)
{
auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount);
if (adj.ownerCountDelta > 0 &&
ownerCountAllowed < static_cast<std::uint32_t>(adj.ownerCountDelta))
return insufReserveCode;
}
auto const sponsorBalance = (*sponsorSle)->getFieldAmount(sfBalance).xrp();
XRPAmount const sponsorReserve = sponsorSle->reserve(adj);
if (sponsorBalance < sponsorReserve)
return insufReserveCode;
}
else
{
XRPAmount const reserve = this->reserve(adj);
if (accBalance < reserve)
return insufReserveCode;
}
}
else
{
XRPL_ASSERT(
!sponsorSle,
"xrpl::checkReserve : featureSponsor disabled and sponsorSle not provided");
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::checkReserve : accountCountDelta is 0");
auto const reserve = ctx.view.fees().accountReserve(
(*this)->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
if (accBalance < reserve)
return insufReserveCode;
}
return tesSUCCESS;
}
template <typename ViewT>
TER
AccountRootEntry<ViewT>::checkReserve(ApplyViewContext ctx, XRPAmount accBalance, Adjustment adj)
const
{
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, *this);
if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE
std::optional<AccountRootEntry<ReadView>> sponsorSle;
if (*sponsorExp)
sponsorSle.emplace(**sponsorExp);
return checkReserve(ctx, accBalance, sponsorSle, adj);
}
template <typename ViewT>
void
AccountRootEntry<ViewT>::adjustOwnerCountSigned(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::int32_t adjustment)
requires Base::kIsWritable
{
auto& view = this->applyView();
auto const j = this->journal();
if (view.rules().enabled(featureSponsor))
{
XRPL_ASSERT(this->exists(), "xrpl::adjustOwnerCountSigned : valid account sle");
if (!this->exists())
return; // LCOV_EXCL_LINE
auto const accountID = (*this)->getAccountID(sfAccount);
bool const validType = (*this)->getType() == ltACCOUNT_ROOT;
XRPL_ASSERT(validType, "xrpl::adjustOwnerCountSigned : valid account sle type");
if (!validType)
return; // LCOV_EXCL_LINE
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCountSigned : nonzero adjustment input");
OwnerCounts const currentOwnerCount(this->rawSle());
OwnerCounts totalOwnerCount(currentOwnerCount);
if (sponsorSle)
{
bool const validSponsorType = (*sponsorSle)->getType() == ltACCOUNT_ROOT;
XRPL_ASSERT(validSponsorType, "xrpl::adjustOwnerCountSigned : valid sponsor sle type");
if (!validSponsorType)
return; // LCOV_EXCL_LINE
auto const sponsorID = (*sponsorSle)->getAccountID(sfAccount);
totalOwnerCount.sponsored = detail::adjustOwnerCountImpl(
view, this->mutableRawSle(), sfSponsoredOwnerCount, accountID, adjustment, j);
{
OwnerCounts const sponsorCurrent(sponsorSle->rawSle());
OwnerCounts sponsorAdjustment(sponsorCurrent);
sponsorAdjustment.sponsoring = detail::adjustOwnerCountImpl(
view,
sponsorSle->mutableRawSle(),
sfSponsoringOwnerCount,
sponsorID,
adjustment,
j);
view.adjustOwnerCountHook(sponsorID, sponsorCurrent, sponsorAdjustment);
}
auto sponsorshipSle = view.peek(keylet::sponsorship(sponsorID, accountID));
if (sponsorshipSle && adjustment > 0)
{
// 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 requires reserve (like other sf...OwnerCounts do).
detail::adjustOwnerCountImpl(
view, sponsorshipSle, sfRemainingOwnerCount, sponsorID, -adjustment, j);
}
}
totalOwnerCount.owner = detail::adjustOwnerCountImpl(
view, this->mutableRawSle(), sfOwnerCount, accountID, adjustment, j);
view.adjustOwnerCountHook(accountID, currentOwnerCount, totalOwnerCount);
}
else
{
XRPL_ASSERT(this->exists(), "xrpl::adjustOwnerCountSigned : valid account sle");
if (!this->exists())
return;
// the remaining are only asserts to preserve existing behavior
XRPL_ASSERT(!sponsorSle, "xrpl::adjustOwnerCountSigned : sponsor not enabled");
XRPL_ASSERT(
(*this)->getType() == ltACCOUNT_ROOT,
"xrpl::adjustOwnerCountSigned : valid account sle type");
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input");
std::uint32_t const current{(*this)->getFieldU32(sfOwnerCount)};
AccountID const id = (**this)[sfAccount];
std::uint32_t const adjusted = detail::confineOwnerCount(current, adjustment, id, j);
OwnerCounts const currentOwnerCount(this->rawSle());
OwnerCounts finalOwnerCount(currentOwnerCount);
finalOwnerCount.owner = adjusted;
view.adjustOwnerCountHook(id, currentOwnerCount, finalOwnerCount);
(*this)->at(sfOwnerCount) = adjusted;
this->update();
}
}
template <typename ViewT>
void
AccountRootEntry<ViewT>::increaseOwnerCount(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::uint32_t count)
requires Base::kIsWritable
{
XRPL_ASSERT(
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
"xrpl::increaseOwnerCount : count in signed delta range");
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
return; // LCOV_EXCL_LINE
adjustOwnerCountSigned(sponsorSle, static_cast<std::int32_t>(count));
}
template <typename ViewT>
void
AccountRootEntry<ViewT>::increaseOwnerCount(ApplyViewContext ctx, std::uint32_t count)
requires Base::kIsWritable
{
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, *this);
// The sponsor's existence is validated by checkReserve/checkSponsor before
// any owner-count mutation, so loading it here cannot fail.
XRPL_ASSERT(
sponsorExp.has_value(), "xrpl::increaseOwnerCount : sponsor validated before mutation");
std::optional<AccountRootEntry<ApplyView>> sponsorSle;
if (sponsorExp && *sponsorExp)
sponsorSle.emplace(std::move(**sponsorExp));
increaseOwnerCount(sponsorSle, count);
}
template <typename ViewT>
void
AccountRootEntry<ViewT>::decreaseOwnerCount(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::uint32_t count)
requires Base::kIsWritable
{
XRPL_ASSERT(
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
"xrpl::decreaseOwnerCount : count in signed delta range");
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
return; // LCOV_EXCL_LINE
adjustOwnerCountSigned(sponsorSle, -static_cast<std::int32_t>(count));
}
template class AccountRootEntry<ReadView>;
template class AccountRootEntry<ApplyView>;
} // namespace xrpl
+4 -3
View File
@@ -11,6 +11,7 @@
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -641,7 +642,7 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
[&](Issue const& issue) { [&](Issue const& issue) {
if (isXRP(issue)) if (isXRP(issue))
{ {
if (auto const sle = view.read(keylet::account(ammAccountID))) if (auto const sle = AccountRootEntryR(ammAccountID, view))
return (*sle)[sfBalance]; return (*sle)[sfBalance];
} }
else if ( else if (
@@ -757,7 +758,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
} }
auto const ammAccountID = (*ammSle)[sfAccount]; auto const ammAccountID = (*ammSle)[sfAccount];
auto sleAMMRoot = sb.peek(keylet::account(ammAccountID)); auto sleAMMRoot = AccountRootEntryW(ammAccountID, sb);
if (!sleAMMRoot) if (!sleAMMRoot)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
@@ -795,7 +796,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
} }
sb.erase(ammSle); sb.erase(ammSle);
sb.erase(sleAMMRoot); sleAMMRoot.erase();
return tesSUCCESS; return tesSUCCESS;
} }
+22 -300
View File
@@ -9,6 +9,7 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OwnerCounts.h> #include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -17,7 +18,6 @@
#include <xrpl/protocol/Rate.h> #include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h> #include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h> #include <xrpl/protocol/XRPAmount.h>
#include <xrpl/protocol/digest.h> #include <xrpl/protocol/digest.h>
@@ -38,25 +38,19 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer)
{ {
if (isXRP(issuer)) if (isXRP(issuer))
return false; return false;
if (auto const sle = view.read(keylet::account(issuer))) if (auto const sle = AccountRootEntryR(issuer, view))
return sle->isFlag(lsfGlobalFreeze); return sle->isFlag(lsfGlobalFreeze);
return false; return false;
} }
namespace { namespace detail {
// An owner count cannot be negative. If adjustment would cause a negative
// owner count, clamp the owner count at 0. Similarly for overflow. This
// adjustment allows the ownerCount to be adjusted up or down in multiple steps.
// If id != std::nullopt, then do error reporting.
//
// Returns adjusted owner count.
std::uint32_t std::uint32_t
confineOwnerCount( confineOwnerCount(
std::uint32_t currentOwnerCount, std::uint32_t currentOwnerCount,
std::int32_t ownerCountAdj, std::int32_t ownerCountAdj,
std::optional<AccountID> const& id = std::nullopt, std::optional<AccountID> const& id,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) beast::Journal j)
{ {
std::uint32_t totalOwnerCount{currentOwnerCount + ownerCountAdj}; std::uint32_t totalOwnerCount{currentOwnerCount + ownerCountAdj};
if (ownerCountAdj > 0) if (ownerCountAdj > 0)
@@ -91,10 +85,8 @@ confineOwnerCount(
return totalOwnerCount; return totalOwnerCount;
} }
// Returns the number of account reserves funded by this account: 1 for itself (0 if sponsored by
// another account) plus the count of accounts it sponsors.
std::uint32_t std::uint32_t
accountCountImpl(SLE::ConstRef sle, std::int32_t accountCountAdj, beast::Journal j) accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, beast::Journal j)
{ {
bool const isSponsored = sle->isFieldPresent(sfSponsor); bool const isSponsored = sle->isFieldPresent(sfSponsor);
std::int64_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount); std::int64_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount);
@@ -131,151 +123,25 @@ adjustOwnerCountImpl(
{ {
std::uint32_t const currentOwnerCount = sle->at(sfield); std::uint32_t const currentOwnerCount = sle->at(sfield);
std::uint32_t const totalOwnerCount = std::uint32_t const totalOwnerCount =
confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j); detail::confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j);
sle->at(sfield) = totalOwnerCount; sle->at(sfield) = totalOwnerCount;
view.update(sle); view.update(sle);
return totalOwnerCount; return totalOwnerCount;
} }
void } // namespace detail
adjustOwnerCountSigned(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::int32_t adjustment,
beast::Journal j)
{
if (view.rules().enabled(featureSponsor))
{
XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCountSigned : valid account sle");
if (!accountSle)
return; // LCOV_EXCL_LINE
auto const accountID = accountSle->getAccountID(sfAccount);
bool const validType = accountSle->getType() == ltACCOUNT_ROOT;
XRPL_ASSERT(validType, "xrpl::adjustOwnerCountSigned : valid account sle type");
if (!validType)
return; // LCOV_EXCL_LINE
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCountSigned : nonzero adjustment input");
OwnerCounts const currentOwnerCount(accountSle);
OwnerCounts totalOwnerCount(currentOwnerCount);
if (sponsorSle)
{
bool const validSponsorType = sponsorSle->getType() == ltACCOUNT_ROOT;
XRPL_ASSERT(validSponsorType, "xrpl::adjustOwnerCountSigned : valid sponsor sle type");
if (!validSponsorType)
return; // LCOV_EXCL_LINE
auto const sponsorID = sponsorSle->getAccountID(sfAccount);
totalOwnerCount.sponsored = adjustOwnerCountImpl(
view, accountSle, sfSponsoredOwnerCount, accountID, adjustment, j);
{
OwnerCounts const sponsorCurrent(sponsorSle);
OwnerCounts sponsorAdjustment(sponsorCurrent);
sponsorAdjustment.sponsoring = adjustOwnerCountImpl(
view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j);
view.adjustOwnerCountHook(sponsorID, sponsorCurrent, sponsorAdjustment);
}
auto sponsorshipSle = view.peek(keylet::sponsorship(sponsorID, accountID));
if (sponsorshipSle && adjustment > 0)
{
// 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 requires reserve (like other sf...OwnerCounts do).
adjustOwnerCountImpl(
view, sponsorshipSle, sfRemainingOwnerCount, sponsorID, -adjustment, j);
}
}
totalOwnerCount.owner =
adjustOwnerCountImpl(view, accountSle, sfOwnerCount, accountID, adjustment, j);
view.adjustOwnerCountHook(accountID, currentOwnerCount, totalOwnerCount);
}
else
{
XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCountSigned : valid account sle");
if (!accountSle)
return;
// the remaining are only asserts to preserve existing behavior
XRPL_ASSERT(sponsorSle == nullptr, "xrpl::adjustOwnerCountSigned : sponsor not enabled");
XRPL_ASSERT(
accountSle->getType() == ltACCOUNT_ROOT,
"xrpl::adjustOwnerCountSigned : valid account sle type");
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input");
std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)};
AccountID const id = (*accountSle)[sfAccount];
std::uint32_t const adjusted = confineOwnerCount(current, adjustment, id, j);
OwnerCounts const currentOwnerCount(accountSle);
OwnerCounts finalOwnerCount(currentOwnerCount);
finalOwnerCount.owner = adjusted;
view.adjustOwnerCountHook(id, currentOwnerCount, finalOwnerCount);
accountSle->at(sfOwnerCount) = adjusted;
view.update(accountSle);
}
}
} // namespace
std::uint32_t
ownerCount(SLE::ConstRef sle, beast::Journal j, std::int32_t ownerCountAdj)
{
XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::ownerCount : sle is account root");
AccountID const id = sle->getAccountID(sfAccount);
std::uint32_t const currentOwnerCount = sle->at(sfOwnerCount);
std::uint32_t const sponsoredOwnerCount = sle->at(sfSponsoredOwnerCount);
std::uint32_t const sponsoringOwnerCount = sle->at(sfSponsoringOwnerCount);
XRPL_ASSERT(
currentOwnerCount >= sponsoredOwnerCount,
"xrpl::ownerCount : OwnerCount must be greater than or equal to SponsoredOwnerCount");
std::int64_t deltaCount =
static_cast<std::int64_t>(ownerCountAdj) - sponsoredOwnerCount + sponsoringOwnerCount;
if (deltaCount > std::numeric_limits<std::int32_t>::max())
{
// LCOV_EXCL_START
deltaCount = std::numeric_limits<std::int32_t>::max();
JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, "
<< "adjustment: " << ownerCountAdj
<< ", sponsoredCount: " << sponsoredOwnerCount
<< ", sponsoringOwnerCount: " << sponsoringOwnerCount;
// LCOV_EXCL_STOP
}
else if (deltaCount < std::numeric_limits<std::int32_t>::min())
{
// LCOV_EXCL_START
deltaCount = std::numeric_limits<std::int32_t>::min();
JLOG(j.fatal()) << "Account " << id << " delta count is below min, "
<< "adjustment: " << ownerCountAdj
<< ", sponsoredCount: " << sponsoredOwnerCount
<< ", sponsoringCount: " << sponsoringOwnerCount;
// LCOV_EXCL_STOP
}
return confineOwnerCount(currentOwnerCount, deltaCount);
}
XRPAmount XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j) xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
{ {
auto const sle = view.read(keylet::account(id)); auto const sle = AccountRootEntryR(id, view);
if (sle == nullptr) if (!sle.exists())
return beast::kZero; return beast::kZero;
// Return balance minus reserve // Return balance minus reserve
std::uint32_t const currentOwnerCount = std::uint32_t const currentOwnerCount = detail::confineOwnerCount(
confineOwnerCount(view.ownerCountHook(id, OwnerCounts(sle)).count(), ownerCountAdj); view.ownerCountHook(id, OwnerCounts(sle.rawSle())).count(), ownerCountAdj);
std::uint32_t const currentAccountCount = accountCountImpl(sle, 0, j); std::uint32_t const currentAccountCount = detail::accountCountImpl(sle, 0, j);
// Pseudo-accounts have no reserve requirement // Pseudo-accounts have no reserve requirement
auto const reserve = isPseudoAccount(sle) auto const reserve = isPseudoAccount(sle)
@@ -300,7 +166,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
Rate Rate
transferRate(ReadView const& view, AccountID const& issuer) transferRate(ReadView const& view, AccountID const& issuer)
{ {
auto const sle = view.read(keylet::account(issuer)); auto const sle = AccountRootEntryR(issuer, view);
if (sle && sle->isFieldPresent(sfTransferRate)) if (sle && sle->isFieldPresent(sfTransferRate))
return Rate{sle->getFieldU32(sfTransferRate)}; return Rate{sle->getFieldU32(sfTransferRate)};
@@ -308,57 +174,10 @@ transferRate(ReadView const& view, AccountID const& issuer)
return kParityRate; return kParityRate;
} }
void
increaseOwnerCount(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::uint32_t count,
beast::Journal j)
{
XRPL_ASSERT(
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
"xrpl::increaseOwnerCount : count in signed delta range");
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
return; // LCOV_EXCL_LINE
adjustOwnerCountSigned(view, accountSle, sponsorSle, static_cast<std::int32_t>(count), j);
}
void
increaseOwnerCount(ApplyViewContext ctx, SLE::Ref accountSle, std::uint32_t count, beast::Journal j)
{
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, accountSle);
// The sponsor's existence is validated by checkReserve/checkSponsor before
// any owner-count mutation, so loading it here cannot fail.
XRPL_ASSERT(
sponsorExp.has_value(), "xrpl::increaseOwnerCount : sponsor validated before mutation");
increaseOwnerCount(ctx.view, accountSle, sponsorExp ? *sponsorExp : SLE::pointer(), count, j);
}
void
decreaseOwnerCount(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::uint32_t count,
beast::Journal j)
{
XRPL_ASSERT(
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
"xrpl::decreaseOwnerCount : count in signed delta range");
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
return; // LCOV_EXCL_LINE
adjustOwnerCountSigned(view, accountSle, sponsorSle, -static_cast<std::int32_t>(count), j);
}
void void
decreaseOwnerCountForObject( decreaseOwnerCountForObject(
ApplyView& view, ApplyView& view,
SLE::Ref accountSle, AccountRootEntryW& accountSle,
SLE::Ref objectSle, SLE::Ref objectSle,
std::uint32_t count, std::uint32_t count,
beast::Journal j) beast::Journal j)
@@ -372,8 +191,8 @@ decreaseOwnerCountForObject(
if (!validObjectType) if (!validObjectType)
return; // LCOV_EXCL_LINE return; // LCOV_EXCL_LINE
SLE::Ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle); auto sponsorSle = getLedgerEntryReserveSponsor(view, objectSle);
decreaseOwnerCount(view, accountSle, sponsorSle, count, j); accountSle.decreaseOwnerCount(sponsorSle, count);
} }
void void
@@ -393,107 +212,10 @@ adjustLoanBrokerOwnerCount(
if (delta == 0) if (delta == 0)
return; // LCOV_EXCL_LINE return; // LCOV_EXCL_LINE
adjustOwnerCountImpl( detail::adjustOwnerCountImpl(
view, brokerSle, sfOwnerCount, brokerSle->getAccountID(sfAccount), delta, j); view, brokerSle, sfOwnerCount, brokerSle->getAccountID(sfAccount), delta, j);
} }
XRPAmount
accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjustment adj)
{
XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle");
if (!view.rules().enabled(featureSponsor))
{
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::accountReserve : no account count delta");
return view.fees().accountReserve(sle->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
}
std::uint32_t const currentOwnerCount = ownerCount(sle, j, adj.ownerCountDelta);
std::uint32_t const currentAccountCount = accountCountImpl(sle, adj.accountCountDelta, j);
return view.fees().accountReserve(currentOwnerCount, currentAccountCount);
}
TER
checkReserve(
ApplyViewContext ctx,
SLE::ConstRef accSle,
XRPAmount accBalance,
SLE::ConstRef sponsorSle,
Adjustment adj,
beast::Journal j,
TER insufReserveCode)
{
// TODO: swap to assert after fixCleanup3_2_0 is retired
if (!accSle || accSle->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
XRPL_ASSERT(
!isTesSuccess(insufReserveCode), "xrpl::checkReserve : insufReserveCode is not tesSUCCESS");
if (ctx.view.rules().enabled(featureSponsor))
{
if (sponsorSle)
{
if (sponsorSle->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sle = ctx.view.read(
keylet::sponsorship(
sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount)));
// A reserve-sponsored tx must carry a sponsor signature
// (cosigning path) and/or have a pre-existing sponsorship SLE
// (prefunded path). Absence of both is an internal invariant break.
if (isReserveSponsored(ctx.tx) && !sle && !ctx.tx.isFieldPresent(sfSponsorSignature))
return tecINTERNAL; // LCOV_EXCL_LINE
if (sle)
{
auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount);
if (adj.ownerCountDelta > 0 &&
ownerCountAllowed < static_cast<std::uint32_t>(adj.ownerCountDelta))
return insufReserveCode;
}
auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance).xrp();
XRPAmount const sponsorReserve = accountReserve(ctx.view, sponsorSle, j, adj);
if (sponsorBalance < sponsorReserve)
return insufReserveCode;
}
else
{
XRPAmount const reserve = accountReserve(ctx.view, accSle, j, adj);
if (accBalance < reserve)
return insufReserveCode;
}
}
else
{
XRPL_ASSERT(
!sponsorSle,
"xrpl::checkReserve : featureSponsor disabled and sponsorSle not provided");
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::checkReserve : accountCountDelta is 0");
auto const reserve = ctx.view.fees().accountReserve(
accSle->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
if (accBalance < reserve)
return insufReserveCode;
}
return tesSUCCESS;
}
TER
checkReserve(
ApplyViewContext ctx,
SLE::ConstRef accSle,
XRPAmount accBalance,
Adjustment adj,
beast::Journal j)
{
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, accSle);
if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE
return checkReserve(ctx, accSle, accBalance, *sponsorExp, adj, j);
}
// ---------------------------------------------------- // ----------------------------------------------------
AccountID AccountID
@@ -507,7 +229,7 @@ pseudoAccountAddress(ReadView const& view, UInt256 const& pseudoOwnerKey)
auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey); auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey);
rsh(hash.data(), hash.size()); rsh(hash.data(), hash.size());
AccountID const ret = AccountID::fromRaw(static_cast<RipeshaHasher::result_type>(rsh)); AccountID const ret = AccountID::fromRaw(static_cast<RipeshaHasher::result_type>(rsh));
if (!view.read(keylet::account(ret))) if (!AccountRootEntryR(ret, view).exists())
return ret; return ret;
} }
return beast::kZero; return beast::kZero;
@@ -546,7 +268,7 @@ getPseudoAccountFields()
} }
[[nodiscard]] bool [[nodiscard]] bool
isPseudoAccount(SLE::const_pointer sleAcct) isPseudoAccount(AccountRootEntryR const& sleAcct)
{ {
// Intentionally use defensive coding here because it's cheap and makes the // Intentionally use defensive coding here because it's cheap and makes the
// semantics of true return value clean. // semantics of true return value clean.
@@ -599,9 +321,9 @@ createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const
} }
[[nodiscard]] TER [[nodiscard]] TER
checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag) checkDestinationAndTag(AccountRootEntryR const& toSle, bool hasDestinationTag)
{ {
if (toSle == nullptr) if (!toSle.exists())
return tecNO_DST; return tecNO_DST;
// The tag is basically account-specific information we don't // The tag is basically account-specific information we don't
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -81,7 +82,7 @@ deleteSLE(ApplyView& view, SLE::Ref sleCredential, beast::Journal j)
auto delSLE = [&view, &sleCredential, j]( auto delSLE = [&view, &sleCredential, j](
AccountID const& account, SField const& node, bool isOwner) -> TER { AccountID const& account, SField const& node, bool isOwner) -> TER {
auto const sleAccount = view.peek(keylet::account(account)); auto sleAccount = AccountRootEntryW(account, view);
if (!sleAccount) if (!sleAccount)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
+15 -11
View File
@@ -7,6 +7,7 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -206,7 +207,7 @@ authorizeMPToken(
std::uint32_t flags, std::uint32_t flags,
std::optional<AccountID> holderID) std::optional<AccountID> holderID)
{ {
auto const sleAcct = ctx.view.peek(keylet::account(account)); auto sleAcct = AccountRootEntryW(account, ctx.view, journal);
if (!sleAcct) if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -242,10 +243,10 @@ authorizeMPToken(
// - create the MPToken object for the holder // - create the MPToken object for the holder
// A reserve sponsor only covers tx.Account's own objects. // A reserve sponsor only covers tx.Account's own objects.
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct); auto sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct);
if (!sponsorExp) if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE return sponsorExp.error(); // LCOV_EXCL_LINE
auto const sponsorSle = *sponsorExp; auto& sponsorSle = *sponsorExp;
// The reserve that is required to create the MPToken. Note // The reserve that is required to create the MPToken. Note
// that although the reserve increases with every item // that although the reserve increases with every item
@@ -255,10 +256,10 @@ authorizeMPToken(
// The "free-tier" shortcut (ownerCount < 2) does not apply once a sponsor is on // 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 // the tx — the sponsor must always cover the reserve (via balance or prefunded
// budget), so this check always runs for sponsored transactions. // budget), so this check always runs for sponsored transactions.
if (sponsorSle || ownerCount(sleAcct, journal) >= 2) if (sponsorSle || sleAcct.ownerCount() >= 2)
{ {
if (auto const ret = checkReserve( if (auto const ret =
ctx, sleAcct, priorBalance, sponsorSle, {.ownerCountDelta = 1}, journal); sleAcct.checkReserve(ctx, priorBalance, sponsorSle, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
} }
@@ -285,8 +286,8 @@ authorizeMPToken(
ctx.view.insert(mptoken); ctx.view.insert(mptoken);
// Update owner count. // Update owner count.
increaseOwnerCount(ctx.view, sleAcct, sponsorSle, 1, journal); sleAcct.increaseOwnerCount(sponsorSle, 1);
addSponsorToLedgerEntry(mptoken, sponsorSle); addSponsorToLedgerEntry(mptoken, sponsorSle ? sponsorSle->rawSle() : SLE::const_pointer{});
return tesSUCCESS; return tesSUCCESS;
} }
@@ -415,7 +416,7 @@ requireAuth(
} }
// requireAuth is recursive if the issuer is a vault pseudo-account // requireAuth is recursive if the issuer is a vault pseudo-account
auto const sleIssuer = view.read(keylet::account(mptIssuer)); auto const sleIssuer = AccountRootEntryR(mptIssuer, view);
if (!sleIssuer) if (!sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -1020,13 +1021,16 @@ checkCreateMPT(
{ {
return err; return err;
} }
auto const sleAcct = view.peek(keylet::account(holder)); auto sleAcct = AccountRootEntryW(holder, view, j);
if (!sleAcct) if (!sleAcct)
{ {
return tecINTERNAL; return tecINTERNAL;
} }
increaseOwnerCount(view, sleAcct, sponsorSle, 1, j); std::optional<AccountRootEntryW> sponsorAccSle;
if (sponsorSle)
sponsorAccSle.emplace(sponsorSle->getAccountID(sfAccount), view);
sleAcct.increaseOwnerCount(sponsorAccSle, 1);
} }
return tesSUCCESS; return tesSUCCESS;
} }
+18 -15
View File
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -33,6 +34,7 @@
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <format>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
@@ -394,9 +396,11 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
if (!page2) if (!page2)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"page " + to_string(page1->key()) + " has a broken " + field.getName() + "page {} has a broken {} field pointing to {}",
" field pointing to " + to_string(*id)); to_string(page1->key()),
field.getName(),
to_string(*id)));
} }
} }
@@ -597,7 +601,7 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
else else
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(
"Offer " + to_string(offerIndexes[i]) + " cannot be deleted!"); std::format("Offer {} cannot be deleted!", to_string(offerIndexes[i])));
} }
} }
@@ -734,9 +738,9 @@ repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner)
if (!newPrev) if (!newPrev)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"NFTokenPage directory for " + to_string(owner) + "NFTokenPage directory for {} cannot be repaired. Unexpected link problem.",
" cannot be repaired. Unexpected link problem."); to_string(owner)));
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
newPrev->at(sfNextPageMin) = nextPage->key(); newPrev->at(sfNextPageMin) = nextPage->key();
@@ -852,7 +856,7 @@ tokenOfferCreatePreclaim(
if (nftIssuer != acctID && ((nftFlags & nft::kFlagTransferable) == 0)) if (nftIssuer != acctID && ((nftFlags & nft::kFlagTransferable) == 0))
{ {
auto const root = view.read(keylet::account(nftIssuer)); auto const root = AccountRootEntryR(nftIssuer, view);
XRPL_ASSERT(root, "xrpl::nft::tokenOfferCreatePreclaim : non-null account"); XRPL_ASSERT(root, "xrpl::nft::tokenOfferCreatePreclaim : non-null account");
if (auto minter = (*root)[~sfNFTokenMinter]; minter != acctID) if (auto minter = (*root)[~sfNFTokenMinter]; minter != acctID)
@@ -883,7 +887,7 @@ tokenOfferCreatePreclaim(
{ {
// If a destination is specified, the destination must already be in // If a destination is specified, the destination must already be in
// the ledger. // the ledger.
auto const sleDst = view.read(keylet::account(*dest)); auto const sleDst = AccountRootEntryR(*dest, view);
if (!sleDst) if (!sleDst)
return tecNO_DST; return tecNO_DST;
@@ -895,7 +899,7 @@ tokenOfferCreatePreclaim(
if (owner) if (owner)
{ {
auto const sleOwner = view.read(keylet::account(*owner)); auto const sleOwner = AccountRootEntryR(*owner, view);
// defensively check // defensively check
// it should not be possible to specify owner that doesn't exist // it should not be possible to specify owner that doesn't exist
@@ -934,9 +938,8 @@ tokenOfferCreateApply(
beast::Journal j, beast::Journal j,
std::uint32_t txFlags) std::uint32_t txFlags)
{ {
Keylet const acctKeylet = keylet::account(acctID); if (auto const acct = AccountRootEntryW(acctID, view, j);
if (auto const acct = view.read(acctKeylet); priorBalance < acct.reserve({.ownerCountDelta = 1}))
priorBalance < accountReserve(view, acct, j, {.ownerCountDelta = 1}))
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
auto const offerID = keylet::nftokenOffer(acctID, seqProxy); auto const offerID = keylet::nftokenOffer(acctID, seqProxy);
@@ -1005,7 +1008,7 @@ checkTrustlineAuthorized(
if (view.rules().enabled(fixEnforceNFTokenTrustlineV2)) if (view.rules().enabled(fixEnforceNFTokenTrustlineV2))
{ {
auto const issuerAccount = view.read(keylet::account(issue.account)); auto const issuerAccount = AccountRootEntryR(issue.account, view);
if (!issuerAccount) if (!issuerAccount)
{ {
JLOG(j.debug()) << "xrpl::nft::checkTrustlineAuthorized: can't " JLOG(j.debug()) << "xrpl::nft::checkTrustlineAuthorized: can't "
@@ -1057,7 +1060,7 @@ checkTrustlineDeepFrozen(
if (view.rules().enabled(featureDeepFreeze)) if (view.rules().enabled(featureDeepFreeze))
{ {
auto const issuerAccount = view.read(keylet::account(issue.account)); auto const issuerAccount = AccountRootEntryR(issue.account, view);
if (!issuerAccount) if (!issuerAccount)
{ {
JLOG(j.debug()) << "xrpl::nft::checkTrustlineDeepFrozen: can't " JLOG(j.debug()) << "xrpl::nft::checkTrustlineDeepFrozen: can't "
@@ -6,6 +6,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -51,7 +52,7 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
} }
// Transfer amount back to owner, decrement owner count // Transfer amount back to owner, decrement owner count
auto const sle = view.peek(keylet::account(src)); auto sle = AccountRootEntryW(src, view);
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -59,7 +60,7 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
(*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount"); (*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount");
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
decreaseOwnerCountForObject(view, sle, slep, 1, j); decreaseOwnerCountForObject(view, sle, slep, 1, j);
view.update(sle); sle.update();
// Remove PayChan from ledger // Remove PayChan from ledger
view.erase(slep); view.erase(slep);
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -133,14 +134,14 @@ isFrozen(
{ {
if (isXRP(currency)) if (isXRP(currency))
return false; return false;
auto sle = view.read(keylet::account(issuer)); auto const sleIssuer = AccountRootEntryR(issuer, view);
if (sle && sle->isFlag(lsfGlobalFreeze)) if (sleIssuer && sleIssuer->isFlag(lsfGlobalFreeze))
return true; return true;
if (issuer != account) if (issuer != account)
{ {
// Check if the issuer froze the line // Check if the issuer froze the line
sle = view.read(keylet::trustLine(account, issuer, currency)); auto const sleLine = view.read(keylet::trustLine(account, issuer, currency));
if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) if (sleLine && sleLine->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
return true; return true;
} }
return false; return false;
@@ -238,7 +239,7 @@ trustCreate(
XRPL_ASSERT( XRPL_ASSERT(
sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID), sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID),
"xrpl::trustCreate : matching account ID"); "xrpl::trustCreate : matching account ID");
auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID)); auto const slePeer = AccountRootEntryW(bSetHigh ? uLowAccountID : uHighAccountID, view);
if (!slePeer) if (!slePeer)
return tecNO_TARGET; return tecNO_TARGET;
@@ -283,7 +284,11 @@ trustCreate(
} }
sleRippleState->setFieldU32(sfFlags, uFlags); sleRippleState->setFieldU32(sfFlags, uFlags);
increaseOwnerCount(view, sleAccount, sponsorSle, 1, j); AccountRootEntryW accountSle(bSetHigh ? uHighAccountID : uLowAccountID, view, j);
std::optional<AccountRootEntryW> sponsorAccSle;
if (sponsorSle)
sponsorAccSle.emplace(sponsorSle->getAccountID(sfAccount), view);
accountSle.increaseOwnerCount(sponsorAccSle, 1);
addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsor : sfLowSponsor); addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsor : sfLowSponsor);
@@ -349,7 +354,7 @@ updateTrustLine(
if (!state) if (!state)
return false; return false;
auto sle = view.peek(keylet::account(sender)); auto sle = AccountRootEntryW(sender, view, j);
if (!sle) if (!sle)
return false; return false;
@@ -376,9 +381,9 @@ updateTrustLine(
{ {
// VFALCO Where is the line being deleted? // VFALCO Where is the line being deleted?
// Clear the reserve of the sender, possibly delete the line! // Clear the reserve of the sender, possibly delete the line!
auto const currentSponsor = auto currentSponsor =
getLedgerEntryReserveSponsor(view, state, bSenderHigh ? sfHighSponsor : sfLowSponsor); getLedgerEntryReserveSponsor(view, state, bSenderHigh ? sfHighSponsor : sfLowSponsor);
decreaseOwnerCount(view, sle, currentSponsor, 1, j); sle.decreaseOwnerCount(currentSponsor, 1);
// Clear reserve flag. // Clear reserve flag.
state->clearFlag(senderReserveFlag); state->clearFlag(senderReserveFlag);
@@ -464,7 +469,7 @@ issueIOU(
finalBalance.get<Issue>().account = noAccount(); finalBalance.get<Issue>().account = noAccount();
auto const receiverAccount = view.peek(keylet::account(account)); auto receiverAccount = AccountRootEntryW(account, view);
if (!receiverAccount) if (!receiverAccount)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -476,7 +481,7 @@ issueIOU(
issue.account, issue.account,
account, account,
index.key, index.key,
receiverAccount, receiverAccount.mutableRawSle(),
false, false,
noRipple, noRipple,
false, false,
@@ -579,7 +584,7 @@ requireAuth(ReadView const& view, Issue const& issue, AccountID const& account,
// If this is a weak or legacy check, or if the account has a line, fail if // If this is a weak or legacy check, or if the account has a line, fail if
// auth is required and not set on the line // auth is required and not set on the line
if (auto const issuerAccount = view.read(keylet::account(issue.account)); if (auto const issuerAccount = AccountRootEntryR(issue.account, view);
issuerAccount && issuerAccount->isFlag(lsfRequireAuth)) issuerAccount && issuerAccount->isFlag(lsfRequireAuth))
{ {
if (trustLine) if (trustLine)
@@ -609,8 +614,8 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
auto const& issuerId = issue.getIssuer(); auto const& issuerId = issue.getIssuer();
if (issuerId == from || issuerId == to) if (issuerId == from || issuerId == to)
return tesSUCCESS; return tesSUCCESS;
auto const sleIssuer = view.read(keylet::account(issuerId)); auto const sleIssuer = AccountRootEntryR(issuerId, view);
if (sleIssuer == nullptr) if (!sleIssuer.exists())
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const isRippleDisabled = [&](AccountID account) -> bool { auto const isRippleDisabled = [&](AccountID account) -> bool {
@@ -665,8 +670,8 @@ addEmptyHolding(
if (isGlobalFrozen(ctx.view, issuerId)) if (isGlobalFrozen(ctx.view, issuerId))
return tecFROZEN; // LCOV_EXCL_LINE return tecFROZEN; // LCOV_EXCL_LINE
auto const sleSrc = ctx.view.peek(keylet::account(srcId)); auto const sleSrc = AccountRootEntryW(srcId, ctx.view);
auto const sleDst = ctx.view.peek(keylet::account(dstId)); auto sleDst = AccountRootEntryW(dstId, ctx.view, journal);
if (!sleDst || !sleSrc) if (!sleDst || !sleSrc)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Create path: DefaultRipple is still required. terNO_RIPPLE is // Create path: DefaultRipple is still required. terNO_RIPPLE is
@@ -681,20 +686,14 @@ addEmptyHolding(
return tecDUPLICATE; return tecDUPLICATE;
// A reserve sponsor only covers tx.Account's own objects. // A reserve sponsor only covers tx.Account's own objects.
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst); auto sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst);
if (!sponsorExp) if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE return sponsorExp.error(); // LCOV_EXCL_LINE
auto const sponsorSle = *sponsorExp; auto& sponsorSle = *sponsorExp;
// Can the account cover the trust line reserve ? // Can the account cover the trust line reserve ?
if (auto const ret = checkReserve( if (auto const ret = sleDst.checkReserve(
ctx, ctx, priorBalance, sponsorSle, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE);
sleDst,
priorBalance,
sponsorSle,
{.ownerCountDelta = 1},
journal,
tecNO_LINE_INSUF_RESERVE);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
return ret; return ret;
@@ -706,7 +705,7 @@ addEmptyHolding(
srcId, srcId,
dstId, dstId,
index.key, index.key,
sleDst, sleDst.mutableRawSle(),
/*bAuth=*/false, /*bAuth=*/false,
/*bNoRipple=*/true, /*bNoRipple=*/true,
/*bFreeze=*/false, /*bFreeze=*/false,
@@ -715,7 +714,7 @@ addEmptyHolding(
/*saLimit=*/STAmount{Issue{currency, dstId}}, /*saLimit=*/STAmount{Issue{currency, dstId}},
/*uQualityIn=*/0, /*uQualityIn=*/0,
/*uQualityOut=*/0, /*uQualityOut=*/0,
sponsorSle, sponsorSle ? sponsorSle->mutableRawSle() : SLE::pointer{},
journal); journal);
} }
@@ -728,7 +727,7 @@ removeEmptyHolding(
{ {
if (issue.native()) if (issue.native())
{ {
auto const sle = ctx.view.read(keylet::account(accountID)); auto const sle = AccountRootEntryW(accountID, ctx.view);
if (!sle) if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -753,13 +752,14 @@ removeEmptyHolding(
if (line->isFlag(lsfLowReserve)) if (line->isFlag(lsfLowReserve))
{ {
// Clear reserve for low account. // Clear reserve for low account.
auto sleLowAccount = ctx.view.peek(keylet::account(line->at(sfLowLimit)->getIssuer())); auto sleLowAccount =
AccountRootEntryW(line->at(sfLowLimit)->getIssuer(), ctx.view, journal);
if (!sleLowAccount) if (!sleLowAccount)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor); auto currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor);
decreaseOwnerCount(ctx.view, sleLowAccount, currentLowSponsor, 1, journal); sleLowAccount.decreaseOwnerCount(currentLowSponsor, 1);
// It's not really necessary to clear the reserve flag, since the line // 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 // is about to be deleted, but this will make the metadata reflect an
// accurate state at the time of deletion. // accurate state at the time of deletion.
@@ -770,13 +770,14 @@ removeEmptyHolding(
if (line->isFlag(lsfHighReserve)) if (line->isFlag(lsfHighReserve))
{ {
// Clear reserve for high account. // Clear reserve for high account.
auto sleHighAccount = ctx.view.peek(keylet::account(line->at(sfHighLimit)->getIssuer())); auto sleHighAccount =
AccountRootEntryW(line->at(sfHighLimit)->getIssuer(), ctx.view, journal);
if (!sleHighAccount) if (!sleHighAccount)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor); auto currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor);
decreaseOwnerCount(ctx.view, sleHighAccount, currentHighSponsor, 1, journal); sleHighAccount.decreaseOwnerCount(currentHighSponsor, 1);
// It's not really necessary to clear the reserve flag, since the line // 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 // is about to be deleted, but this will make the metadata reflect an
// accurate state at the time of deletion. // accurate state at the time of deletion.
@@ -805,8 +806,8 @@ deleteAMMTrustLine(
auto const& [low, high] = std::minmax( auto const& [low, high] = std::minmax(
sleState->getFieldAmount(sfLowLimit).getIssuer(), sleState->getFieldAmount(sfLowLimit).getIssuer(),
sleState->getFieldAmount(sfHighLimit).getIssuer()); sleState->getFieldAmount(sfHighLimit).getIssuer());
auto sleLow = view.peek(keylet::account(low)); auto sleLow = AccountRootEntryW(low, view, j);
auto sleHigh = view.peek(keylet::account(high)); auto sleHigh = AccountRootEntryW(high, view, j);
if (!sleLow || !sleHigh) if (!sleLow || !sleHigh)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -825,7 +826,7 @@ deleteAMMTrustLine(
if (ammAccountID && (low != *ammAccountID && high != *ammAccountID)) if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
return terNO_AMM; return terNO_AMM;
auto const sponsorSle = auto sponsorSle =
getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor); getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor);
if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter)) if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter))
@@ -838,7 +839,7 @@ deleteAMMTrustLine(
if (!sleState->isFlag(uFlags)) if (!sleState->isFlag(uFlags))
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
decreaseOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, 1, j); (!ammLow ? sleLow : sleHigh).decreaseOwnerCount(sponsorSle, 1);
return tesSUCCESS; return tesSUCCESS;
} }
+19 -18
View File
@@ -3,6 +3,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h> #include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/ledger/helpers/ProposalHelpers.h> #include <xrpl/ledger/helpers/ProposalHelpers.h>
@@ -76,7 +77,7 @@ getTxReserveSponsorID(STTx const& tx)
return {}; return {};
} }
std::expected<SLE::pointer, TER> std::expected<std::optional<AccountRootEntryW>, TER>
getTxReserveSponsor(ApplyViewContext ctx) getTxReserveSponsor(ApplyViewContext ctx)
{ {
auto const sponsorID = getTxReserveSponsorID(ctx.tx); auto const sponsorID = getTxReserveSponsorID(ctx.tx);
@@ -85,17 +86,17 @@ getTxReserveSponsor(ApplyViewContext ctx)
XRPL_ASSERT( XRPL_ASSERT(
ctx.view.rules().enabled(featureSponsor), ctx.view.rules().enabled(featureSponsor),
"xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled"); "xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
auto sle = ctx.view.peek(keylet::account(*sponsorID)); auto sle = AccountRootEntryW(*sponsorID, ctx.view);
// already checked in Transactor::checkSponsor // already checked in Transactor::checkSponsor
if (!sle) if (!sle.exists())
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
return sle; return sle;
} }
return SLE::pointer(); return std::nullopt;
} }
std::expected<SLE::const_pointer, TER> std::expected<std::optional<AccountRootEntryR>, TER>
getTxReserveSponsor(ReadView const& view, STTx const& tx) getTxReserveSponsor(ReadView const& view, STTx const& tx)
{ {
auto const sponsorID = getTxReserveSponsorID(tx); auto const sponsorID = getTxReserveSponsorID(tx);
@@ -104,18 +105,18 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx)
XRPL_ASSERT( XRPL_ASSERT(
view.rules().enabled(featureSponsor), view.rules().enabled(featureSponsor),
"xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled"); "xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
auto sle = view.read(keylet::account(*sponsorID)); auto sle = AccountRootEntryR(*sponsorID, view);
// already checked in Transactor::checkSponsor // already checked in Transactor::checkSponsor
if (!sle) if (!sle.exists())
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
return sle; return sle;
} }
return SLE::pointer(); return std::nullopt;
} }
std::expected<SLE::pointer, TER> std::expected<std::optional<AccountRootEntryW>, TER>
getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle) getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle)
{ {
// A reserve sponsor only covers tx.Account's own objects. // A reserve sponsor only covers tx.Account's own objects.
if (ctx.view.rules().enabled(fixCleanup3_2_0)) if (ctx.view.rules().enabled(fixCleanup3_2_0))
@@ -133,7 +134,7 @@ getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle)
} }
if (isPseudoAccount(accountSle) || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount]) if (isPseudoAccount(accountSle) || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount])
return SLE::pointer(); return std::nullopt;
return getTxReserveSponsor(ctx); return getTxReserveSponsor(ctx);
} }
@@ -151,7 +152,7 @@ getLedgerEntryReserveSponsorID(SLE::ConstRef sle, SF_ACCOUNT const& field)
return {}; return {};
} }
SLE::pointer std::optional<AccountRootEntryW>
getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT const& field) getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT const& field)
{ {
auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field); auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field);
@@ -160,9 +161,9 @@ getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT cons
XRPL_ASSERT( XRPL_ASSERT(
view.rules().enabled(featureSponsor), view.rules().enabled(featureSponsor),
"xrpl::getLedgerEntryReserveSponsor : sponsor exists + Sponsor enabled"); "xrpl::getLedgerEntryReserveSponsor : sponsor exists + Sponsor enabled");
return view.peek(keylet::account(*sponsorID)); return AccountRootEntryW(*sponsorID, view);
} }
return {}; return std::nullopt;
} }
void void
@@ -185,16 +186,16 @@ addSponsorToLedgerEntry(SLE::Ref sle, SLE::ConstRef sponsorSle, SF_ACCOUNT const
void void
addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::Ref sle, SF_ACCOUNT const& field) addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::Ref sle, SF_ACCOUNT const& field)
{ {
// getTxReserveSponsor yields a null pointer when the tx is not // getTxReserveSponsor yields nullopt when the tx is not reserve-sponsored,
// reserve-sponsored, so addSponsorToLedgerEntry becomes a no-op then. The // so addSponsorToLedgerEntry becomes a no-op then. The error case
// error case (tecINTERNAL) is an already-checked invariant; skip stamping. // (tecINTERNAL) is an already-checked invariant; skip stamping.
auto const sponsorSle = getTxReserveSponsor(ctx); auto const sponsorSle = getTxReserveSponsor(ctx);
if (sponsorSle && *sponsorSle) if (sponsorSle && *sponsorSle)
{ {
XRPL_ASSERT( XRPL_ASSERT(
ctx.view.rules().enabled(featureSponsor), ctx.view.rules().enabled(featureSponsor),
"xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled"); "xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled");
addSponsorToLedgerEntry(sle, *sponsorSle, field); addSponsorToLedgerEntry(sle, (**sponsorSle).rawSle(), field);
} }
} }
+24 -18
View File
@@ -7,6 +7,7 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -293,7 +294,7 @@ getLineIfUsable(
// we need to check if the associated assets have been frozen // we need to check if the associated assets have been frozen
if (view.rules().enabled(fixFrozenLPTokenTransfer)) if (view.rules().enabled(fixFrozenLPTokenTransfer))
{ {
auto const sleIssuer = view.read(keylet::account(issuer)); auto const sleIssuer = AccountRootEntryR(issuer, view);
if (!sleIssuer) if (!sleIssuer)
{ {
return nullptr; // LCOV_EXCL_LINE return nullptr; // LCOV_EXCL_LINE
@@ -562,7 +563,7 @@ canAddHolding(ReadView const& view, Issue const& issue)
return tesSUCCESS; // No special checks for XRP return tesSUCCESS; // No special checks for XRP
} }
auto const issuer = view.read(keylet::account(issue.getIssuer())); auto const issuer = AccountRootEntryR(issue.getIssuer(), view);
if (!issuer) if (!issuer)
{ {
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -738,6 +739,8 @@ directSendNoFeeIOU(
auto const senderFreezeFlag = bSenderHigh ? lsfHighFreeze : lsfLowFreeze; auto const senderFreezeFlag = bSenderHigh ? lsfHighFreeze : lsfLowFreeze;
auto const receiverReserveFlag = bSenderHigh ? lsfLowReserve : lsfHighReserve; auto const receiverReserveFlag = bSenderHigh ? lsfLowReserve : lsfHighReserve;
AccountRootEntryW senderSle(uSenderID, view, j);
// FIXME This NEEDS to be cleaned up and simplified. It's impossible // FIXME This NEEDS to be cleaned up and simplified. It's impossible
// for anyone to understand. // for anyone to understand.
if (saBefore > beast::kZero if (saBefore > beast::kZero
@@ -746,8 +749,7 @@ directSendNoFeeIOU(
// Sender is zero or negative. // Sender is zero or negative.
&& sleRippleState->isFlag(senderReserveFlag) && sleRippleState->isFlag(senderReserveFlag)
// Sender reserve is set. // Sender reserve is set.
&& sleRippleState->isFlag(senderNoRippleFlag) != && sleRippleState->isFlag(senderNoRippleFlag) != senderSle->isFlag(lsfDefaultRipple) &&
view.read(keylet::account(uSenderID))->isFlag(lsfDefaultRipple) &&
!sleRippleState->isFlag(senderFreezeFlag) && !sleRippleState->isFlag(senderFreezeFlag) &&
!sleRippleState->getFieldAmount(bSenderHigh ? sfHighLimit : sfLowLimit) !sleRippleState->getFieldAmount(bSenderHigh ? sfHighLimit : sfLowLimit)
// Sender trust limit is 0. // Sender trust limit is 0.
@@ -758,9 +760,9 @@ directSendNoFeeIOU(
// Sender quality out is 0. // Sender quality out is 0.
{ {
// Clear the reserve of the sender, possibly delete the line! // Clear the reserve of the sender, possibly delete the line!
auto const currentSponsor = getLedgerEntryReserveSponsor( auto currentSponsor = getLedgerEntryReserveSponsor(
view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor);
decreaseOwnerCount(view, view.peek(keylet::account(uSenderID)), currentSponsor, 1, j); senderSle.decreaseOwnerCount(currentSponsor, 1);
removeSponsorFromLedgerEntry( removeSponsorFromLedgerEntry(
sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor);
@@ -805,7 +807,7 @@ directSendNoFeeIOU(
<< to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : "
<< saAmount.getFullText(); << saAmount.getFullText();
auto const sleAccount = view.peek(keylet::account(uReceiverID)); auto sleAccount = AccountRootEntryW(uReceiverID, view);
if (!sleAccount) if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -817,7 +819,7 @@ directSendNoFeeIOU(
uSenderID, uSenderID,
uReceiverID, uReceiverID,
index.key, index.key,
sleAccount, sleAccount.mutableRawSle(),
false, false,
noRipple, noRipple,
false, false,
@@ -1010,10 +1012,12 @@ accountSendIOU(
*/ */
TER terResult(tesSUCCESS); TER terResult(tesSUCCESS);
SLE::pointer const sender = SLE::pointer const sender = uSenderID != beast::kZero
uSenderID != beast::kZero ? view.peek(keylet::account(uSenderID)) : SLE::pointer(); ? AccountRootEntryW(uSenderID, view).mutableRawSle()
SLE::pointer const receiver = : SLE::pointer();
uReceiverID != beast::kZero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer(); SLE::pointer const receiver = uReceiverID != beast::kZero
? AccountRootEntryW(uReceiverID, view).mutableRawSle()
: SLE::pointer();
if (auto stream = j.trace()) if (auto stream = j.trace())
{ {
@@ -1106,8 +1110,9 @@ accountSendMultiIOU(
* ensure that transfers are balanced. * ensure that transfers are balanced.
*/ */
SLE::pointer const sender = SLE::pointer const sender = senderID != beast::kZero
senderID != beast::kZero ? view.peek(keylet::account(senderID)) : SLE::pointer(); ? AccountRootEntryW(senderID, view).mutableRawSle()
: SLE::pointer();
if (auto stream = j.trace()) if (auto stream = j.trace())
{ {
@@ -1138,8 +1143,9 @@ accountSendMultiIOU(
if (!amount || (senderID == receiverID)) if (!amount || (senderID == receiverID))
continue; continue;
SLE::pointer const receiver = SLE::pointer const receiver = receiverID != beast::kZero
receiverID != beast::kZero ? view.peek(keylet::account(receiverID)) : SLE::pointer(); ? AccountRootEntryW(receiverID, view).mutableRawSle()
: SLE::pointer();
if (auto stream = j.trace()) if (auto stream = j.trace())
{ {
@@ -1591,8 +1597,8 @@ transferXRP(
XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver"); XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver");
XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP"); XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP");
SLE::pointer const sender = view.peek(keylet::account(from)); SLE::pointer const sender = AccountRootEntryW(from, view).mutableRawSle();
SLE::pointer const receiver = view.peek(keylet::account(to)); SLE::pointer const receiver = AccountRootEntryW(to, view).mutableRawSle();
if (!sender || !receiver) if (!sender || !receiver)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -35,6 +35,7 @@
#include <atomic> #include <atomic>
#include <cstddef> #include <cstddef>
#include <filesystem> #include <filesystem>
#include <format>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
@@ -193,7 +194,7 @@ public:
if (!s.ok()) if (!s.ok())
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(
std::string("Unable to set RocksDB bbt_options: ") + s.ToString()); std::format("Unable to set RocksDB bbt_options: {}", s.ToString()));
} }
} }
@@ -206,7 +207,7 @@ public:
if (!s.ok()) if (!s.ok())
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(
std::string("Unable to set RocksDB options: ") + s.ToString()); std::format("Unable to set RocksDB options: {}", s.ToString()));
} }
} }
@@ -241,7 +242,7 @@ public:
if (!status.ok() || (localDb == nullptr)) if (!status.ok() || (localDb == nullptr))
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(
std::string("Unable to open/create RocksDB: ") + status.ToString()); std::format("Unable to open/create RocksDB: {}", status.ToString()));
} }
db.reset(localDb); db.reset(localDb);
} }
@@ -356,7 +357,7 @@ public:
auto ret = db->Write(options, &wb); auto ret = db->Write(options, &wb);
if (!ret.ok()) if (!ret.ok())
Throw<std::runtime_error>("storeBatch failed: " + ret.ToString()); Throw<std::runtime_error>(std::format("storeBatch failed: {}", ret.ToString()));
} }
void void
+3 -2
View File
@@ -14,6 +14,7 @@
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
#include <format>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <optional> #include <optional>
@@ -105,8 +106,8 @@ Permission::Permission()
if (type <= UINT16_MAX) if (type <= UINT16_MAX)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
Throw<std::logic_error>( Throw<std::logic_error>(std::format(
"Granular permission value must exceed the maximum uint16_t value: " + name); "Granular permission value must exceed the maximum uint16_t value: {}", name));
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
} }
+4 -5
View File
@@ -37,6 +37,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <format>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <memory> #include <memory>
@@ -1193,8 +1194,7 @@ muldiv(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divis
if (ret > std::numeric_limits<std::uint64_t>::max()) if (ret > std::numeric_limits<std::uint64_t>::max())
{ {
Throw<std::overflow_error>( Throw<std::overflow_error>(
"overflow: (" + std::to_string(multiplier) + " * " + std::to_string(multiplicand) + std::format("overflow: ({} * {}) / {}", multiplier, multiplicand, divisor));
") / " + std::to_string(divisor));
} }
return static_cast<uint64_t>(ret); return static_cast<uint64_t>(ret);
@@ -1215,9 +1215,8 @@ muldivRound(
if (ret > std::numeric_limits<std::uint64_t>::max()) if (ret > std::numeric_limits<std::uint64_t>::max())
{ {
Throw<std::overflow_error>( Throw<std::overflow_error>(std::format(
"overflow: ((" + std::to_string(multiplier) + " * " + std::to_string(multiplicand) + "overflow: (({} * {}) + {}) / {}", multiplier, multiplicand, rounding, divisor));
") + " + std::to_string(rounding) + ") / " + std::to_string(divisor));
} }
return static_cast<uint64_t>(ret); return static_cast<uint64_t>(ret);
+2 -3
View File
@@ -35,9 +35,8 @@ STLedgerEntry::STLedgerEntry(Keylet const& k) : STObject(sfLedgerEntry), key_(k.
if (format == nullptr) if (format == nullptr)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"Attempt to create a SLE of unknown type " + "Attempt to create a SLE of unknown type {}", safeCast<std::uint16_t>(k.type)));
std::to_string(safeCast<std::uint16_t>(k.type)));
} }
set(format->getSOTemplate()); set(format->getSOTemplate());
+2 -1
View File
@@ -19,6 +19,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <format>
#include <limits> #include <limits>
#include <ostream> #include <ostream>
#include <stdexcept> #include <stdexcept>
@@ -170,7 +171,7 @@ partsFromString(std::string const& number)
boost::smatch match; boost::smatch match;
if (!boost::regex_match(number, match, kReNumber)) if (!boost::regex_match(number, match, kReNumber))
Throw<std::runtime_error>("'" + number + "' is not a number"); Throw<std::runtime_error>(std::format("'{}' is not a number", number));
// Match fields: // Match fields:
// 0 = whole input // 0 = whole input
+2 -3
View File
@@ -59,9 +59,8 @@ getTxFormat(TxType type)
if (format == nullptr) if (format == nullptr)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"Invalid transaction type " + "Invalid transaction type {}", safeCast<std::underlying_type_t<TxType>>(type)));
std::to_string(safeCast<std::underlying_type_t<TxType>>(type)));
} }
return format; return format;
+2 -2
View File
@@ -9,8 +9,8 @@
#include <xrpl/protocol/Serializer.h> #include <xrpl/protocol/Serializer.h>
#include <cstddef> #include <cstddef>
#include <format>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <utility> #include <utility>
namespace xrpl { namespace xrpl {
@@ -22,7 +22,7 @@ STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name)
if (slice.size() % UInt256::size() != 0) if (slice.size() % UInt256::size() != 0)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(
"Bad serialization for STVector256: " + std::to_string(slice.size())); std::format("Bad serialization for STVector256: {}", slice.size()));
} }
auto const cnt = slice.size() / UInt256::size(); auto const cnt = slice.size() / UInt256::size();
+2 -1
View File
@@ -70,7 +70,8 @@ STXChainBridge::STXChainBridge(SField const& name, json::Value const& v) : STBas
std::string const name = it.memberName(); std::string const name = it.memberName();
if (!kBridgeJson.isMember(name)) if (!kBridgeJson.isMember(name))
{ {
Throw<std::runtime_error>("STXChainBridge extra field detected: " + name); Throw<std::runtime_error>(
std::format("STXChainBridge extra field detected: {}", name));
} }
} }
return true; return true;
+3 -3
View File
@@ -15,8 +15,8 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <format>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <type_traits> #include <type_traits>
namespace xrpl { namespace xrpl {
@@ -456,7 +456,7 @@ SerialIter::getFieldID(int& type, int& name)
// uncommon type // uncommon type
type = get8(); type = get8();
if (type < 16) if (type < 16)
Throw<std::runtime_error>("gFID: uncommon type out of range " + std::to_string(type)); Throw<std::runtime_error>(std::format("gFID: uncommon type out of range {}", type));
} }
if (name == 0) if (name == 0)
@@ -464,7 +464,7 @@ SerialIter::getFieldID(int& type, int& name)
// uncommon name // uncommon name
name = get8(); name = get8();
if (name < 16) if (name < 16)
Throw<std::runtime_error>("gFID: uncommon name out of range " + std::to_string(name)); Throw<std::runtime_error>(std::format("gFID: uncommon name out of range {}", name));
} }
} }
+5 -4
View File
@@ -27,6 +27,7 @@
#include <soci/sqlite3/soci-sqlite3.h> // IWYU pragma: keep #include <soci/sqlite3/soci-sqlite3.h> // IWYU pragma: keep
#include <format>
#include <memory> #include <memory>
namespace xrpl { namespace xrpl {
@@ -40,8 +41,8 @@ getSociSqliteInit(std::string const& name, std::string const& dir, std::string c
{ {
if (name.empty()) if (name.empty())
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"Sqlite databases must specify a dir and a name. Name: " + name + " Dir: " + dir); "Sqlite databases must specify a dir and a name. Name: {} Dir: {}", name, dir));
} }
std::filesystem::path file(dir); std::filesystem::path file(dir);
if (std::filesystem::is_directory(file)) if (std::filesystem::is_directory(file))
@@ -56,7 +57,7 @@ getSociInit(BasicConfig const& config, std::string const& dbName)
auto const backendName = get(section, Keys::kBackend, "sqlite"); auto const backendName = get(section, Keys::kBackend, "sqlite");
if (backendName != "sqlite") if (backendName != "sqlite")
Throw<std::runtime_error>("Unsupported soci backend: " + backendName); Throw<std::runtime_error>(std::format("Unsupported soci backend: {}", backendName));
auto const path = config.legacy(Sections::kDatabasePath); auto const path = config.legacy(Sections::kDatabasePath);
auto const ext = dbName == "validators" || dbName == "peerfinder" ? ".sqlite" : ".db"; auto const ext = dbName == "validators" || dbName == "peerfinder" ? ".sqlite" : ".db";
@@ -101,7 +102,7 @@ open(soci::session& s, std::string const& beName, std::string const& connectionS
} }
else else
{ {
Throw<std::runtime_error>("Unsupported soci backend: " + beName); Throw<std::runtime_error>(std::format("Unsupported soci backend: {}", beName));
} }
} }
+2 -1
View File
@@ -26,6 +26,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <format>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <mutex> #include <mutex>
@@ -48,7 +49,7 @@ to_string(Manifest const& m)
return "Revocation Manifest " + mk; return "Revocation Manifest " + mk;
if (!m.signingKey) if (!m.signingKey)
Throw<std::runtime_error>("No SigningKey in manifest " + mk); Throw<std::runtime_error>(std::format("No SigningKey in manifest {}", mk));
return "Manifest " + mk + " (" + std::to_string(m.sequence) + ": " + return "Manifest " + mk + " (" + std::to_string(m.sequence) + ": " +
toBase58(TokenType::NodePublic, *m.signingKey) + ")"; toBase58(TokenType::NodePublic, *m.signingKey) + ")";
+3 -2
View File
@@ -8,6 +8,7 @@
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
#include <format>
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
@@ -107,10 +108,10 @@ SHAMapNodeID::getChildNodeID(unsigned int branch) const
depth_ <= SHAMap::kLeafDepth, "xrpl::SHAMapNodeID::getChildNodeID : maximum leaf depth"); depth_ <= SHAMap::kLeafDepth, "xrpl::SHAMapNodeID::getChildNodeID : maximum leaf depth");
if (depth_ >= SHAMap::kLeafDepth) if (depth_ >= SHAMap::kLeafDepth)
Throw<std::logic_error>("Request for child node ID of " + to_string(*this)); Throw<std::logic_error>(std::format("Request for child node ID of {}", to_string(*this)));
if (!isPrefixOf(id_)) if (!isPrefixOf(id_))
Throw<std::logic_error>("Incorrect mask for " + to_string(*this)); Throw<std::logic_error>(std::format("Incorrect mask for {}", to_string(*this)));
SHAMapNodeID node{depth_ + 1, id_}; SHAMapNodeID node{depth_ + 1, id_};
node.id_.begin()[depth_ / 2] |= ((depth_ & 1) != 0u) ? branch : (branch << 4); node.id_.begin()[depth_ / 2] |= ((depth_ & 1) != 0u) ? branch : (branch << 4);
+21 -24
View File
@@ -18,6 +18,7 @@
#include <xrpl/shamap/SHAMapTxPlusMetaLeafNode.h> #include <xrpl/shamap/SHAMapTxPlusMetaLeafNode.h>
#include <cstdint> #include <cstdint>
#include <format>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
@@ -30,9 +31,8 @@ SHAMapTreeNode::makeTransaction(Slice data, SHAMapHash const& hash, bool hashVal
{ {
if (data.size() < kMinShaMapItemBytes) if (data.size() < kMinShaMapItemBytes)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"Short TXN node: " + std::to_string(data.size()) + " bytes (minimum " + "Short TXN node: {} bytes (minimum {} required)", data.size(), kMinShaMapItemBytes));
std::to_string(kMinShaMapItemBytes) + " required)");
} }
auto item = makeShamapitem(sha512Half(HashPrefix::TransactionId, data), data); auto item = makeShamapitem(sha512Half(HashPrefix::TransactionId, data), data);
@@ -52,27 +52,25 @@ SHAMapTreeNode::makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool
if (s.size() < tag.kBytes) if (s.size() < tag.kBytes)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"Short TXN+MD node: " + std::to_string(s.size()) + " bytes (minimum " + "Short TXN+MD node: {} bytes (minimum {} required for tag)", s.size(), tag.kBytes));
std::to_string(tag.kBytes) + " required for tag)");
} }
// FIXME: improve this interface so that the above check isn't needed // FIXME: improve this interface so that the above check isn't needed
if (!s.getBitString(tag, s.size() - tag.kBytes)) if (!s.getBitString(tag, s.size() - tag.kBytes))
{ {
Throw<std::out_of_range>( Throw<std::out_of_range>(std::format(
"Short TXN+MD node: failed to read tag at offset " + "Short TXN+MD node: failed to read tag at offset {}", s.size() - tag.kBytes));
std::to_string(s.size() - tag.kBytes));
} }
s.chop(tag.kBytes); s.chop(tag.kBytes);
if (s.size() < kMinShaMapItemBytes) if (s.size() < kMinShaMapItemBytes)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"Short TXN+MD node: " + std::to_string(s.size()) + "Short TXN+MD node: {} bytes after tag removal (minimum {} required)",
" bytes after tag removal (minimum " + std::to_string(kMinShaMapItemBytes) + s.size(),
" required)"); kMinShaMapItemBytes));
} }
auto item = makeShamapitem(tag, s.slice()); auto item = makeShamapitem(tag, s.slice());
@@ -92,16 +90,15 @@ SHAMapTreeNode::makeAccountState(Slice data, SHAMapHash const& hash, bool hashVa
if (s.size() < tag.kBytes) if (s.size() < tag.kBytes)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"Short AS node: " + std::to_string(s.size()) + " bytes (minimum " + "Short AS node: {} bytes (minimum {} required for tag)", s.size(), tag.kBytes));
std::to_string(tag.kBytes) + " required for tag)");
} }
// FIXME: improve this interface so that the above check isn't needed // FIXME: improve this interface so that the above check isn't needed
if (!s.getBitString(tag, s.size() - tag.kBytes)) if (!s.getBitString(tag, s.size() - tag.kBytes))
{ {
Throw<std::out_of_range>( Throw<std::out_of_range>(
"Short AS node: failed to read tag at offset " + std::to_string(s.size() - tag.kBytes)); std::format("Short AS node: failed to read tag at offset {}", s.size() - tag.kBytes));
} }
s.chop(tag.kBytes); s.chop(tag.kBytes);
@@ -111,9 +108,10 @@ SHAMapTreeNode::makeAccountState(Slice data, SHAMapHash const& hash, bool hashVa
if (s.size() < kMinShaMapItemBytes) if (s.size() < kMinShaMapItemBytes)
{ {
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"Short AS node: " + std::to_string(s.size()) + " bytes after tag removal (minimum " + "Short AS node: {} bytes after tag removal (minimum {} required)",
std::to_string(kMinShaMapItemBytes) + " required)"); s.size(),
kMinShaMapItemBytes));
} }
auto item = makeShamapitem(tag, s.slice()); auto item = makeShamapitem(tag, s.slice());
@@ -152,7 +150,7 @@ SHAMapTreeNode::makeFromWire(Slice rawNode)
if (type == kWireTypeTransactionWithMeta) if (type == kWireTypeTransactionWithMeta)
return makeTransactionWithMeta(rawNode, hash, hashValid); return makeTransactionWithMeta(rawNode, hash, hashValid);
Throw<std::runtime_error>("wire: Unknown type (" + std::to_string(type) + ")"); Throw<std::runtime_error>(std::format("wire: Unknown type ({})", type));
} }
SHAMapTreeNodePtr SHAMapTreeNodePtr
@@ -183,9 +181,8 @@ SHAMapTreeNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash)
if (type == HashPrefix::TxNode) if (type == HashPrefix::TxNode)
return makeTransactionWithMeta(rawNode, hash, hashValid); return makeTransactionWithMeta(rawNode, hash, hashValid);
Throw<std::runtime_error>( Throw<std::runtime_error>(std::format(
"prefix: unknown type (" + "prefix: unknown type ({})", safeCast<std::underlying_type_t<HashPrefix>>(type)));
std::to_string(safeCast<std::underlying_type_t<HashPrefix>>(type)) + ")");
} }
std::string std::string
+19 -18
View File
@@ -11,6 +11,7 @@
#include <xrpl/json/to_string.h> // IWYU pragma: keep #include <xrpl/json/to_string.h> // IWYU pragma: keep
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DelegateHelpers.h> #include <xrpl/ledger/helpers/DelegateHelpers.h>
@@ -586,7 +587,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
if (feePayer.type == FeePayerType::SponsorCoSigned) if (feePayer.type == FeePayerType::SponsorCoSigned)
{ {
auto const sponsorReserve = accountReserve(ctx.view, payerSle, ctx.j); auto const sponsorReserve = accountReserve(ctx.view, feePayer.id, ctx.j);
maxSpendable = payerSle->getFieldAmount(sfBalance).xrp() - sponsorReserve; maxSpendable = payerSle->getFieldAmount(sfBalance).xrp() - sponsorReserve;
} }
else else
@@ -652,7 +653,7 @@ Transactor::payFee()
XRPAmount spendable = balance; XRPAmount spendable = balance;
if (feePayer.type == FeePayerType::SponsorCoSigned) if (feePayer.type == FeePayerType::SponsorCoSigned)
{ {
auto const sponsorReserve = accountReserve(view(), sle, j_); auto const sponsorReserve = accountReserve(view(), feePayer.id, j_);
// max(balance - reserve, 0) with overflow handling // max(balance - reserve, 0) with overflow handling
spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero; spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero;
} }
@@ -700,7 +701,7 @@ Transactor::checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j
{ {
auto const id = tx.getAccountID(sfAccount); auto const id = tx.getAccountID(sfAccount);
auto const sle = view.read(keylet::account(id)); auto const sle = AccountRootEntryR(id, view);
if (!sle) if (!sle)
{ {
@@ -765,7 +766,7 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
{ {
auto const id = ctx.tx.getAccountID(sfAccount); auto const id = ctx.tx.getAccountID(sfAccount);
auto const sle = ctx.view.read(keylet::account(id)); auto const sle = AccountRootEntryR(id, ctx.view);
if (!sle) if (!sle)
{ {
@@ -835,7 +836,7 @@ Transactor::ticketDelete(
// Update the account root's TicketCount. If the ticket count drops to // Update the account root's TicketCount. If the ticket count drops to
// zero remove the (optional) field. // zero remove the (optional) field.
auto sleAccount = view.peek(keylet::account(account)); auto sleAccount = AccountRootEntryW(account, view);
if (!sleAccount) if (!sleAccount)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
@@ -885,19 +886,19 @@ Transactor::apply()
// If the transactor requires a valid account and the transaction doesn't // If the transactor requires a valid account and the transaction doesn't
// list one, preflight will have already a flagged a failure. // list one, preflight will have already a flagged a failure.
auto const sle = view().peek(keylet::account(accountID_)); auto sle = AccountRootEntryW(accountID_, view());
// sle must exist except for transactions // sle must exist except for transactions
// that allow zero account. // that allow zero account.
XRPL_ASSERT( XRPL_ASSERT(
sle != nullptr || accountID_ == beast::kZero, sle.exists() || accountID_ == beast::kZero,
"xrpl::Transactor::apply : non-null SLE or zero account"); "xrpl::Transactor::apply : non-null SLE or zero account");
if (sle) if (sle)
{ {
preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp(); preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp();
TER result = consumeSeqProxy(sle); TER result = consumeSeqProxy(sle.mutableRawSle());
if (!isTesSuccess(result)) if (!isTesSuccess(result))
return result; return result;
@@ -908,7 +909,7 @@ Transactor::apply()
if (sle->isFieldPresent(sfAccountTxnID)) if (sle->isFieldPresent(sfAccountTxnID))
sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID()); sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID());
view().update(sle); sle.update();
} }
return doApply(); return doApply();
@@ -925,7 +926,7 @@ Transactor::checkSign(
bool permitUncreatedAccount) bool permitUncreatedAccount)
{ {
{ {
auto const sle = view.read(keylet::account(idAccount)); auto const sle = AccountRootEntryR(idAccount, view);
if ((view.rules().enabled(featureLendingProtocol) || if ((view.rules().enabled(featureLendingProtocol) ||
view.rules().enabled(featureBatchV1_1) || view.rules().enabled(fixCleanup3_3_0)) && view.rules().enabled(featureBatchV1_1) || view.rules().enabled(fixCleanup3_3_0)) &&
@@ -991,7 +992,7 @@ Transactor::checkSign(
// Look up the account. // Look up the account.
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner))); auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const sleAccount = view.read(keylet::account(idAccount)); auto const sleAccount = AccountRootEntryR(idAccount, view);
if (!sleAccount) if (!sleAccount)
{ {
// An account that does not exist yet can only be authorized by its own // An account that does not exist yet can only be authorized by its own
@@ -1004,7 +1005,7 @@ Transactor::checkSign(
return tesSUCCESS; return tesSUCCESS;
} }
return checkSingleSign(view, idSigner, idAccount, sleAccount, j); return checkSingleSign(view, idSigner, idAccount, sleAccount.rawSle(), j);
} }
NotTEC NotTEC
@@ -1153,7 +1154,7 @@ Transactor::checkMultiSign(
// In any of these cases we need to know whether the account is in // In any of these cases we need to know whether the account is in
// the ledger. Determine that now. // the ledger. Determine that now.
auto const sleTxSignerRoot = view.read(keylet::account(txSignerAcctID)); auto const sleTxSignerRoot = AccountRootEntryR(txSignerAcctID, view);
if (signingAcctIDFromPubKey == txSignerAcctID) if (signingAcctIDFromPubKey == txSignerAcctID)
{ {
@@ -1296,7 +1297,7 @@ Transactor::reset(XRPAmount fee)
{ {
ctx_.discard(); ctx_.discard();
auto const txnAcct = view().peek(keylet::account(ctx_.tx.getAccountID(sfAccount))); auto txnAcct = AccountRootEntryW(ctx_.tx.getAccountID(sfAccount), view());
// The account should never be missing from the ledger. But if it // The account should never be missing from the ledger. But if it
// is missing then we can't very well charge it a fee, can we? // is missing then we can't very well charge it a fee, can we?
@@ -1331,7 +1332,7 @@ Transactor::reset(XRPAmount fee)
XRPAmount spendable = balance; XRPAmount spendable = balance;
if (feePayer.type == FeePayerType::SponsorCoSigned) if (feePayer.type == FeePayerType::SponsorCoSigned)
{ {
auto const sponsorReserve = accountReserve(view(), payerSle, j_); auto const sponsorReserve = accountReserve(view(), feePayer.id, j_);
// max(balance - reserve, 0) with overflow handling // max(balance - reserve, 0) with overflow handling
spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero; spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero;
} }
@@ -1364,13 +1365,13 @@ Transactor::reset(XRPAmount fee)
payerSle->setFieldAmount(feePayer.balanceField, feeAmountAfter); payerSle->setFieldAmount(feePayer.balanceField, feeAmountAfter);
} }
TER const ter{consumeSeqProxy(txnAcct)}; TER const ter{consumeSeqProxy(txnAcct.mutableRawSle())};
XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS"); XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS");
if (isTesSuccess(ter)) if (isTesSuccess(ter))
{ {
view().update(txnAcct); txnAcct.update();
if (payerSle != txnAcct) if (payerSle != txnAcct.mutableRawSle())
view().update(payerSle); view().update(payerSle);
} }
+13 -4
View File
@@ -735,6 +735,15 @@ NoDeepFreezeTrustLinesWithoutFreeze::finalize(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool
isPseudoAccountSle(SLE::ConstRef sleAcct)
{
auto const& fields = getPseudoAccountFields();
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
std::ranges::any_of(
fields, [&sleAcct](SField const* sf) { return sleAcct->isFieldPresent(*sf); });
}
void void
ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after) ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after)
{ {
@@ -742,7 +751,7 @@ ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after)
{ {
accountsCreated_++; accountsCreated_++;
accountSeq_ = (*after)[sfSequence]; accountSeq_ = (*after)[sfSequence];
pseudoAccount_ = isPseudoAccount(after); pseudoAccount_ = isPseudoAccountSle(after);
flags_ = after->getFlags(); flags_ = after->getFlags();
} }
} }
@@ -1020,9 +1029,9 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstR
if (after && after->getType() == ltACCOUNT_ROOT) if (after && after->getType() == ltACCOUNT_ROOT)
{ {
bool const isPseudo = [&]() { bool const isPseudo = [&]() {
// isPseudoAccount checks that any of the pseudo-account fields are // isPseudoAccountSle checks that any of the pseudo-account fields
// set. // are set.
if (isPseudoAccount(after)) if (isPseudoAccountSle(after))
return true; return true;
// Not all pseudo-accounts have a zero sequence, but all accounts // Not all pseudo-accounts have a zero sequence, but all accounts
// with a zero sequence had better be pseudo-accounts. // with a zero sequence had better be pseudo-accounts.
+58 -15
View File
@@ -23,6 +23,7 @@
#include <xrpl/protocol/TxFormats.h> #include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/UintTypes.h> #include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h> #include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/invariants/InvariantCheck.h>
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h> #include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
#include <algorithm> #include <algorithm>
@@ -41,6 +42,7 @@ constexpr auto kConfidentialMptTxTypes = std::to_array<TxType>({
ttCONFIDENTIAL_MPT_MERGE_INBOX, ttCONFIDENTIAL_MPT_MERGE_INBOX,
ttCONFIDENTIAL_MPT_CLAWBACK, ttCONFIDENTIAL_MPT_CLAWBACK,
ttCONFIDENTIAL_MPT_MIRROR_UPDATE, ttCONFIDENTIAL_MPT_MIRROR_UPDATE,
ttCONFIDENTIAL_MPT_HOLDER_KEY_UPDATE,
}); });
// Clamp to the cap (== INT64_MAX) before the signed conversion. Invariant // Clamp to the cap (== INT64_MAX) before the signed conversion. Invariant
@@ -73,7 +75,7 @@ ValidMPTIssuance::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef
// only meaningful post-fixCleanup3_2_0 (the field is never set // only meaningful post-fixCleanup3_2_0 (the field is never set
// pre-amendment, and the holding-deletion rule does not apply). // pre-amendment, and the holding-deletion rule does not apply).
// Skip both blocks when the amendment is off so we avoid wasted work // Skip both blocks when the amendment is off so we avoid wasted work
// on the hot path. // on the hot path, except where noted for fixCleanup3_5_0 below.
bool const fix320Enabled = isFeatureEnabled(fixCleanup3_2_0); bool const fix320Enabled = isFeatureEnabled(fixCleanup3_2_0);
if (after && after->getType() == ltMPTOKEN_ISSUANCE) if (after && after->getType() == ltMPTOKEN_ISSUANCE)
@@ -108,7 +110,10 @@ ValidMPTIssuance::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef
if (isDelete) if (isDelete)
{ {
mptokensDeleted_++; mptokensDeleted_++;
if (fix320Enabled) // deletedHoldings_ also feeds finalize()'s erase-time public
// balance check, gated on fixCleanup3_5_0 independently of
// fixCleanup3_2_0.
if (fix320Enabled || isFeatureEnabled(fixCleanup3_5_0))
deletedHoldings_.push_back(after); deletedHoldings_.push_back(after);
} }
else if (!before) else if (!before)
@@ -195,6 +200,19 @@ ValidMPTIssuance::finalize(
return false; return false;
} }
// Deleting an MPToken with a non-zero MPTAmount is rejected.
if (rules.enabled(fixCleanup3_5_0))
{
for (auto const& sleHolding : deletedHoldings_)
{
if (sleHolding->getType() == ltMPTOKEN && sleHolding->getFieldU64(sfMPTAmount) > 0)
{
JLOG(j.fatal()) << "Invariant failed: MPToken deleted with non-zero balance";
return false;
}
}
}
if (isTesSuccess(result) || (mptV2Enabled && result == tecINCOMPLETE)) if (isTesSuccess(result) || (mptV2Enabled && result == tecINCOMPLETE))
{ {
[[maybe_unused]] [[maybe_unused]]
@@ -579,16 +597,27 @@ ValidConfidentialMPToken::visitEntry(
change.mptAmountDelta = change.mptAmountDelta =
subtractMPTAmountDelta(change.mptAmountDelta, before->getFieldU64(sfMPTAmount)); subtractMPTAmountDelta(change.mptAmountDelta, before->getFieldU64(sfMPTAmount));
// Cannot delete MPToken with non-zero confidential state or non-zero public amount // Cannot delete MPToken with non-zero confidential state.
if (isDelete) if (isDelete)
{ {
bool const hasPublicBalance = before->getFieldU64(sfMPTAmount) > 0; // changes_ is keyed by issuance, so sibling holders erased by the
bool const hasEncryptedFields = before->isFieldPresent(sfConfidentialBalanceSpending) || // same transaction share this entry. Only ever set these flags,
// never clear them, or an empty sibling visited later would mask
// a funded MPToken.
// Retired by fixCleanup3_5_0, which moved the public balance
// check to ValidMPTIssuance::finalize. Kept pre-amendment for
// consensus safety: a non-zero public balance used to feed the
// confidential gate below, rejecting the erase whenever the
// issuance's COA was non-zero, and already-validated ledgers
// depend on that.
if (!isFeatureEnabled(fixCleanup3_5_0) && before->getFieldU64(sfMPTAmount) > 0)
changes_[id].deletedWithBalanceBefore = true;
if (before->isFieldPresent(sfConfidentialBalanceSpending) ||
before->isFieldPresent(sfConfidentialBalanceInbox) || before->isFieldPresent(sfConfidentialBalanceInbox) ||
before->isFieldPresent(sfIssuerEncryptedBalance) || before->isFieldPresent(sfIssuerEncryptedBalance) ||
before->isFieldPresent(sfAuditorEncryptedBalance); before->isFieldPresent(sfAuditorEncryptedBalance))
if (hasPublicBalance || hasEncryptedFields)
changes_[id].deletedWithEncrypted = true; changes_[id].deletedWithEncrypted = true;
} }
} }
@@ -694,6 +723,8 @@ ValidConfidentialMPToken::finalize(
if (result != tesSUCCESS) if (result != tesSUCCESS)
return true; return true;
bool const fix350Enabled = view.rules().enabled(fixCleanup3_5_0);
for (auto const& [id, checks] : changes_) for (auto const& [id, checks] : changes_)
{ {
// Find the MPTokenIssuance // Find the MPTokenIssuance
@@ -708,8 +739,20 @@ ValidConfidentialMPToken::finalize(
if (!issuance) if (!issuance)
continue; continue;
// Cannot delete MPToken with non-zero confidential state // Cannot delete MPToken with non-zero confidential state.
if (checks.deletedWithEncrypted) //
// Before fixCleanup3_5_0 this gate also absorbed the pre-transaction
// public balance, so any drain-then-erase of an MPToken -- an
// AMMWithdraw of the whole pool, a LoanBrokerDelete returning cover --
// was rejected whenever some unrelated holder of the same issuance
// held a confidential balance. The COA gate itself is correct for
// ciphertext and mirrors MPTokenAuthorize::preclaim; only the public
// balance leg was misplaced.
bool const deletedWithEncrypted = fix350Enabled
? checks.deletedWithEncrypted
: (checks.deletedWithEncrypted || checks.deletedWithBalanceBefore);
if (deletedWithEncrypted)
{ {
if ((*issuance)[~sfConfidentialOutstandingAmount].value_or(0) > 0) if ((*issuance)[~sfConfidentialOutstandingAmount].value_or(0) > 0)
{ {
@@ -784,10 +827,10 @@ ValidConfidentialMPToken::finalize(
return false; return false;
} }
// Among confidential MPT transactions, only ConfidentialMPTSend and // Reaching here means this confidential MPT transaction left coaDelta
// ConfidentialMPTMergeInbox leave coaDelta unmodified. Therefore, if a confidential MPT // unmodified (e.g. ConfidentialMPTSend, ConfidentialMPTMergeInbox, or
// transaction reaches here, it must be one of these two types, neither of which will // ConfidentialMPTHolderKeyUpdate/ConfidentialMPTMirrorUpdate, none of which touch
// modify sfOutstandingAmount // sfConfidentialOutstandingAmount), so it must not modify sfOutstandingAmount either.
if (checks.outstandingDelta != 0) if (checks.outstandingDelta != 0)
{ {
JLOG(j.fatal()) << "Invariant failed: OutstandingAmount changed " JLOG(j.fatal()) << "Invariant failed: OutstandingAmount changed "
@@ -851,7 +894,7 @@ ValidMPTTransfer::visitEntry(
// no trace of its pseudo-account status in the post-transaction view // no trace of its pseudo-account status in the post-transaction view
// isAuthorized() sees at finalize() time. // isAuthorized() sees at finalize() time.
if (before && before->getType() == ltACCOUNT_ROOT) if (before && before->getType() == ltACCOUNT_ROOT)
pseudoAccountsBefore_[before->at(sfAccount)] = isPseudoAccount(before); pseudoAccountsBefore_[before->at(sfAccount)] = isPseudoAccountSle(before);
} }
bool bool
+1 -1
View File
@@ -740,7 +740,7 @@ ValidVault::finalize(
return false; return false;
} }
if (!isPseudoAccount(sleSharesIssuer)) if (!isPseudoAccount(AccountRootEntryR(sleSharesIssuer, view)))
{ {
JLOG(j.fatal()) // JLOG(j.fatal()) //
<< "Invariant failed: shares issuer must be a " << "Invariant failed: shares issuer must be a "
+3 -2
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -836,7 +837,7 @@ DirectStepI<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH; return temBAD_PATH;
} }
auto const sleSrc = ctx.view.read(keylet::account(src_)); auto const sleSrc = AccountRootEntryR(src_, ctx.view);
if (!sleSrc) if (!sleSrc)
{ {
JLOG(j_.warn()) << "DirectStepI: can't receive IOUs from non-existent issuer: " << src_; JLOG(j_.warn()) << "DirectStepI: can't receive IOUs from non-existent issuer: " << src_;
@@ -902,7 +903,7 @@ DirectStepI<TDerived>::check(StrandContext const& ctx) const
} }
} }
return static_cast<TDerived const*>(this)->check(ctx, sleSrc); return static_cast<TDerived const*>(this)->check(ctx, sleSrc.rawSle());
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
+3 -2
View File
@@ -6,6 +6,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -865,7 +866,7 @@ MPTEndpointStep<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH; return temBAD_PATH;
} }
auto const sleSrc = ctx.view.read(keylet::account(src_)); auto const sleSrc = AccountRootEntryR(src_, ctx.view);
if (!sleSrc) if (!sleSrc)
{ {
JLOG(j_.warn()) << "MPTEndpointStep: can't receive MPT from non-existent issuer: " << src_; JLOG(j_.warn()) << "MPTEndpointStep: can't receive MPT from non-existent issuer: " << src_;
@@ -928,7 +929,7 @@ MPTEndpointStep<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH; return temBAD_PATH;
} }
return static_cast<TDerived const*>(this)->check(ctx, sleSrc); return static_cast<TDerived const*>(this)->check(ctx, sleSrc.rawSle());
} }
template <class TDerived> template <class TDerived>
+2 -1
View File
@@ -4,6 +4,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -342,7 +343,7 @@ XRPEndpointStep<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH; return temBAD_PATH;
} }
auto sleAcc = ctx.view.read(keylet::account(acc_)); auto sleAcc = AccountRootEntryR(acc_, ctx.view);
if (!sleAcc) if (!sleAcc)
{ {
JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from " JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
@@ -9,6 +9,7 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
@@ -224,7 +225,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
AccountID const account{ctx.tx[sfAccount]}; AccountID const account{ctx.tx[sfAccount]};
AccountID const dst{ctx.tx[sfDestination]}; AccountID const dst{ctx.tx[sfDestination]};
auto sleDst = ctx.view.read(keylet::account(dst)); auto sleDst = AccountRootEntryR(dst, ctx.view);
if (!sleDst) if (!sleDst)
return tecNO_DST; return tecNO_DST;
@@ -250,7 +251,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
} }
} }
auto sleAccount = ctx.view.read(keylet::account(account)); auto sleAccount = AccountRootEntryR(account, ctx.view);
XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account"); XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account");
if (!sleAccount) if (!sleAccount)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -353,11 +354,11 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
TER TER
AccountDelete::doApply() AccountDelete::doApply()
{ {
auto src = view().peek(keylet::account(accountID_)); auto src = AccountRootEntryW(accountID_, view());
XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account"); XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account");
auto const dstID = ctx_.tx[sfDestination]; auto const dstID = ctx_.tx[sfDestination];
auto dst = view().peek(keylet::account(dstID)); auto dst = AccountRootEntryW(dstID, view());
XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account"); XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account");
if (!src || !dst) if (!src || !dst)
@@ -365,8 +366,8 @@ AccountDelete::doApply()
if (ctx_.tx.isFieldPresent(sfCredentialIDs)) if (ctx_.tx.isFieldPresent(sfCredentialIDs))
{ {
if (auto err = if (auto err = verifyDepositPreauth(
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dstID, dst, ctx_.journal); ctx_.tx, ctx_.view(), accountID_, dstID, dst.rawSle(), ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
} }
@@ -407,7 +408,7 @@ AccountDelete::doApply()
if (src->isFieldPresent(sfSponsor)) if (src->isFieldPresent(sfSponsor))
{ {
auto const sponsorID = src->getAccountID(sfSponsor); auto const sponsorID = src->getAccountID(sfSponsor);
auto sponsorSle = view().peek(keylet::account(sponsorID)); auto sponsorSle = AccountRootEntryW(sponsorID, view());
if (!sponsorSle) if (!sponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -425,7 +426,7 @@ AccountDelete::doApply()
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
} }
sponsorSle->at(sfSponsoringAccountCount) = sponsoringAccountCount - 1; sponsorSle->at(sfSponsoringAccountCount) = sponsoringAccountCount - 1;
view().update(sponsorSle); sponsorSle.update();
// Following line might look redundant, but without it, sfSponsor // Following line might look redundant, but without it, sfSponsor
// would end up remaining in after-ltAccountRoot during the // would end up remaining in after-ltAccountRoot during the
@@ -448,8 +449,8 @@ AccountDelete::doApply()
if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent)) if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
dst->clearFlag(lsfPasswordSpent); dst->clearFlag(lsfPasswordSpent);
view().update(dst); dst.update();
view().erase(src); src.erase();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -6,6 +6,7 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -170,7 +171,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
{ {
auto const id = ctx.tx[sfAccount]; auto const id = ctx.tx[sfAccount];
auto const sle = ctx.view.read(keylet::account(id)); auto const sle = AccountRootEntryR(id, ctx.view);
if (!sle) if (!sle)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -224,7 +225,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
TER TER
AccountSet::doApply() AccountSet::doApply()
{ {
auto const sle = view().peek(keylet::account(accountID_)); auto sle = AccountRootEntryW(accountID_, view());
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -582,7 +583,7 @@ AccountSet::doApply()
if (uFlagsIn != uFlagsOut) if (uFlagsIn != uFlagsOut)
sle->setFieldU32(sfFlags, uFlagsOut); sle->setFieldU32(sfFlags, uFlagsOut);
ctx_.view().update(sle); sle.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -3,6 +3,7 @@
#include <xrpl/basics/Slice.h> #include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/PublicKey.h> #include <xrpl/protocol/PublicKey.h>
@@ -25,7 +26,7 @@ SetRegularKey::calculateBaseFee(ReadView const& view, STTx const& tx)
{ {
if (calcAccountID(PublicKey(makeSlice(spk))) == id) if (calcAccountID(PublicKey(makeSlice(spk))) == id)
{ {
auto const sle = view.read(keylet::account(id)); auto const sle = AccountRootEntryR(id, view);
if (sle && !sle->isFlag(lsfPasswordSpent)) if (sle && !sle->isFlag(lsfPasswordSpent))
{ {
@@ -53,7 +54,7 @@ SetRegularKey::preflight(PreflightContext const& ctx)
TER TER
SetRegularKey::doApply() SetRegularKey::doApply()
{ {
auto const sle = view().peek(keylet::account(accountID_)); auto sle = AccountRootEntryW(accountID_, view());
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -73,7 +74,7 @@ SetRegularKey::doApply()
sle->makeFieldAbsent(sfRegularKey); sle->makeFieldAbsent(sfRegularKey);
} }
ctx_.view().update(sle); sle.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -6,6 +6,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -213,8 +214,9 @@ removeSignersFromLedger(
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
AccountRootEntryW accountSle(accountKeylet, view);
decreaseOwnerCountForObject( decreaseOwnerCountForObject(
view, view.peek(accountKeylet), signers, removeFromOwnerCount, registry.getJournal("View")); view, accountSle, signers, removeFromOwnerCount, registry.getJournal("View"));
view.erase(signers); view.erase(signers);
@@ -309,7 +311,7 @@ SignerListSet::replaceSignerList()
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_)) ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_))
return ter; return ter;
auto const sle = view().peek(accountKeylet); auto sle = AccountRootEntryW(accountKeylet, view(), ctx_.journal);
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -319,12 +321,8 @@ SignerListSet::replaceSignerList()
// We check the reserve against the starting balance because we want to // We check the reserve against the starting balance because we want to
// allow dipping into the reserve to pay fees. This behavior is consistent // allow dipping into the reserve to pay fees. This behavior is consistent
// with TicketCreate. // with TicketCreate.
if (auto const ret = checkReserve( if (auto const ret = sle.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = kAddedOwnerCount});
sle,
preFeeBalance_,
{.ownerCountDelta = kAddedOwnerCount},
ctx_.journal);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -333,7 +331,6 @@ SignerListSet::replaceSignerList()
view().insert(signerList); view().insert(signerList);
writeSignersToSLE(signerList, flags); writeSignersToSLE(signerList, flags);
auto viewJ = ctx_.registry.get().getJournal("View");
// Add the signer list to the account's directory. // Add the signer list to the account's directory.
auto const page = auto const page =
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_)); ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_));
@@ -348,7 +345,7 @@ SignerListSet::replaceSignerList()
// If we succeeded, the new entry counts against the // If we succeeded, the new entry counts against the
// creator's reserve. // creator's reserve.
increaseOwnerCount(ctx_.getApplyViewContext(), sle, kAddedOwnerCount, viewJ); sle.increaseOwnerCount(ctx_.getApplyViewContext(), kAddedOwnerCount);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), signerList); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), signerList);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -359,7 +356,7 @@ SignerListSet::destroySignerList()
auto const accountKeylet = keylet::account(accountID_); auto const accountKeylet = keylet::account(accountID_);
// Destroying the signer list is only allowed if either the master key // Destroying the signer list is only allowed if either the master key
// is enabled or there is a regular key. // is enabled or there is a regular key.
SLE::pointer const ledgerEntry = view().peek(accountKeylet); auto const ledgerEntry = AccountRootEntryW(accountKeylet, view());
if (!ledgerEntry) if (!ledgerEntry)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -10,6 +10,7 @@
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/RawView.h> #include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -125,8 +126,7 @@ checkAttestationPublicKey(
AccountID const accountFromPK = calcAccountID(pk); AccountID const accountFromPK = calcAccountID(pk);
if (auto const sleAttestationSigningAccount = if (auto const sleAttestationSigningAccount = AccountRootEntryR(attestationSignerAccount, view))
view.read(keylet::account(attestationSignerAccount)))
{ {
if (accountFromPK == attestationSignerAccount) if (accountFromPK == attestationSignerAccount)
{ {
@@ -404,7 +404,7 @@ transferHelper(
return tesSUCCESS; return tesSUCCESS;
auto const dstK = keylet::account(dst); auto const dstK = keylet::account(dst);
if (auto sleDst = psb.read(dstK)) if (auto sleDst = AccountRootEntryR(dstK, psb))
{ {
// Check dst tag and deposit auth // Check dst tag and deposit auth
@@ -430,13 +430,13 @@ transferHelper(
if (amt.native()) if (amt.native())
{ {
auto const sleSrc = psb.peek(keylet::account(src)); auto sleSrc = AccountRootEntryW(src, psb, j);
XRPL_ASSERT(sleSrc, "xrpl::transferHelper : non-null source account"); XRPL_ASSERT(sleSrc, "xrpl::transferHelper : non-null source account");
if (!sleSrc) if (!sleSrc)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
{ {
auto const reserve = accountReserve(psb, sleSrc, j); auto const reserve = sleSrc.reserve();
auto const availableBalance = [&]() -> STAmount { auto const availableBalance = [&]() -> STAmount {
STAmount curBal = (*sleSrc)[sfBalance]; STAmount curBal = (*sleSrc)[sfBalance];
@@ -455,7 +455,7 @@ transferHelper(
} }
} }
auto sleDst = psb.peek(dstK); auto sleDst = AccountRootEntryW(dstK, psb);
if (!sleDst) if (!sleDst)
{ {
if (canCreate == CanCreateDstPolicy::No) if (canCreate == CanCreateDstPolicy::No)
@@ -470,17 +470,17 @@ transferHelper(
} }
// Create the account. // Create the account.
sleDst = std::make_shared<SLE>(dstK); sleDst.newSLE();
sleDst->setAccountID(sfAccount, dst); sleDst->setAccountID(sfAccount, dst);
sleDst->setFieldU32(sfSequence, psb.seq()); sleDst->setFieldU32(sfSequence, psb.seq());
psb.insert(sleDst); sleDst.insert();
} }
(*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt; (*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt;
(*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt; (*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt;
psb.update(sleSrc); sleSrc.update();
psb.update(sleDst); sleDst.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -724,7 +724,7 @@ finalizeClaimHelper(
auto const cidOwner = (*sleClaimID)[sfAccount]; auto const cidOwner = (*sleClaimID)[sfAccount];
{ {
// Remove the claim id // Remove the claim id
auto const sleOwner = outerSb.peek(keylet::account(cidOwner)); auto sleOwner = AccountRootEntryW(cidOwner, outerSb);
auto const page = (*sleClaimID)[sfOwnerNode]; auto const page = (*sleClaimID)[sfOwnerNode];
if (!outerSb.dirRemove(keylet::ownerDir(cidOwner), page, sleClaimID->key(), true)) if (!outerSb.dirRemove(keylet::ownerDir(cidOwner), page, sleClaimID->key(), true))
{ {
@@ -759,7 +759,7 @@ getSignersListAndQuorum(ReadView const& view, SLE const& sleBridge, beast::Journ
std::uint32_t q = std::numeric_limits<std::uint32_t>::max(); std::uint32_t q = std::numeric_limits<std::uint32_t>::max();
AccountID const thisDoor = sleBridge[sfAccount]; AccountID const thisDoor = sleBridge[sfAccount];
auto const sleDoor = [&] { return view.read(keylet::account(thisDoor)); }(); auto const sleDoor = [&] { return AccountRootEntryR(thisDoor, view); }();
if (!sleDoor) if (!sleDoor)
{ {
@@ -1036,7 +1036,7 @@ applyCreateAccountAttestations(
// Check reserve // Check reserve
auto const balance = (*sleDoor)[sfBalance]; auto const balance = (*sleDoor)[sfBalance];
auto const reserve = accountReserve(psb, sleDoor, j, {.ownerCountDelta = 1}); auto const reserve = AccountRootEntryR(sleDoor, psb, j).reserve({.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return std::unexpected(tecINSUFFICIENT_RESERVE); return std::unexpected(tecINSUFFICIENT_RESERVE);
@@ -1145,7 +1145,9 @@ applyCreateAccountAttestations(
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
// Reserve was already checked // Reserve was already checked
increaseOwnerCount(psb, sleDoor, {}, 1, j); AccountRootEntryW doorSle(doorK, psb, j);
std::optional<AccountRootEntryW> noSponsor;
doorSle.increaseOwnerCount(noSponsor, 1);
psb.insert(createdSleClaimID); psb.insert(createdSleClaimID);
psb.update(sleDoor); psb.update(sleDoor);
} }
@@ -1429,7 +1431,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
if (!isXRP(bridgeSpec.issue(chainType))) if (!isXRP(bridgeSpec.issue(chainType)))
{ {
auto const sleIssuer = ctx.view.read(keylet::account(bridgeSpec.issue(chainType).account)); auto const sleIssuer = AccountRootEntryR(bridgeSpec.issue(chainType).account, ctx.view);
if (!sleIssuer) if (!sleIssuer)
return tecNO_ISSUER; return tecNO_ISSUER;
@@ -1442,12 +1444,12 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
{ {
// Check reserve // Check reserve
auto const sleAcc = ctx.view.read(keylet::account(account)); auto const sleAcc = AccountRootEntryR(account, ctx.view, ctx.j);
if (!sleAcc) if (!sleAcc)
return terNO_ACCOUNT; return terNO_ACCOUNT;
auto const balance = (*sleAcc)[sfBalance]; auto const balance = (*sleAcc)[sfBalance];
auto const reserve = accountReserve(ctx.view, sleAcc, ctx.j, {.ownerCountDelta = 1}); auto const reserve = sleAcc.reserve({.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
@@ -1464,7 +1466,7 @@ XChainCreateBridge::doApply()
auto const reward = ctx_.tx[sfSignatureReward]; auto const reward = ctx_.tx[sfSignatureReward];
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
auto const sleAcct = ctx_.view().peek(keylet::account(account)); auto sleAcct = AccountRootEntryW(account, ctx_.view(), ctx_.journal);
if (!sleAcct) if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1492,10 +1494,11 @@ XChainCreateBridge::doApply()
(*sleBridge)[sfOwnerNode] = *page; (*sleBridge)[sfOwnerNode] = *page;
} }
increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal); std::optional<AccountRootEntryW> noSponsor;
sleAcct.increaseOwnerCount(noSponsor, 1);
ctx_.view().insert(sleBridge); ctx_.view().insert(sleBridge);
ctx_.view().update(sleAcct); sleAcct.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -1575,7 +1578,7 @@ BridgeModify::doApply()
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
bool const clearAccountCreate = ctx_.tx.isFlag(tfClearAccountCreateAmount); bool const clearAccountCreate = ctx_.tx.isFlag(tfClearAccountCreateAmount);
auto const sleAcct = ctx_.view().peek(keylet::account(account)); auto const sleAcct = AccountRootEntryW(account, ctx_.view());
if (!sleAcct) if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1633,7 +1636,7 @@ XChainClaim::preclaim(PreclaimContext const& ctx)
return tecNO_ENTRY; return tecNO_ENTRY;
} }
if (!ctx.view.read(keylet::account(ctx.tx[sfDestination]))) if (!AccountRootEntryR(ctx.tx[sfDestination], ctx.view))
{ {
return tecNO_DST; return tecNO_DST;
} }
@@ -1738,7 +1741,7 @@ XChainClaim::doApply()
// `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
// views, it's important that the sle's lifetime doesn't overlap. // views, it's important that the sle's lifetime doesn't overlap.
auto const sleAcct = psb.peek(keylet::account(account)); auto const sleAcct = AccountRootEntryW(account, psb);
auto const sleBridge = peekBridge(psb, bridgeSpec); auto const sleBridge = peekBridge(psb, bridgeSpec);
auto const sleClaimID = psb.peek(claimIDKeylet); auto const sleClaimID = psb.peek(claimIDKeylet);
@@ -1919,7 +1922,7 @@ XChainCommit::doApply()
auto const amount = ctx_.tx[sfAmount]; auto const amount = ctx_.tx[sfAmount];
auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const bridgeSpec = ctx_.tx[sfXChainBridge];
auto const sleAccount = psb.read(keylet::account(account)); auto const sleAccount = AccountRootEntryR(account, psb);
if (!sleAccount) if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1990,12 +1993,12 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx)
{ {
// Check reserve // Check reserve
auto const sleAcc = ctx.view.read(keylet::account(account)); auto const sleAcc = AccountRootEntryR(account, ctx.view, ctx.j);
if (!sleAcc) if (!sleAcc)
return terNO_ACCOUNT; return terNO_ACCOUNT;
auto const balance = (*sleAcc)[sfBalance]; auto const balance = (*sleAcc)[sfBalance];
auto const reserve = accountReserve(ctx.view, sleAcc, ctx.j, {.ownerCountDelta = 1}); auto const reserve = sleAcc.reserve({.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
} }
@@ -2011,7 +2014,7 @@ XChainCreateClaimID::doApply()
auto const reward = ctx_.tx[sfSignatureReward]; auto const reward = ctx_.tx[sfSignatureReward];
auto const otherChainSrc = ctx_.tx[sfOtherChainSource]; auto const otherChainSrc = ctx_.tx[sfOtherChainSource];
auto const sleAcct = ctx_.view().peek(keylet::account(account)); auto sleAcct = AccountRootEntryW(account, ctx_.view(), ctx_.journal);
if (!sleAcct) if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -2053,11 +2056,12 @@ XChainCreateClaimID::doApply()
(*sleClaimID)[sfOwnerNode] = *page; (*sleClaimID)[sfOwnerNode] = *page;
} }
increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal); std::optional<AccountRootEntryW> noSponsor;
sleAcct.increaseOwnerCount(noSponsor, 1);
ctx_.view().insert(sleClaimID); ctx_.view().insert(sleClaimID);
ctx_.view().update(sleBridge); ctx_.view().update(sleBridge);
ctx_.view().update(sleAcct); sleAcct.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -2195,7 +2199,7 @@ XChainCreateAccountCommit::doApply()
STAmount const reward = ctx_.tx[sfSignatureReward]; STAmount const reward = ctx_.tx[sfSignatureReward];
STXChainBridge const bridge = ctx_.tx[sfXChainBridge]; STXChainBridge const bridge = ctx_.tx[sfXChainBridge];
auto const sle = psb.peek(keylet::account(account)); auto const sle = AccountRootEntryW(account, psb);
if (!sle) if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+22 -18
View File
@@ -7,6 +7,7 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -112,8 +113,8 @@ CheckCash::preclaim(PreclaimContext const& ctx)
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
{ {
auto const sleSrc = ctx.view.read(keylet::account(srcId)); auto const sleSrc = AccountRootEntryR(srcId, ctx.view);
auto const sleDst = ctx.view.read(keylet::account(dstId)); auto const sleDst = AccountRootEntryR(dstId, ctx.view);
if (!sleSrc || !sleDst) if (!sleSrc || !sleDst)
{ {
// If the check exists this should never occur. // If the check exists this should never occur.
@@ -201,7 +202,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
auto const sleTrustLine = auto const sleTrustLine =
ctx.view.read(keylet::trustLine(dstId, issuerId, currency)); ctx.view.read(keylet::trustLine(dstId, issuerId, currency));
auto const sleIssuer = ctx.view.read(keylet::account(issuerId)); auto const sleIssuer = AccountRootEntryR(issuerId, ctx.view);
if (!sleIssuer) if (!sleIssuer)
{ {
JLOG(ctx.j.warn()) << "Can't receive IOUs from " JLOG(ctx.j.warn()) << "Can't receive IOUs from "
@@ -253,7 +254,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
return tesSUCCESS; return tesSUCCESS;
}, },
[&](MPTIssue const& issue) -> TER { [&](MPTIssue const& issue) -> TER {
auto const sleIssuer = ctx.view.read(keylet::account(issuerId)); auto const sleIssuer = AccountRootEntryR(issuerId, ctx.view);
if (!sleIssuer) if (!sleIssuer)
{ {
JLOG(ctx.j.warn()) << "Can't receive MPTs from " JLOG(ctx.j.warn()) << "Can't receive MPTs from "
@@ -406,23 +407,18 @@ CheckCash::doApply()
optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)}; optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)};
auto applyViewContext = ApplyViewContext({.view = psb, .tx = ctx_.tx}); auto applyViewContext = ApplyViewContext({.view = psb, .tx = ctx_.tx});
auto const sponsorSle = getTxReserveSponsor(applyViewContext); auto sponsorSle = getTxReserveSponsor(applyViewContext);
if (!sponsorSle) if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE return sponsorSle.error(); // LCOV_EXCL_LINE
// Check reserve. Return destination account SLE if enough reserve, // Check reserve. Return destination account SLE if enough reserve,
// otherwise return nullptr. // otherwise return nullptr.
auto checkDstReserve = [&]() -> SLE::pointer { auto checkDstReserve = [&]() -> SLE::pointer {
auto sleDst = psb.peek(keylet::account(accountID_)); auto sleDst = AccountRootEntryW(accountID_, psb, j_);
// Can the account cover the trust line's or MPT reserve? // Can the account cover the trust line's or MPT reserve?
if (auto const ret = checkReserve( if (auto const ret = sleDst.checkReserve(
applyViewContext, applyViewContext, preFeeBalance_, *sponsorSle, {.ownerCountDelta = 1});
sleDst,
preFeeBalance_,
*sponsorSle,
{.ownerCountDelta = 1},
j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
JLOG(j_.trace()) << "Trust line does not exist. " JLOG(j_.trace()) << "Trust line does not exist. "
@@ -430,7 +426,7 @@ CheckCash::doApply()
return nullptr; return nullptr;
} }
return sleDst; return sleDst.mutableRawSle();
}; };
std::optional<Keylet> trustLineKey; std::optional<Keylet> trustLineKey;
@@ -486,8 +482,10 @@ CheckCash::doApply()
Issue(currency, accountID_), // limit of zero Issue(currency, accountID_), // limit of zero
0, // quality in 0, // quality in
0, // quality out 0, // quality out
*sponsorSle, // sponsor sponsorSle->has_value() // sponsor
viewJ); // journal ? (*sponsorSle)->mutableRawSle()
: SLE::pointer{},
viewJ); // journal
!isTesSuccess(ter)) !isTesSuccess(ter))
{ {
return ter; return ter;
@@ -533,8 +531,14 @@ CheckCash::doApply()
if (sleDst == nullptr) if (sleDst == nullptr)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
if (auto const err = if (auto const err = checkCreateMPT(
checkCreateMPT(psb, mptID, accountID_, *sponsorSle, 0, j_); psb,
mptID,
accountID_,
sponsorSle->has_value() ? (*sponsorSle)->mutableRawSle()
: SLE::pointer{},
0,
j_);
!isTesSuccess(err)) !isTesSuccess(err))
{ {
return err; return err;
@@ -4,6 +4,7 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -79,7 +80,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
{ {
AccountID const dstId{ctx.tx[sfDestination]}; AccountID const dstId{ctx.tx[sfDestination]};
AccountID const srcId{ctx.tx[sfAccount]}; AccountID const srcId{ctx.tx[sfAccount]};
auto const sleDst = ctx.view.read(keylet::account(dstId)); auto const sleDst = AccountRootEntryR(dstId, ctx.view);
if (!sleDst) if (!sleDst)
{ {
JLOG(ctx.j.warn()) << "Destination account does not exist."; JLOG(ctx.j.warn()) << "Destination account does not exist.";
@@ -187,15 +188,15 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
TER TER
CheckCreate::doApply() CheckCreate::doApply()
{ {
auto const sle = view().peek(keylet::account(accountID_)); auto sle = AccountRootEntryW(accountID_, view(), ctx_.journal);
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// A check counts against the reserve of the issuing account, but we // A check counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the // check the starting balance because we want to allow dipping into the
// reserve to pay fees. // reserve to pay fees.
if (auto const ret = checkReserve( if (auto const ret =
ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, ctx_.journal); sle.checkReserve(ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
// Note that we use the value from the sequence or ticket as the // Note that we use the value from the sequence or ticket as the
@@ -220,7 +221,6 @@ CheckCreate::doApply()
view().insert(sleCheck); view().insert(sleCheck);
auto viewJ = ctx_.registry.get().getJournal("View");
// If it's not a self-send (and it shouldn't be), add Check to the // If it's not a self-send (and it shouldn't be), add Check to the
// destination's owner directory. // destination's owner directory.
if (dstAccountId != accountID_) if (dstAccountId != accountID_)
@@ -251,7 +251,7 @@ CheckCreate::doApply()
} }
// If we succeeded, the new entry counts against the creator's reserve. // If we succeeded, the new entry counts against the creator's reserve.
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, viewJ); sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -2,6 +2,7 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -87,8 +88,8 @@ CredentialAccept::doApply()
AccountID const issuer{ctx_.tx[sfIssuer]}; AccountID const issuer{ctx_.tx[sfIssuer]};
// Both exist as credential object exist itself (checked in preclaim) // Both exist as credential object exist itself (checked in preclaim)
auto const sleSubject = view().peek(keylet::account(accountID_)); auto sleSubject = AccountRootEntryW(accountID_, view(), j_);
auto const sleIssuer = view().peek(keylet::account(issuer)); auto sleIssuer = AccountRootEntryW(issuer, view());
if (!sleSubject || !sleIssuer) if (!sleSubject || !sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -97,13 +98,8 @@ CredentialAccept::doApply()
if (!txSponsorSle) if (!txSponsorSle)
return txSponsorSle.error(); // LCOV_EXCL_LINE return txSponsorSle.error(); // LCOV_EXCL_LINE
if (auto const ret = checkReserve( if (auto const ret = sleSubject.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(), preFeeBalance_, *txSponsorSle, {.ownerCountDelta = 1});
sleSubject,
preFeeBalance_,
*txSponsorSle,
{.ownerCountDelta = 1},
j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -130,7 +126,7 @@ CredentialAccept::doApply()
removeSponsorFromLedgerEntry(sleCred); removeSponsorFromLedgerEntry(sleCred);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
increaseOwnerCount(ctx_.getApplyViewContext(), sleSubject, 1, j_); sleSubject.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
view().update(sleCred); view().update(sleCred);
return tesSUCCESS; return tesSUCCESS;
@@ -4,6 +4,7 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> // IWYU pragma: keep #include <xrpl/ledger/helpers/CredentialHelpers.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -84,7 +85,7 @@ CredentialCreate::preclaim(PreclaimContext const& ctx)
auto const credType(ctx.tx[sfCredentialType]); auto const credType(ctx.tx[sfCredentialType]);
auto const subject = ctx.tx[sfSubject]; auto const subject = ctx.tx[sfSubject];
auto const subjectSle = ctx.view.read(keylet::account(subject)); auto const subjectSle = AccountRootEntryR(subject, ctx.view);
if (!subjectSle) if (!subjectSle)
{ {
@@ -134,12 +135,12 @@ CredentialCreate::doApply()
sleCred->setFieldU32(sfExpiration, *optExp); sleCred->setFieldU32(sfExpiration, *optExp);
} }
auto const sleIssuer = view().peek(keylet::account(accountID_)); auto sleIssuer = AccountRootEntryW(accountID_, view(), j_);
if (!sleIssuer) if (!sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ret = checkReserve( if (auto const ret = sleIssuer.checkReserve(
ctx_.getApplyViewContext(), sleIssuer, preFeeBalance_, {.ownerCountDelta = 1}, j_); ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -159,7 +160,7 @@ CredentialCreate::doApply()
return tecDIR_FULL; return tecDIR_FULL;
sleCred->setFieldU64(sfIssuerNode, *page); sleCred->setFieldU64(sfIssuerNode, *page);
increaseOwnerCount(ctx_.getApplyViewContext(), sleIssuer, 1, j_); sleIssuer.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
} }
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -52,7 +53,7 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount]))) if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount])))
return terNO_ACCOUNT; // LCOV_EXCL_LINE return terNO_ACCOUNT; // LCOV_EXCL_LINE
auto const sleAuthorize = ctx.view.read(keylet::account(ctx.tx[sfAuthorize])); auto const sleAuthorize = AccountRootEntryR(ctx.tx[sfAuthorize], ctx.view);
if (!sleAuthorize) if (!sleAuthorize)
return tecNO_TARGET; return tecNO_TARGET;
@@ -72,7 +73,7 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
TER TER
DelegateSet::doApply() DelegateSet::doApply()
{ {
auto const sleOwner = ctx_.view().peek(keylet::account(accountID_)); auto sleOwner = AccountRootEntryW(accountID_, ctx_.view(), ctx_.journal);
if (!sleOwner) if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -98,12 +99,8 @@ DelegateSet::doApply()
if (permissions.empty()) if (permissions.empty())
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
if (auto const ret = checkReserve( if (auto const ret = sleOwner.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
sleOwner,
preFeeBalance_,
{.ownerCountDelta = 1},
ctx_.journal);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -133,7 +130,7 @@ DelegateSet::doApply()
(*sle)[sfDestinationNode] = *destPage; (*sle)[sfDestinationNode] = *destPage;
ctx_.view().insert(sle); ctx_.view().insert(sle);
increaseOwnerCount(ctx_.getApplyViewContext(), sleOwner, 1, ctx_.journal); sleOwner.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sle); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sle);
return tesSUCCESS; return tesSUCCESS;
@@ -170,7 +167,7 @@ DelegateSet::deleteDelegate(ApplyView& view, SLE::Ref sle, beast::Journal j)
} }
// Only the delegating account's owner count was incremented on creation // Only the delegating account's owner count was incremented on creation
auto const sleOwner = view.peek(keylet::account(delegator)); auto sleOwner = AccountRootEntryW(delegator, view);
if (!sleOwner) if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+3 -2
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h> #include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -119,7 +120,7 @@ AMMBid::preclaim(PreclaimContext const& ctx)
{ {
for (auto const& account : ctx.tx.getFieldArray(sfAuthAccounts)) for (auto const& account : ctx.tx.getFieldArray(sfAuthAccounts))
{ {
if (!ctx.view.read(keylet::account(account[sfAccount]))) if (!AccountRootEntryR(account[sfAccount], ctx.view))
{ {
JLOG(ctx.j.debug()) << "AMM Bid: Invalid Account."; JLOG(ctx.j.debug()) << "AMM Bid: Invalid Account.";
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -208,7 +209,7 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa
auto validOwner = [&](AccountID const& account) { auto validOwner = [&](AccountID const& account) {
// Valid range is 0-19 but the tailing slot pays MinSlotPrice // Valid range is 0-19 but the tailing slot pays MinSlotPrice
// and doesn't refund so the check is < instead of <= to optimize. // and doesn't refund so the check is < instead of <= to optimize.
return timeSlot && *timeSlot < kTailingSlot && sb.read(keylet::account(account)); return timeSlot && *timeSlot < kTailingSlot && AccountRootEntryR(account, sb);
}; };
auto updateSlot = [&](std::uint32_t fee, Number const& minPrice, Number const& burn) -> TER { auto updateSlot = [&](std::uint32_t fee, Number const& minPrice, Number const& burn) -> TER {
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h> #include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -102,11 +103,11 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
{ {
auto const asset = ctx.tx[sfAsset]; auto const asset = ctx.tx[sfAsset];
auto const asset2 = ctx.tx[sfAsset2]; auto const asset2 = ctx.tx[sfAsset2];
auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount])); auto const sleIssuer = AccountRootEntryR(ctx.tx[sfAccount], ctx.view);
if (!sleIssuer) if (!sleIssuer)
return terNO_ACCOUNT; // LCOV_EXCL_LINE return terNO_ACCOUNT; // LCOV_EXCL_LINE
if (!ctx.view.read(keylet::account(ctx.tx[sfHolder]))) if (!AccountRootEntryR(ctx.tx[sfHolder], ctx.view))
return terNO_ACCOUNT; return terNO_ACCOUNT;
auto const ammSle = ctx.view.read(keylet::amm(asset, asset2)); auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
@@ -178,7 +179,7 @@ AMMClawback::applyGuts(Sandbox& sb)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const ammAccount = (*ammSle)[sfAccount]; auto const ammAccount = (*ammSle)[sfAccount];
auto const accountSle = sb.read(keylet::account(ammAccount)); auto const accountSle = AccountRootEntryR(ammAccount, sb);
if (!accountSle) if (!accountSle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+4 -3
View File
@@ -5,6 +5,7 @@
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h> #include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -133,7 +134,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
if (asset.holds<MPTIssue>() || isXRP(asset)) if (asset.holds<MPTIssue>() || isXRP(asset))
return false; return false;
if (auto const issuerAccount = view.read(keylet::account(asset.getIssuer()))) if (auto const issuerAccount = AccountRootEntryR(asset.getIssuer(), view))
return !issuerAccount->isFlag(lsfDefaultRipple); return !issuerAccount->isFlag(lsfDefaultRipple);
return false; return false;
@@ -173,7 +174,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
} }
auto isLPToken = [&](STAmount const& amount) -> bool { auto isLPToken = [&](STAmount const& amount) -> bool {
if (auto const sle = ctx.view.read(keylet::account(amount.asset().getIssuer()))) if (auto const sle = AccountRootEntryR(amount.asset().getIssuer(), ctx.view))
return sle->isFieldPresent(sfAMMID); return sle->isFieldPresent(sfAMMID);
return false; return false;
}; };
@@ -237,7 +238,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
[&](Issue const& issue) -> TER { [&](Issue const& issue) -> TER {
if (isXRP(issue)) if (isXRP(issue))
return tesSUCCESS; return tesSUCCESS;
auto const sle = ctx.view.read(keylet::account(issue.account)); auto const sle = AccountRootEntryR(issue.account, ctx.view);
if (!sle) if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
if (sle->isFlag(lsfAllowTrustLineClawback)) if (sle->isFlag(lsfAllowTrustLineClawback))
@@ -6,8 +6,8 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h> #include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -344,7 +344,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
if (!ammSle) if (!ammSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const ammAccountID = (*ammSle)[sfAccount]; auto const ammAccountID = (*ammSle)[sfAccount];
auto const accountSle = sb.read(keylet::account(ammAccountID)); auto const accountSle = AccountRootEntryR(ammAccountID, sb);
if (!accountSle) if (!accountSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal); auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
@@ -691,16 +691,15 @@ AMMWithdraw::withdraw(
reserveHandling == ReserveHandling::IgnoreReserve) reserveHandling == ReserveHandling::IgnoreReserve)
return tesSUCCESS; return tesSUCCESS;
auto sleAccount = view.peek(keylet::account(account)); auto sleAccount = AccountRootEntryW(account, view, journal);
if (!sleAccount) if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const balance = (*sleAccount)[sfBalance]->xrp(); auto const balance = (*sleAccount)[sfBalance]->xrp();
// See also TrustSet::doApply() and MPTokenAuthorize::authorize() // See also TrustSet::doApply() and MPTokenAuthorize::authorize()
XRPAmount const reserve( XRPAmount const reserve(
(ownerCount(sleAccount, journal) < 2) (sleAccount.ownerCount() < 2) ? XRPAmount(beast::kZero)
? XRPAmount(beast::kZero) : sleAccount.reserve({.ownerCountDelta = 1}));
: accountReserve(view, sleAccount, journal, {.ownerCountDelta = 1}));
auto const balanceAdj = std::max(priorBalance, balance); auto const balanceAdj = std::max(priorBalance, balance);
if (balanceAdj < reserve) if (balanceAdj < reserve)
@@ -1,6 +1,8 @@
#include <xrpl/tx/transactors/dex/OfferCancel.h> #include <xrpl/tx/transactors/dex/OfferCancel.h>
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/OfferHelpers.h> #include <xrpl/ledger/helpers/OfferHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
@@ -33,7 +35,7 @@ OfferCancel::preclaim(PreclaimContext const& ctx)
auto const id = ctx.tx[sfAccount]; auto const id = ctx.tx[sfAccount];
auto const offerSequence = ctx.tx[sfOfferSequence]; auto const offerSequence = ctx.tx[sfOfferSequence];
auto const sle = ctx.view.read(keylet::account(id)); auto const sle = AccountRootEntryR(id, ctx.view);
if (!sle) if (!sle)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -54,7 +56,7 @@ OfferCancel::doApply()
{ {
auto const offerSequence = ctx_.tx[sfOfferSequence]; auto const offerSequence = ctx_.tx[sfOfferSequence];
auto const sle = view().read(keylet::account(accountID_)); auto const sle = AccountRootEntryW(accountID_, view());
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -9,7 +9,7 @@
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -181,7 +181,7 @@ OfferCreate::preclaim(PreclaimContext const& ctx)
auto const cancelSequence = ctx.tx[~sfOfferSequence]; auto const cancelSequence = ctx.tx[~sfOfferSequence];
auto const sleCreator = ctx.view.read(keylet::account(id)); auto const sleCreator = AccountRootEntryR(id, ctx.view);
if (!sleCreator) if (!sleCreator)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -288,7 +288,7 @@ OfferCreate::checkAcceptAsset(
// Only valid for custom currencies // Only valid for custom currencies
XRPL_ASSERT(!isXRP(asset), "xrpl::OfferCreate::checkAcceptAsset : input is not XRP"); XRPL_ASSERT(!isXRP(asset), "xrpl::OfferCreate::checkAcceptAsset : input is not XRP");
auto const issuerAccount = view.read(keylet::account(asset.getIssuer())); auto const issuerAccount = AccountRootEntryR(asset.getIssuer(), view);
if (!issuerAccount) if (!issuerAccount)
{ {
@@ -707,14 +707,14 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
// Not XRP or MPT // Not XRP or MPT
if (!saTakerPays.integral()) if (!saTakerPays.integral())
{ {
auto const sle = sb.read(keylet::account(uPaysIssuerID)); auto const sle = AccountRootEntryR(uPaysIssuerID, sb);
if (sle && sle->isFieldPresent(sfTickSize)) if (sle && sle->isFieldPresent(sfTickSize))
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]); uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
} }
// Not XRP or MPT // Not XRP or MPT
if (!saTakerGets.integral()) if (!saTakerGets.integral())
{ {
auto const sle = sb.read(keylet::account(uGetsIssuerID)); auto const sle = AccountRootEntryR(uGetsIssuerID, sb);
if (sle && sle->isFieldPresent(sfTickSize)) if (sle && sle->isFieldPresent(sfTickSize))
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]); uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
} }
@@ -884,12 +884,12 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
return {tesSUCCESS, true}; return {tesSUCCESS, true};
} }
auto const sleCreator = sb.peek(keylet::account(accountID_)); auto sleCreator = AccountRootEntryW(accountID_, sb, viewJ);
if (!sleCreator) if (!sleCreator)
return {tefINTERNAL, false}; return {tefINTERNAL, false};
{ {
XRPAmount const reserve = accountReserve(sb, sleCreator, viewJ, {.ownerCountDelta = 1}); XRPAmount const reserve = sleCreator.reserve({.ownerCountDelta = 1});
if (preFeeBalance_ < reserve) if (preFeeBalance_ < reserve)
{ {
// If we are here, the signing account had an insufficient reserve // If we are here, the signing account had an insufficient reserve
@@ -923,7 +923,8 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
} }
// Update owner count. // Update owner count.
increaseOwnerCount(sb, sleCreator, {}, 1, viewJ); std::optional<AccountRootEntryW> noSponsor;
sleCreator.increaseOwnerCount(noSponsor, 1);
JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.asset()) << " : " JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.asset()) << " : "
<< to_string(saTakerGets.asset()) << to_string(saTakerGets.asset())
+2 -1
View File
@@ -4,6 +4,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -46,7 +47,7 @@ DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, b
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
auto const sleOwner = view.peek(keylet::account(owner)); auto sleOwner = AccountRootEntryW(owner, view);
if (!sleOwner) if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+7 -6
View File
@@ -3,7 +3,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -20,6 +20,7 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <optional>
namespace xrpl { namespace xrpl {
@@ -65,15 +66,14 @@ DIDSet::preflight(PreflightContext const& ctx)
static TER static TER
addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner) addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner)
{ {
auto const sleAccount = ctx.view().peek(keylet::account(owner)); auto sleAccount = AccountRootEntryW(owner, ctx.view(), ctx.journal);
if (!sleAccount) if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Check reserve availability for new object creation // Check reserve availability for new object creation
{ {
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
auto const reserve = auto const reserve = sleAccount.reserve({.ownerCountDelta = 1});
accountReserve(ctx.view(), sleAccount, ctx.journal, {.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
@@ -90,8 +90,9 @@ addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner)
return tecDIR_FULL; // LCOV_EXCL_LINE return tecDIR_FULL; // LCOV_EXCL_LINE
(*sle)[sfOwnerNode] = *page; (*sle)[sfOwnerNode] = *page;
} }
increaseOwnerCount(ctx.view(), sleAccount, {}, 1, ctx.journal); std::optional<AccountRootEntryW> noSponsor;
ctx.view().update(sleAccount); sleAccount.increaseOwnerCount(noSponsor, 1);
sleAccount.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h> #include <xrpl/ledger/helpers/EscrowHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -166,7 +167,7 @@ EscrowCancel::doApply()
} }
} }
auto const sle = ctx_.view().peek(keylet::account(account)); auto sle = AccountRootEntryW(account, ctx_.view());
STAmount const amount = slep->getFieldAmount(sfAmount); STAmount const amount = slep->getFieldAmount(sfAmount);
// The return can re-create a holding the owner deleted while the escrow // The return can re-create a holding the owner deleted while the escrow
@@ -192,7 +193,7 @@ EscrowCancel::doApply()
return escrowUnlockApplyHelper<T>( return escrowUnlockApplyHelper<T>(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
kParityRate, kParityRate,
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : slep, ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle.mutableRawSle() : slep,
preFeeBalance_, preFeeBalance_,
amount, amount,
issuer, issuer,
@@ -7,6 +7,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -207,7 +208,7 @@ escrowCreatePreclaimHelper<Issue>(
return tecNO_PERMISSION; return tecNO_PERMISSION;
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION // If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
auto const sleIssuer = ctx.view.read(keylet::account(issuer)); auto const sleIssuer = AccountRootEntryR(issuer, ctx.view);
if (!sleIssuer) if (!sleIssuer)
return tecNO_ISSUER; return tecNO_ISSUER;
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking)) if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
@@ -347,7 +348,7 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
AccountID const account{ctx.tx[sfAccount]}; AccountID const account{ctx.tx[sfAccount]};
AccountID const dest{ctx.tx[sfDestination]}; AccountID const dest{ctx.tx[sfDestination]};
auto const sled = ctx.view.read(keylet::account(dest)); auto const sled = AccountRootEntryR(dest, ctx.view);
if (!sled) if (!sled)
return tecNO_DST; return tecNO_DST;
@@ -433,7 +434,7 @@ EscrowCreate::doApply()
if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter])) if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter]))
return tecNO_PERMISSION; return tecNO_PERMISSION;
auto const sle = ctx_.view().peek(keylet::account(accountID_)); auto sle = AccountRootEntryW(accountID_, ctx_.view(), j_);
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -447,7 +448,7 @@ EscrowCreate::doApply()
// unsponsored this hits the source branch and validates the // unsponsored this hits the source branch and validates the
// source's pre-lock balance against base + (currentOC+1)*increment. // source's pre-lock balance against base + (currentOC+1)*increment.
if (auto const ret = if (auto const ret =
checkReserve(ctx_.getApplyViewContext(), sle, balance, {.ownerCountDelta = 1}, j_); sle.checkReserve(ctx_.getApplyViewContext(), balance, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -464,15 +465,15 @@ EscrowCreate::doApply()
// - sponsored: 0 — sponsor covers the new owner increment, so the // - sponsored: 0 — sponsor covers the new owner increment, so the
// source only owes reserve for its current owners. // source only owes reserve for its current owners.
// - unsponsored: 1 — source owes reserve including the new increment. // - unsponsored: 1 — source owes reserve including the new increment.
auto const sourceReserve = accountReserve( auto const sourceReserve =
ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1}); sle.reserve({.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
if (balance - STAmount(amount).xrp() < sourceReserve) if (balance - STAmount(amount).xrp() < sourceReserve)
return tecUNFUNDED; return tecUNFUNDED;
} }
// Check destination account // Check destination account
{ {
auto const sled = ctx_.view().read(keylet::account(ctx_.tx[sfDestination])); auto const sled = AccountRootEntryW(ctx_.tx[sfDestination], ctx_.view());
if (!sled) if (!sled)
return tecNO_DST; // LCOV_EXCL_LINE return tecNO_DST; // LCOV_EXCL_LINE
if (sled->isFlag(lsfRequireDestTag) && !ctx_.tx[~sfDestinationTag]) if (sled->isFlag(lsfRequireDestTag) && !ctx_.tx[~sfDestinationTag])
@@ -558,9 +559,9 @@ EscrowCreate::doApply()
} }
// increment owner count // increment owner count
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal); sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
ctx_.view().update(sle); sle.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -9,6 +9,7 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h> #include <xrpl/ledger/helpers/EscrowHelpers.h>
@@ -314,12 +315,12 @@ EscrowFinish::doApply()
// NOTE: Escrow payments cannot be used to fund accounts. // NOTE: Escrow payments cannot be used to fund accounts.
AccountID const destID = (*slep)[sfDestination]; AccountID const destID = (*slep)[sfDestination];
auto const sled = ctx_.view().peek(keylet::account(destID)); auto sled = AccountRootEntryW(destID, ctx_.view());
if (!sled) if (!sled)
return tecNO_DST; return tecNO_DST;
if (auto err = if (auto err = verifyDepositPreauth(
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal); ctx_.tx, ctx_.view(), accountID_, destID, sled.rawSle(), ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
@@ -378,7 +379,7 @@ EscrowFinish::doApply()
return escrowUnlockApplyHelper<T>( return escrowUnlockApplyHelper<T>(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
lockedRate, lockedRate,
sled, sled.mutableRawSle(),
preFeeBalance_, preFeeBalance_,
amount, amount,
issuer, issuer,
@@ -404,7 +405,7 @@ EscrowFinish::doApply()
} }
} }
ctx_.view().update(sled); sled.update();
if (!recycleReserve) if (!recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal); decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
@@ -6,6 +6,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -108,7 +109,7 @@ determineBrokerID(ReadView const& view, STTx const& tx)
// Thus, Amount.issuer _should_ be the loan broker's // Thus, Amount.issuer _should_ be the loan broker's
// pseudo-account, but we don't know yet whether it is. // pseudo-account, but we don't know yet whether it is.
auto const maybePseudo = dstAmount->getIssuer(); auto const maybePseudo = dstAmount->getIssuer();
auto const sle = view.read(keylet::account(maybePseudo)); auto const sle = AccountRootEntryR(maybePseudo, view);
// If the account was not found, the transaction can't go further. // If the account was not found, the transaction can't go further.
if (!sle) if (!sle)
@@ -319,7 +320,7 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
return tecINTERNAL; // tecINSUFFICIENT_FUNDS; LCOV_EXCL_LINE return tecINTERNAL; // tecINSUFFICIENT_FUNDS; LCOV_EXCL_LINE
// Check if the vault asset issuer has the correct flags // Check if the vault asset issuer has the correct flags
auto const sleIssuer = ctx.view.read(keylet::account(vaultAsset.getIssuer())); auto const sleIssuer = AccountRootEntryR(vaultAsset.getIssuer(), ctx.view);
if (!sleIssuer) if (!sleIssuer)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h> #include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -161,7 +162,7 @@ LoanBrokerDelete::doApply()
if (auto ter = removeEmptyHolding(ctx_.getApplyViewContext(), brokerPseudoID, vaultAsset, j_)) if (auto ter = removeEmptyHolding(ctx_.getApplyViewContext(), brokerPseudoID, vaultAsset, j_))
return ter; return ter;
auto brokerPseudoSLE = view().peek(keylet::account(brokerPseudoID)); auto brokerPseudoSLE = AccountRootEntryW(brokerPseudoID, view());
if (!brokerPseudoSLE) if (!brokerPseudoSLE)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -183,10 +184,10 @@ LoanBrokerDelete::doApply()
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
} }
view().erase(brokerPseudoSLE); brokerPseudoSLE.erase();
{ {
auto owner = view().peek(keylet::account(accountID_)); auto owner = AccountRootEntryW(accountID_, view());
if (!owner) if (!owner)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -23,6 +24,7 @@
#include <xrpl/tx/Transactor.h> #include <xrpl/tx/Transactor.h>
#include <memory> #include <memory>
#include <optional>
#include <vector> #include <vector>
namespace xrpl { namespace xrpl {
@@ -236,7 +238,7 @@ LoanBrokerSet::doApply()
auto const vaultAsset = sleVault->at(sfAsset); auto const vaultAsset = sleVault->at(sfAsset);
auto const sequence = tx.getSeqProxy(); auto const sequence = tx.getSeqProxy();
auto owner = view.peek(keylet::account(accountID_)); auto owner = AccountRootEntryW(accountID_, view, j_);
if (!owner) if (!owner)
{ {
// This should be impossible // This should be impossible
@@ -254,8 +256,9 @@ LoanBrokerSet::doApply()
// Increases the owner count by two: one for the LoanBroker object, and // Increases the owner count by two: one for the LoanBroker object, and
// one for the pseudo-account. // one for the pseudo-account.
increaseOwnerCount(view, owner, {}, 2, j_); std::optional<AccountRootEntryW> noSponsor;
if (preFeeBalance_ < accountReserve(view, owner, j_)) owner.increaseOwnerCount(noSponsor, 2);
if (preFeeBalance_ < owner.reserve())
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID); auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID);
@@ -4,6 +4,7 @@
#include <xrpl/basics/Number.h> // IWYU pragma: keep #include <xrpl/basics/Number.h> // IWYU pragma: keep
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -80,7 +81,7 @@ LoanDelete::doApply()
if (!loanSle) if (!loanSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const borrower = loanSle->at(sfBorrower); auto const borrower = loanSle->at(sfBorrower);
auto const borrowerSle = view.peek(keylet::account(borrower)); auto borrowerSle = AccountRootEntryW(borrower, view);
if (!borrowerSle) if (!borrowerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/to_string.h> #include <xrpl/json/to_string.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -56,7 +57,7 @@ conservationBalance(ReadView const& view, AccountID const& id, Asset const& asse
{ {
if (isXRP(asset)) if (isXRP(asset))
{ {
auto const sle = view.read(keylet::account(id)); auto const sle = AccountRootEntryR(id, view);
if (!sle) if (!sle)
return STAmount{asset}; // LCOV_EXCL_LINE return STAmount{asset}; // LCOV_EXCL_LINE
return view.balanceHookIOU(id, xrpAccount(), sle->getFieldAmount(sfBalance)); return view.balanceHookIOU(id, xrpAccount(), sle->getFieldAmount(sfBalance));
@@ -391,7 +392,7 @@ LoanPay::doApply()
}(); }();
auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount; auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount;
auto const brokerPayeeSle = view.peek(keylet::account(brokerPayee)); auto const brokerPayeeSle = AccountRootEntryW(brokerPayee, view);
if (!sendBrokerFeeToOwner) if (!sendBrokerFeeToOwner)
{ {
// If we can't send the fee to the owner, and the pseudo-account is // If we can't send the fee to the owner, and the pseudo-account is
@@ -7,6 +7,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -305,7 +306,7 @@ LoanSet::preclaim(PreclaimContext const& ctx)
auto const brokerPseudo = brokerSle->at(sfAccount); auto const brokerPseudo = brokerSle->at(sfAccount);
auto const borrower = counterparty == brokerOwner ? account : counterparty; auto const borrower = counterparty == brokerOwner ? account : counterparty;
if (auto const borrowerSle = ctx.view.read(keylet::account(borrower)); !borrowerSle) if (auto const borrowerSle = AccountRootEntryR(borrower, ctx.view); !borrowerSle)
{ {
// It may not be possible to hit this case, because it'll fail the // It may not be possible to hit this case, because it'll fail the
// signature check with terNO_ACCOUNT. // signature check with terNO_ACCOUNT.
@@ -443,7 +444,7 @@ LoanSet::doApply()
if (!brokerSle) if (!brokerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const brokerOwner = brokerSle->at(sfOwner); auto const brokerOwner = brokerSle->at(sfOwner);
auto const brokerOwnerSle = view.peek(keylet::account(brokerOwner)); auto const brokerOwnerSle = AccountRootEntryW(brokerOwner, view);
if (!brokerOwnerSle) if (!brokerOwnerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -455,14 +456,14 @@ LoanSet::doApply()
auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner); auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner);
auto const borrower = counterparty == brokerOwner ? accountID_ : counterparty; auto const borrower = counterparty == brokerOwner ? accountID_ : counterparty;
auto const borrowerSle = view.peek(keylet::account(borrower)); auto borrowerSle = AccountRootEntryW(borrower, view, j_);
if (!borrowerSle) if (!borrowerSle)
{ {
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
} }
auto const brokerPseudo = brokerSle->at(sfAccount); auto const brokerPseudo = brokerSle->at(sfAccount);
auto const brokerPseudoSle = view.peek(keylet::account(brokerPseudo)); auto const brokerPseudoSle = AccountRootEntryW(brokerPseudo, view);
if (!brokerPseudoSle) if (!brokerPseudoSle)
{ {
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -581,12 +582,13 @@ LoanSet::doApply()
} }
} }
increaseOwnerCount(view, borrowerSle, {}, 1, j_); std::optional<AccountRootEntryW> noSponsor;
borrowerSle.increaseOwnerCount(noSponsor, 1);
{ {
auto const balance = auto const balance =
accountID_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp(); accountID_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp();
if (balance < accountReserve(view, borrowerSle, j_)) if (balance < borrowerSle.reserve())
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
} }
@@ -5,7 +5,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/Asset.h> #include <xrpl/protocol/Asset.h>
@@ -376,7 +376,7 @@ NFTokenAcceptOffer::transferNFToken(
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
auto const sleBuyer = view().read(keylet::account(buyer)); auto const sleBuyer = AccountRootEntryW(buyer, view(), j_);
if (!sleBuyer) if (!sleBuyer)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -397,7 +397,7 @@ NFTokenAcceptOffer::transferNFToken(
auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
if (buyerOwnerCountAfter > buyerOwnerCountBefore) if (buyerOwnerCountAfter > buyerOwnerCountBefore)
{ {
if (buyerBalance < accountReserve(view(), sleBuyer, j_)) if (buyerBalance < sleBuyer.reserve())
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
} }
@@ -1,6 +1,8 @@
#include <xrpl/tx/transactors/nft/NFTokenBurn.h> #include <xrpl/tx/transactors/nft/NFTokenBurn.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
@@ -43,7 +45,7 @@ NFTokenBurn::preclaim(PreclaimContext const& ctx)
if (auto const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account) if (auto const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account)
{ {
if (auto const sle = ctx.view.read(keylet::account(issuer)); sle) if (auto const sle = AccountRootEntryR(issuer, ctx.view); sle)
{ {
if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account) if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account)
return tecNO_PERMISSION; return tecNO_PERMISSION;
@@ -68,10 +70,10 @@ NFTokenBurn::doApply()
if (!isTesSuccess(ret)) if (!isTesSuccess(ret))
return ret; return ret;
if (auto issuer = view().peek(keylet::account(nft::getIssuer(ctx_.tx[sfNFTokenID])))) if (auto issuer = AccountRootEntryW(nft::getIssuer(ctx_.tx[sfNFTokenID]), view()))
{ {
(*issuer)[~sfBurnedNFTokens] = (*issuer)[~sfBurnedNFTokens].valueOr(0) + 1; (*issuer)[~sfBurnedNFTokens] = (*issuer)[~sfBurnedNFTokens].valueOr(0) + 1;
view().update(issuer); issuer.update();
} }
// Delete up to 500 offers in total. // Delete up to 500 offers in total.
@@ -2,12 +2,12 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/InnerObjectFormats.h> #include <xrpl/protocol/InnerObjectFormats.h>
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
@@ -186,7 +186,7 @@ NFTokenMint::preclaim(PreclaimContext const& ctx)
// transaction. Check that and verify that this is allowed: // transaction. Check that and verify that this is allowed:
if (auto issuer = ctx.tx[~sfIssuer]) if (auto issuer = ctx.tx[~sfIssuer])
{ {
auto const sle = ctx.view.read(keylet::account(*issuer)); auto const sle = AccountRootEntryR(*issuer, ctx.view);
if (!sle) if (!sle)
return tecNO_ISSUER; return tecNO_ISSUER;
@@ -225,8 +225,8 @@ NFTokenMint::doApply()
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_); auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
auto const tokenSeq = [this, &issuer]() -> std::expected<std::uint32_t, TER> { auto const tokenSeq = [this, &issuer]() -> std::expected<std::uint32_t, TER> {
auto const root = view().peek(keylet::account(issuer)); auto root = AccountRootEntryW(issuer, view());
if (root == nullptr) if (!root)
{ {
// Should not happen. Checked in preclaim. // Should not happen. Checked in preclaim.
return std::unexpected(tecNO_ISSUER); return std::unexpected(tecNO_ISSUER);
@@ -271,7 +271,7 @@ NFTokenMint::doApply()
if (tokenSeq + 1u == 0u || tokenSeq < offset) if (tokenSeq + 1u == 0u || tokenSeq < offset)
return std::unexpected(tecMAX_SEQUENCE_REACHED); return std::unexpected(tecMAX_SEQUENCE_REACHED);
ctx_.view().update(root); root.update();
return tokenSeq; return tokenSeq;
}(); }();
@@ -279,7 +279,7 @@ NFTokenMint::doApply()
return (tokenSeq.error()); return (tokenSeq.error());
std::uint32_t const ownerCountBefore = std::uint32_t const ownerCountBefore =
view().read(keylet::account(accountID_))->getFieldU32(sfOwnerCount); AccountRootEntryW(accountID_, view())->getFieldU32(sfOwnerCount);
// Assemble the new NFToken. // Assemble the new NFToken.
SOTemplate const* nfTokenTemplate = SOTemplate const* nfTokenTemplate =
@@ -332,14 +332,14 @@ NFTokenMint::doApply()
// allows NFTs to be added to the page (and burn fees) without // allows NFTs to be added to the page (and burn fees) without
// requiring the reserve to be met each time. The reserve is // requiring the reserve to be met each time. The reserve is
// only managed when a new NFT page or sell offer is added. // only managed when a new NFT page or sell offer is added.
auto const sleAccount = view().read(keylet::account(accountID_)); auto const sleAccount = AccountRootEntryW(accountID_, view(), j_);
if (!sleAccount) if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ownerCountAfter = sleAccount->getFieldU32(sfOwnerCount); if (auto const ownerCountAfter = sleAccount->getFieldU32(sfOwnerCount);
ownerCountAfter > ownerCountBefore) ownerCountAfter > ownerCountBefore)
{ {
if (preFeeBalance_ < accountReserve(view(), sleAccount, j_)) if (preFeeBalance_ < sleAccount.reserve())
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
} }
return tesSUCCESS; return tesSUCCESS;
@@ -2,9 +2,10 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
@@ -47,7 +48,7 @@ NFTokenModify::preclaim(PreclaimContext const& ctx)
// Verify permissions for the issuer // Verify permissions for the issuer
if (AccountID const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account) if (AccountID const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account)
{ {
auto const sle = ctx.view.read(keylet::account(issuer)); auto const sle = AccountRootEntryR(issuer, ctx.view);
if (!sle) if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account) if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account)
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h> #include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -67,7 +68,7 @@ OracleDelete::deleteOracle(
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
auto const sleOwner = view.peek(keylet::account(account)); auto sleOwner = AccountRootEntryW(account, view);
if (!sleOwner) if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+17 -19
View File
@@ -3,7 +3,7 @@
#include <xrpl/basics/chrono.h> #include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h> #include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -27,6 +27,7 @@
#include <cstdlib> #include <cstdlib>
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional>
#include <set> #include <set>
#include <utility> #include <utility>
@@ -64,7 +65,7 @@ OracleSet::preflight(PreflightContext const& ctx)
TER TER
OracleSet::preclaim(PreclaimContext const& ctx) OracleSet::preclaim(PreclaimContext const& ctx)
{ {
auto const sleSetter = ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount))); auto const sleSetter = AccountRootEntryR(ctx.tx.getAccountID(sfAccount), ctx.view, ctx.j);
if (!sleSetter) if (!sleSetter)
return terNO_ACCOUNT; // LCOV_EXCL_LINE return terNO_ACCOUNT; // LCOV_EXCL_LINE
@@ -172,8 +173,7 @@ OracleSet::preclaim(PreclaimContext const& ctx)
if (pairs.size() > kMaxOracleDataSeries) if (pairs.size() > kMaxOracleDataSeries)
return tecARRAY_TOO_LARGE; return tecARRAY_TOO_LARGE;
auto const reserve = auto const reserve = sleSetter.reserve({.ownerCountDelta = adjustReserve});
accountReserve(ctx.view, sleSetter, ctx.j, {.ownerCountDelta = adjustReserve});
auto const& balance = sleSetter->getFieldAmount(sfBalance); auto const& balance = sleSetter->getFieldAmount(sfBalance);
if (balance < reserve) if (balance < reserve)
@@ -187,22 +187,20 @@ adjustOracleOwnerCount(ApplyContext& ctx, int count)
{ {
XRPL_ASSERT(std::abs(count) <= 2, "xrpl::adjustOracleOwnerCount abs(counter) <= 2"); XRPL_ASSERT(std::abs(count) <= 2, "xrpl::adjustOracleOwnerCount abs(counter) <= 2");
if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) auto sleAccount = AccountRootEntryW(ctx.tx[sfAccount], ctx.view(), ctx.journal);
{ if (!sleAccount)
if (count > 0) return false; // LCOV_EXCL_LINE
{
increaseOwnerCount(
ctx.view(), sleAccount, {}, static_cast<std::uint32_t>(count), ctx.journal);
}
else if (count < 0)
{
decreaseOwnerCount(
ctx.view(), sleAccount, {}, static_cast<std::uint32_t>(-count), ctx.journal);
}
return true;
}
return false; // LCOV_EXCL_LINE std::optional<AccountRootEntryW> noSponsor;
if (count > 0)
{
sleAccount.increaseOwnerCount(noSponsor, static_cast<std::uint32_t>(count));
}
else if (count < 0)
{
sleAccount.decreaseOwnerCount(noSponsor, static_cast<std::uint32_t>(-count));
}
return true;
} }
static void static void
@@ -4,6 +4,7 @@
#include <xrpl/basics/Slice.h> #include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -103,7 +104,7 @@ DepositPreauth::preclaim(PreclaimContext const& ctx)
{ {
// Verify that the Authorize account is present in the ledger. // Verify that the Authorize account is present in the ledger.
AccountID const auth{ctx.tx[sfAuthorize]}; AccountID const auth{ctx.tx[sfAuthorize]};
auto const sleAuth = ctx.view.read(keylet::account(auth)); auto const sleAuth = AccountRootEntryR(auth, ctx.view);
if (!sleAuth) if (!sleAuth)
return tecNO_TARGET; return tecNO_TARGET;
@@ -161,15 +162,15 @@ DepositPreauth::doApply()
auto applyViewContext = ctx_.getApplyViewContext(); auto applyViewContext = ctx_.getApplyViewContext();
if (ctx_.tx.isFieldPresent(sfAuthorize)) if (ctx_.tx.isFieldPresent(sfAuthorize))
{ {
auto const sleOwner = view().peek(keylet::account(accountID_)); auto sleOwner = AccountRootEntryW(accountID_, view(), j_);
if (!sleOwner) if (!sleOwner)
return {tefINTERNAL}; return {tefINTERNAL};
// A preauth counts against the reserve of the issuing account, but we // A preauth counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the // check the starting balance because we want to allow dipping into the
// reserve to pay fees. // reserve to pay fees.
if (auto const ret = checkReserve( if (auto const ret =
applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_); sleOwner.checkReserve(applyViewContext, preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -195,7 +196,7 @@ DepositPreauth::doApply()
slePreauth->setFieldU64(sfOwnerNode, *page); slePreauth->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve. // If we succeeded, the new entry counts against the creator's reserve.
increaseOwnerCount(applyViewContext, sleOwner, 1, j_); sleOwner.increaseOwnerCount(applyViewContext, 1);
addSponsorToLedgerEntry(applyViewContext, slePreauth); addSponsorToLedgerEntry(applyViewContext, slePreauth);
} }
else if (ctx_.tx.isFieldPresent(sfUnauthorize)) else if (ctx_.tx.isFieldPresent(sfUnauthorize))
@@ -206,15 +207,15 @@ DepositPreauth::doApply()
} }
else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials)) else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
{ {
auto const sleOwner = view().peek(keylet::account(accountID_)); auto sleOwner = AccountRootEntryW(accountID_, view(), j_);
if (!sleOwner) if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// A preauth counts against the reserve of the issuing account, but we // A preauth counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the // check the starting balance because we want to allow dipping into the
// reserve to pay fees. // reserve to pay fees.
if (auto const ret = checkReserve( if (auto const ret =
applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_); sleOwner.checkReserve(applyViewContext, preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -254,7 +255,7 @@ DepositPreauth::doApply()
slePreauth->setFieldU64(sfOwnerNode, *page); slePreauth->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve. // If we succeeded, the new entry counts against the creator's reserve.
increaseOwnerCount(applyViewContext, sleOwner, 1, j_); sleOwner.increaseOwnerCount(applyViewContext, 1);
addSponsorToLedgerEntry(applyViewContext, slePreauth); addSponsorToLedgerEntry(applyViewContext, slePreauth);
} }
else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
@@ -289,7 +290,7 @@ DepositPreauth::removeFromLedger(ApplyView& view, UInt256 const& preauthIndex, b
} }
// If we succeeded, update the DepositPreauth owner's reserve. // If we succeeded, update the DepositPreauth owner's reserve.
auto const sleOwner = view.peek(keylet::account(account)); auto sleOwner = AccountRootEntryW(account, view);
if (!sleOwner) if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
+29 -13
View File
@@ -6,6 +6,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -386,7 +387,7 @@ Payment::preclaim(PreclaimContext const& ctx)
STAmount const dstAmount(ctx.tx[sfAmount]); STAmount const dstAmount(ctx.tx[sfAmount]);
auto const k = keylet::account(dstAccountID); auto const k = keylet::account(dstAccountID);
auto const sleDst = ctx.view.read(k); auto const sleDst = AccountRootEntryR(k, ctx.view);
if (!sleDst) if (!sleDst)
{ {
@@ -564,19 +565,19 @@ Payment::doApply()
// Open a ledger for editing. // Open a ledger for editing.
auto const k = keylet::account(dstAccountID); auto const k = keylet::account(dstAccountID);
SLE::pointer sleDst = view().peek(k); AccountRootEntryW sleDst(k, view());
if (!sleDst) if (!sleDst)
{ {
// Create the account. // Create the account.
sleDst = std::make_shared<SLE>(k); sleDst.newSLE();
sleDst->setAccountID(sfAccount, dstAccountID); sleDst->setAccountID(sfAccount, dstAccountID);
sleDst->setFieldU32(sfSequence, view().seq()); sleDst->setFieldU32(sfSequence, view().seq());
sleDst->setFieldAmount(sfBalance, XRPAmount(beast::kZero)); sleDst->setFieldAmount(sfBalance, XRPAmount(beast::kZero));
if (ctx_.tx.isFlag(tfSponsorCreatedAccount)) if (ctx_.tx.isFlag(tfSponsorCreatedAccount))
{ {
auto const sponsor = view().peek(keylet::account(accountID_)); auto sponsor = AccountRootEntryW(accountID_, view());
if (!sponsor) if (!sponsor)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const currentSponsoringAccountCount = auto const currentSponsoringAccountCount =
@@ -591,18 +592,18 @@ Payment::doApply()
} }
sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1); sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1);
addSponsorToLedgerEntry(sleDst, sponsor); addSponsorToLedgerEntry(sleDst.mutableRawSle(), sponsor.rawSle());
view().update(sponsor); sponsor.update();
} }
view().insert(sleDst); sleDst.insert();
} }
else else
{ {
// Tell the engine that we are intending to change the destination // Tell the engine that we are intending to change the destination
// account. The source account gets always charged a fee so it's always // account. The source account gets always charged a fee so it's always
// marked as modified. // marked as modified.
view().update(sleDst); sleDst.update();
} }
bool const mpTokensV2 = view().rules().enabled(featureMPTokensV2); bool const mpTokensV2 = view().rules().enabled(featureMPTokensV2);
@@ -621,7 +622,12 @@ Payment::doApply()
// 2. If Account is deposit preauthorized by destination. // 2. If Account is deposit preauthorized by destination.
if (auto err = verifyDepositPreauth( if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal); ctx_.tx,
ctx_.view(),
accountID_,
dstAccountID,
sleDst.mutableRawSle(),
ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
@@ -691,7 +697,12 @@ Payment::doApply()
return ter; return ter;
if (auto err = verifyDepositPreauth( if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal); ctx_.tx,
ctx_.view(),
accountID_,
dstAccountID,
sleDst.mutableRawSle(),
ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
@@ -757,13 +768,13 @@ Payment::doApply()
// Direct XRP payment. // Direct XRP payment.
auto const sleSrc = view().peek(keylet::account(accountID_)); auto sleSrc = AccountRootEntryW(accountID_, view(), j_);
if (!sleSrc) if (!sleSrc)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// the number of reserves in this ledger for this account that require a // the number of reserves in this ledger for this account that require a
// reserve. // reserve.
auto const reserve = accountReserve(view(), sleSrc, j_); auto const reserve = sleSrc.reserve();
// In a delegated / fee sponsored payment, the fee payer is not the source account (accountID_). // In a delegated / fee sponsored payment, the fee payer is not the source account (accountID_).
bool const accountIsPayer = ctx_.tx.getFeePayerID() == accountID_; bool const accountIsPayer = ctx_.tx.getFeePayerID() == accountID_;
@@ -820,7 +831,12 @@ Payment::doApply()
if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve) if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve)
{ {
if (auto err = verifyDepositPreauth( if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal); ctx_.tx,
ctx_.view(),
accountID_,
dstAccountID,
sleDst.mutableRawSle(),
ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
} }
@@ -3,11 +3,11 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h> #include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/PayChan.h> #include <xrpl/protocol/PayChan.h>
@@ -159,12 +159,12 @@ PaymentChannelClaim::doApply()
return tecUNFUNDED_PAYMENT; return tecUNFUNDED_PAYMENT;
} }
auto const sled = ctx_.view().peek(keylet::account(dst)); auto sled = AccountRootEntryW(dst, ctx_.view());
if (!sled) if (!sled)
return tecNO_DST; return tecNO_DST;
if (auto err = if (auto err = verifyDepositPreauth(
verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal); ctx_.tx, ctx_.view(), txAccount, dst, sled.rawSle(), ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
@@ -173,7 +173,7 @@ PaymentChannelClaim::doApply()
XRPL_ASSERT( XRPL_ASSERT(
reqDelta >= beast::kZero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta"); reqDelta >= beast::kZero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta");
(*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta; (*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta;
ctx_.view().update(sled); sled.update();
ctx_.view().update(slep); ctx_.view().update(slep);
} }
@@ -5,6 +5,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -73,7 +74,7 @@ TER
PaymentChannelCreate::preclaim(PreclaimContext const& ctx) PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
{ {
auto const account = ctx.tx[sfAccount]; auto const account = ctx.tx[sfAccount];
auto const sle = ctx.view.read(keylet::account(account)); auto const sle = AccountRootEntryR(account, ctx.view);
if (!sle) if (!sle)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -94,7 +95,7 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
{ {
// Check destination account // Check destination account
auto const sled = ctx.view.read(keylet::account(dst)); auto const sled = AccountRootEntryR(dst, ctx.view);
if (!sled) if (!sled)
return tecNO_DST; return tecNO_DST;
@@ -122,7 +123,7 @@ TER
PaymentChannelCreate::doApply() PaymentChannelCreate::doApply()
{ {
auto const account = ctx_.tx[sfAccount]; auto const account = ctx_.tx[sfAccount];
auto const sle = ctx_.view().peek(keylet::account(account)); auto sle = AccountRootEntryW(account, ctx_.view(), j_);
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -140,8 +141,8 @@ PaymentChannelCreate::doApply()
// validates the sponsor's reserve + remaining credit. When // validates the sponsor's reserve + remaining credit. When
// unsponsored this hits the source branch and validates the // unsponsored this hits the source branch and validates the
// source's pre-lock balance against base + (currentOC+1)*increment. // source's pre-lock balance against base + (currentOC+1)*increment.
if (auto const ret = checkReserve( if (auto const ret = sle.checkReserve(
ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, j_); ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -157,8 +158,8 @@ PaymentChannelCreate::doApply()
// - sponsored: 0 — sponsor covers the new owner increment, so // - sponsored: 0 — sponsor covers the new owner increment, so
// the source only owes reserve for its current owners. // the source only owes reserve for its current owners.
// - unsponsored: 1 — source owes reserve including the new increment. // - unsponsored: 1 — source owes reserve including the new increment.
auto const sourceReserve = accountReserve( auto const sourceReserve =
ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1}); sle.reserve({.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve) if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve)
return tecUNFUNDED; return tecUNFUNDED;
} }
@@ -210,9 +211,9 @@ PaymentChannelCreate::doApply()
// Deduct owner's balance, increment owner count // Deduct owner's balance, increment owner count
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal); sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
ctx_.view().update(sle); sle.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -4,11 +4,10 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h> #include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
@@ -82,14 +81,14 @@ PaymentChannelFund::doApply()
ctx_.view().update(slep); ctx_.view().update(slep);
} }
auto const sle = ctx_.view().peek(keylet::account(txAccount)); auto sle = AccountRootEntryW(txAccount, ctx_.view(), j_);
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
{ {
// Check reserve and funds availability // Check reserve and funds availability
STAmount const balance = (*sle)[sfBalance]; STAmount const balance = (*sle)[sfBalance];
if (auto const ret = checkReserve(ctx_.getApplyViewContext(), sle, balance.xrp(), {}, j_); if (auto const ret = sle.checkReserve(ctx_.getApplyViewContext(), balance.xrp(), {});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -99,12 +98,12 @@ PaymentChannelFund::doApply()
// when a sponsor is present and would ignore the source's post-lock // when a sponsor is present and would ignore the source's post-lock
// balance entirely. Funding an existing channel adds no owned object, // balance entirely. Funding an existing channel adds no owned object,
// so there is no owner-count delta. // so there is no owner-count delta.
if (balance < accountReserve(ctx_.view(), sle, j_) + ctx_.tx[sfAmount]) if (balance < sle.reserve() + ctx_.tx[sfAmount])
return tecUNFUNDED; return tecUNFUNDED;
} }
// do not allow adding funds if dst does not exist // do not allow adding funds if dst does not exist
if (AccountID const dst = (*slep)[sfDestination]; !ctx_.view().read(keylet::account(dst))) if (AccountID const dst = (*slep)[sfDestination]; !AccountRootEntryW(dst, ctx_.view()))
{ {
return tecNO_DST; return tecNO_DST;
} }
@@ -113,7 +112,7 @@ PaymentChannelFund::doApply()
ctx_.view().update(slep); ctx_.view().update(slep);
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
ctx_.view().update(sle); sle.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
@@ -63,7 +64,7 @@ PermissionedDomainDelete::doApply()
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
auto const ownerSle = view().peek(keylet::account(accountID_)); auto ownerSle = AccountRootEntryW(accountID_, view());
XRPL_ASSERT( XRPL_ASSERT(
ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0, ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0,
"xrpl::PermissionedDomainDelete::doApply : nonzero owner count"); "xrpl::PermissionedDomainDelete::doApply : nonzero owner count");
@@ -2,7 +2,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -19,6 +19,7 @@
#include <xrpl/tx/Transactor.h> #include <xrpl/tx/Transactor.h>
#include <memory> #include <memory>
#include <optional>
#include <utility> #include <utility>
namespace xrpl { namespace xrpl {
@@ -80,7 +81,7 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx)
TER TER
PermissionedDomainSet::doApply() PermissionedDomainSet::doApply()
{ {
auto const ownerSle = view().peek(keylet::account(accountID_)); auto ownerSle = AccountRootEntryW(accountID_, view(), ctx_.journal);
if (!ownerSle) if (!ownerSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -109,8 +110,7 @@ PermissionedDomainSet::doApply()
// Create new permissioned domain. // Create new permissioned domain.
// Check reserve availability for new object creation // Check reserve availability for new object creation
auto const balance = STAmount((*ownerSle)[sfBalance]).xrp(); auto const balance = STAmount((*ownerSle)[sfBalance]).xrp();
auto const reserve = auto const reserve = ownerSle.reserve({.ownerCountDelta = 1});
accountReserve(ctx_.view(), ownerSle, ctx_.journal, {.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
@@ -130,7 +130,8 @@ PermissionedDomainSet::doApply()
slePd->setFieldU64(sfOwnerNode, *page); slePd->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve. // If we succeeded, the new entry counts against the creator's reserve.
increaseOwnerCount(view(), ownerSle, {}, 1, ctx_.journal); std::optional<AccountRootEntryW> noSponsor;
ownerSle.increaseOwnerCount(noSponsor, 1);
view().insert(slePd); view().insert(slePd);
} }
@@ -4,6 +4,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/ProposalHelpers.h> #include <xrpl/ledger/helpers/ProposalHelpers.h>
@@ -159,7 +160,7 @@ TransactionProposalCreate::preclaim(PreclaimContext const& ctx)
} }
AccountID const target = proposedTx.getAccountID(sfAccount); AccountID const target = proposedTx.getAccountID(sfAccount);
auto const sleTarget = ctx.view.read(keylet::account(target)); auto const sleTarget = AccountRootEntryR(target, ctx.view);
if (!sleTarget) if (!sleTarget)
{ {
JLOG(ctx.j.debug()) << "TransactionProposalCreate: target account " JLOG(ctx.j.debug()) << "TransactionProposalCreate: target account "
@@ -297,7 +298,7 @@ TransactionProposalCreate::preclaim(PreclaimContext const& ctx)
TER TER
TransactionProposalCreate::doApply() TransactionProposalCreate::doApply()
{ {
auto const sle = view().peek(keylet::account(accountID_)); auto sle = AccountRootEntryW(accountID_, view(), ctx_.journal);
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -307,12 +308,10 @@ TransactionProposalCreate::doApply()
// The proposal holds a full transaction plus its collected signatures, so // The proposal holds a full transaction plus its collected signatures, so
// it reserves more than a typical ledger entry (5 increments; 10 for a // it reserves more than a typical ledger entry (5 increments; 10 for a
// proposed Batch). // proposed Batch).
if (auto const ret = checkReserve( if (auto const ret = sle.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sle,
preFeeBalance_, preFeeBalance_,
{.ownerCountDelta = static_cast<int>(ownerCount)}, {.ownerCountDelta = static_cast<int>(ownerCount)});
ctx_.journal);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -327,7 +326,6 @@ TransactionProposalCreate::doApply()
view().insert(sleProposal); view().insert(sleProposal);
auto viewJ = ctx_.registry.get().getJournal("View");
{ {
auto const page = view().dirInsert( auto const page = view().dirInsert(
keylet::ownerDir(accountID_), proposalKeylet, describeOwnerDir(accountID_)); keylet::ownerDir(accountID_), proposalKeylet, describeOwnerDir(accountID_));
@@ -336,7 +334,7 @@ TransactionProposalCreate::doApply()
sleProposal->setFieldU64(sfOwnerNode, *page); sleProposal->setFieldU64(sfOwnerNode, *page);
} }
increaseOwnerCount(ctx_.getApplyViewContext(), sle, ownerCount, viewJ); sle.increaseOwnerCount(ctx_.getApplyViewContext(), ownerCount);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleProposal); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleProposal);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -7,6 +7,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -164,11 +165,11 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx)
if (sponseeID == sponsorID) if (sponseeID == sponsorID)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const sponsorAccSle = ctx.view.read(keylet::account(sponsorID)); auto const sponsorAccSle = AccountRootEntryR(sponsorID, ctx.view);
if (!sponsorAccSle) if (!sponsorAccSle)
return tecNO_DST; return tecNO_DST;
auto const sponseeSle = ctx.view.read(keylet::account(sponseeID)); auto const sponseeSle = AccountRootEntryR(sponseeID, ctx.view);
if (!sponseeSle) if (!sponseeSle)
return tecNO_DST; return tecNO_DST;
@@ -212,7 +213,7 @@ deleteSponsorship(ApplyView& view, SLE::Ref sle, beast::Journal j)
// The sponsor owns the Sponsorship object, so deletion releases the // The sponsor owns the Sponsorship object, so deletion releases the
// sponsor's owner reserve. // sponsor's owner reserve.
auto sponsorAccSle = view.peek(keylet::account(sponsorID)); auto sponsorAccSle = AccountRootEntryW(sponsorID, view);
if (!sponsorAccSle) if (!sponsorAccSle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -237,7 +238,7 @@ deleteSponsorship(ApplyView& view, SLE::Ref sle, beast::Journal j)
if (sle->isFieldPresent(sfFeeAmount)) if (sle->isFieldPresent(sfFeeAmount))
{ {
(*sponsorAccSle)[sfBalance] += sle->getFieldAmount(sfFeeAmount); (*sponsorAccSle)[sfBalance] += sle->getFieldAmount(sfFeeAmount);
view.update(sponsorAccSle); sponsorAccSle.update();
} }
view.erase(sle); view.erase(sle);
@@ -250,8 +251,8 @@ SponsorshipSet::createSponsorship(
Keylet const& sponsorshipKeylet, Keylet const& sponsorshipKeylet,
AccountID const& sponsorID, AccountID const& sponsorID,
AccountID const& sponseeID, AccountID const& sponseeID,
SLE::Ref sponsorAccSle, AccountRootEntryW& sponsorAccSle,
SLE::Ref reserveSponsorAccSle) std::optional<AccountRootEntryW>& reserveSponsorAccSle)
{ {
auto const feeAmountDelta = ctx_.tx[~sfFeeAmountDelta]; auto const feeAmountDelta = ctx_.tx[~sfFeeAmountDelta];
auto const maxFee = ctx_.tx[~sfMaxFee]; auto const maxFee = ctx_.tx[~sfMaxFee];
@@ -276,13 +277,11 @@ SponsorshipSet::createSponsorship(
if (hasPositiveFeeAmount) if (hasPositiveFeeAmount)
sponsorBalanceAfterFee -= *feeAmountDelta; sponsorBalanceAfterFee -= *feeAmountDelta;
if (auto const ret = checkReserve( if (auto const ret = sponsorAccSle.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponsorAccSle,
sponsorBalanceAfterFee.xrp(), sponsorBalanceAfterFee.xrp(),
reserveSponsorAccSle, reserveSponsorAccSle,
{.ownerCountDelta = 1}, {.ownerCountDelta = 1},
ctx_.journal,
tecUNFUNDED); tecUNFUNDED);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
@@ -323,8 +322,9 @@ SponsorshipSet::createSponsorship(
(*newSle)[sfSponseeNode] = *sponseePage; (*newSle)[sfSponseeNode] = *sponseePage;
// NOLINTNEXTLINE(readability-suspicious-call-argument) // NOLINTNEXTLINE(readability-suspicious-call-argument)
increaseOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, ctx_.journal); sponsorAccSle.increaseOwnerCount(reserveSponsorAccSle, 1);
addSponsorToLedgerEntry(newSle, reserveSponsorAccSle); if (reserveSponsorAccSle.has_value())
addSponsorToLedgerEntry(newSle, reserveSponsorAccSle->rawSle());
ctx_.view().insert(newSle); ctx_.view().insert(newSle);
return tesSUCCESS; return tesSUCCESS;
@@ -339,7 +339,7 @@ SponsorshipSet::doApply()
if (sponseeID == sponsorID) if (sponseeID == sponsorID)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorID)); auto sponsorAccSle = AccountRootEntryW(sponsorID, ctx_.view(), ctx_.journal);
if (!sponsorAccSle) if (!sponsorAccSle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -388,13 +388,11 @@ SponsorshipSet::doApply()
// object. // object.
(*sponsorAccSle)[sfBalance] -= actualDelta; (*sponsorAccSle)[sfBalance] -= actualDelta;
if (auto const ret = checkReserve( if (auto const ret = sponsorAccSle.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponsorAccSle,
(*sponsorAccSle)[sfBalance]->xrp(), (*sponsorAccSle)[sfBalance]->xrp(),
*reserveSponsorAccSle, *reserveSponsorAccSle,
{}, {},
ctx_.journal,
tecUNFUNDED); tecUNFUNDED);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
@@ -413,7 +411,7 @@ SponsorshipSet::doApply()
{ {
(*sponsorshipSle)[sfFeeAmount] = newFee; (*sponsorshipSle)[sfFeeAmount] = newFee;
} }
ctx_.view().update(sponsorAccSle); sponsorAccSle.update();
} }
if (maxFee) if (maxFee)
@@ -7,7 +7,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -20,17 +20,13 @@
#include <bit> #include <bit>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include <memory> #include <optional>
namespace xrpl { namespace xrpl {
// Increment an uint32 sponsor count field and update the SLE. // Increment an uint32 sponsor count field and update the SLE.
static TER static TER
incrementSponsorCount( incrementSponsorCount(AccountRootEntryW& sle, SF_UINT32 const& field, std::uint32_t const delta)
ApplyView& view,
SLE::Ref sle,
SF_UINT32 const& field,
std::uint32_t const delta)
{ {
auto const currentValue = sle->getFieldU32(field); auto const currentValue = sle->getFieldU32(field);
if (std::numeric_limits<std::uint32_t>::max() - currentValue < delta) if (std::numeric_limits<std::uint32_t>::max() - currentValue < delta)
@@ -42,17 +38,13 @@ incrementSponsorCount(
} }
sle->at(field) = currentValue + delta; sle->at(field) = currentValue + delta;
view.update(sle); sle.update();
return tesSUCCESS; return tesSUCCESS;
} }
// Decrement an uint32 sponsor count field and update the SLE. // Decrement an uint32 sponsor count field and update the SLE.
static TER static TER
decrementSponsorCount( decrementSponsorCount(AccountRootEntryW& sle, SF_UINT32 const& field, std::uint32_t const delta)
ApplyView& view,
SLE::Ref sle,
SF_UINT32 const& field,
std::uint32_t const delta)
{ {
auto const currentValue = sle->getFieldU32(field); auto const currentValue = sle->getFieldU32(field);
if (currentValue < delta) if (currentValue < delta)
@@ -64,7 +56,7 @@ decrementSponsorCount(
} }
sle->at(field) = currentValue - delta; sle->at(field) = currentValue - delta;
view.update(sle); sle.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -232,7 +224,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
auto const account = ctx.tx[sfAccount]; auto const account = ctx.tx[sfAccount];
auto const sponseeID = ctx.tx[~sfSponsee].value_or(account); auto const sponseeID = ctx.tx[~sfSponsee].value_or(account);
auto const sponseeSle = ctx.view.read(keylet::account(sponseeID)); auto const sponseeSle = AccountRootEntryR(sponseeID, ctx.view);
if (!sponseeSle) if (!sponseeSle)
{ {
// If it is ending sponsorship, sfSponsee is user input, return terNO_ACCOUNT if it does not // If it is ending sponsorship, sfSponsee is user input, return terNO_ACCOUNT if it does not
@@ -248,7 +240,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
// Default setup with an account sponsorship transfer. If it is an object transfer, they will be // Default setup with an account sponsorship transfer. If it is an object transfer, they will be
// overridden to the object SLE and its type-specific sponsor field: // overridden to the object SLE and its type-specific sponsor field:
// sfHighSponsor/sfLowSponsor for a RippleState, sfSponsor for other object types. // sfHighSponsor/sfLowSponsor for a RippleState, sfSponsor for other object types.
SLE::const_pointer targetSle = sponseeSle; SLE::const_pointer targetSle = sponseeSle.rawSle();
auto const* sponsorField = &sfSponsor; auto const* sponsorField = &sfSponsor;
if (objectID.has_value()) if (objectID.has_value())
@@ -313,7 +305,7 @@ SponsorshipTransfer::doApply()
auto const objectID = ctx_.tx[~sfObjectID]; auto const objectID = ctx_.tx[~sfObjectID];
auto const sponseeID = ctx_.tx[~sfSponsee].value_or(accountID_); auto const sponseeID = ctx_.tx[~sfSponsee].value_or(accountID_);
auto const sponseeSle = view().peek(keylet::account(sponseeID)); auto sponseeSle = AccountRootEntryW(sponseeID, view(), ctx_.journal);
if (!sponseeSle) if (!sponseeSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -337,7 +329,7 @@ SponsorshipTransfer::doApply()
if (!isLedgerEntryOwner(view(), *objectSle, sponseeID)) if (!isLedgerEntryOwner(view(), *objectSle, sponseeID))
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const ownerSle = view().peek(keylet::account(sponseeID)); auto ownerSle = AccountRootEntryW(sponseeID, view());
if (!ownerSle) if (!ownerSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -355,27 +347,25 @@ SponsorshipTransfer::doApply()
if (!newSponsor) if (!newSponsor)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const newSponsorID = *newSponsor; auto const newSponsorID = *newSponsor;
auto const newSponsorSle = view().peek(keylet::account(newSponsorID)); auto newSponsorSle = AccountRootEntryW(newSponsorID, view());
if (!newSponsorSle) if (!newSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Check if new sponsor has sufficient balance // Check if new sponsor has sufficient balance
// NOLINTNEXTLINE(readability-suspicious-call-argument) // NOLINTNEXTLINE(readability-suspicious-call-argument)
if (auto const ter = checkReserve( if (auto const ter = sponseeSle.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponseeSle,
sponseeSle->getFieldAmount(sfBalance).xrp(), sponseeSle->getFieldAmount(sfBalance).xrp(),
newSponsorSle, std::optional<AccountRootEntryR>{newSponsorSle},
{.ownerCountDelta = ownerCountDelta}, {.ownerCountDelta = ownerCountDelta});
ctx_.journal);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; return ter;
if (isCreate) if (isCreate)
{ {
// Update owner's sponsored count // Update owner's sponsored count
if (auto const ter = incrementSponsorCount( if (auto const ter =
view(), ownerSle, sfSponsoredOwnerCount, ownerCountDelta); incrementSponsorCount(ownerSle, sfSponsoredOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
@@ -384,20 +374,20 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = objectSle->getAccountID(sponsorField); auto const oldSponsorID = objectSle->getAccountID(sponsorField);
if (!oldSponsorID) if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID)); auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view());
if (!oldSponsorSle) if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Decrement old sponsor's sponsoring count // Decrement old sponsor's sponsoring count
if (auto const ter = decrementSponsorCount( if (auto const ter = decrementSponsorCount(
view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
// Increment new sponsor's sponsoring count // Increment new sponsor's sponsoring count
if (auto const ter = incrementSponsorCount( if (auto const ter =
view(), newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); incrementSponsorCount(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
@@ -421,7 +411,7 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = objectSle->getAccountID(sponsorField); auto const oldSponsorID = objectSle->getAccountID(sponsorField);
if (!oldSponsorID) if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID)); auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view());
if (!oldSponsorSle) if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -433,26 +423,24 @@ SponsorshipTransfer::doApply()
// steps in (Reassign), or the object/account is deleted. // steps in (Reassign), or the object/account is deleted.
if (view().rules().enabled(fixCleanup3_4_0)) if (view().rules().enabled(fixCleanup3_4_0))
{ {
if (auto const ter = checkReserve( if (auto const ter = sponseeSle.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponseeSle, balanceBeforeFee(sponseeSle.rawSle()),
balanceBeforeFee(sponseeSle), std::nullopt,
SLE::pointer(), {.ownerCountDelta = ownerCountDelta});
{.ownerCountDelta = ownerCountDelta},
ctx_.journal);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; return ter;
} }
// Decrement sponsored count // Decrement sponsored count
if (auto const ter = decrementSponsorCount( if (auto const ter =
view(), sponseeSle, sfSponsoredOwnerCount, ownerCountDelta); decrementSponsorCount(sponseeSle, sfSponsoredOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
// Decrement old sponsoring count // Decrement old sponsoring count
if (auto const ter = decrementSponsorCount( if (auto const ter =
view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); decrementSponsorCount(oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
@@ -475,17 +463,15 @@ SponsorshipTransfer::doApply()
if (!newSponsor) if (!newSponsor)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const newSponsorID = *newSponsor; auto const newSponsorID = *newSponsor;
auto const newSponsorSle = view().peek(keylet::account(newSponsorID)); auto newSponsorSle = AccountRootEntryW(newSponsorID, view());
if (!newSponsorSle) if (!newSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ter = checkReserve( if (auto const ter = sponseeSle.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponseeSle,
sponseeSle->getFieldAmount(sfBalance).xrp(), sponseeSle->getFieldAmount(sfBalance).xrp(),
newSponsorSle, std::optional<AccountRootEntryR>{newSponsorSle},
{.accountCountDelta = 1}, {.accountCountDelta = 1});
ctx_.journal);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; return ter;
@@ -494,26 +480,25 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor); auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
if (!oldSponsorID) if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID)); auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view());
if (!oldSponsorSle) if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Decrement old sponsoring count // Decrement old sponsoring count
if (auto const ter = if (auto const ter =
decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1); decrementSponsorCount(oldSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
// Increment new sponsoring count // Increment new sponsoring count
if (auto const ter = if (auto const ter = incrementSponsorCount(newSponsorSle, sfSponsoringAccountCount, 1);
incrementSponsorCount(view(), newSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
// Account is now sponsored by new sponsor // Account is now sponsored by new sponsor
sponseeSle->setAccountID(sfSponsor, newSponsorID); sponseeSle->setAccountID(sfSponsor, newSponsorID);
view().update(sponseeSle); sponseeSle.update();
} }
else if (ctx_.tx.isFlag(tfSponsorshipEnd)) else if (ctx_.tx.isFlag(tfSponsorshipEnd))
{ {
@@ -521,28 +506,25 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor); auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
if (!oldSponsorID) if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID)); auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view());
if (!oldSponsorSle) if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// The sponsee must be able to hold its own account reserve after // The sponsee must be able to hold its own account reserve after
// the sponsorship is removed. // the sponsorship is removed.
if (auto const ter = checkReserve( if (auto const ter = sponseeSle.checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponseeSle, balanceBeforeFee(sponseeSle.rawSle()),
balanceBeforeFee(sponseeSle), std::optional<AccountRootEntryR>{},
SLE::pointer(), {.accountCountDelta = 1});
{.accountCountDelta = 1},
ctx_.journal);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; return ter;
sponseeSle->makeFieldAbsent(sfSponsor); sponseeSle->makeFieldAbsent(sfSponsor);
view().update(sponseeSle); sponseeSle.update();
// Decrement account sponsoring count // Decrement account sponsoring count
if (auto const ter = if (auto const ter = decrementSponsorCount(oldSponsorSle, sfSponsoringAccountCount, 1);
decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
@@ -2,6 +2,7 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -96,7 +97,7 @@ LedgerStateFix::preclaim(PreclaimContext const& ctx)
if (static_cast<FixType>(ctx.tx[sfLedgerFixType]) == FixType::NfTokenPageLink) if (static_cast<FixType>(ctx.tx[sfLedgerFixType]) == FixType::NfTokenPageLink)
{ {
AccountID const owner{ctx.tx[sfOwner]}; AccountID const owner{ctx.tx[sfOwner]};
if (!ctx.view.read(keylet::account(owner))) if (!AccountRootEntryR(owner, ctx.view))
return tecOBJECT_NOT_FOUND; return tecOBJECT_NOT_FOUND;
return tesSUCCESS; return tesSUCCESS;
@@ -4,7 +4,8 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
@@ -18,6 +19,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <optional>
namespace xrpl { namespace xrpl {
@@ -42,7 +44,7 @@ TER
TicketCreate::preclaim(PreclaimContext const& ctx) TicketCreate::preclaim(PreclaimContext const& ctx)
{ {
auto const id = ctx.tx[sfAccount]; auto const id = ctx.tx[sfAccount];
auto const sleAccountRoot = ctx.view.read(keylet::account(id)); auto const sleAccountRoot = AccountRootEntryR(id, ctx.view);
if (!sleAccountRoot) if (!sleAccountRoot)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -67,7 +69,7 @@ TicketCreate::preclaim(PreclaimContext const& ctx)
TER TER
TicketCreate::doApply() TicketCreate::doApply()
{ {
SLE::pointer const sleAccountRoot = view().peek(keylet::account(accountID_)); auto sleAccountRoot = AccountRootEntryW(accountID_, view(), j_);
if (!sleAccountRoot) if (!sleAccountRoot)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -75,15 +77,10 @@ TicketCreate::doApply()
// check the starting balance because we want to allow dipping into the // check the starting balance because we want to allow dipping into the
// reserve to pay fees. // reserve to pay fees.
std::uint32_t const ticketCount = ctx_.tx[sfTicketCount]; std::uint32_t const ticketCount = ctx_.tx[sfTicketCount];
if (preFeeBalance_ < accountReserve( if (preFeeBalance_ <
view(), sleAccountRoot.reserve({.ownerCountDelta = static_cast<std::int32_t>(ticketCount)}))
sleAccountRoot,
j_,
{.ownerCountDelta = static_cast<std::int32_t>(ticketCount)}))
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
beast::Journal const viewJ{ctx_.registry.get().getJournal("View")};
// The starting ticket sequence is the same as the current account // The starting ticket sequence is the same as the current account
// root sequence. Before we got here to doApply(), the transaction // root sequence. Before we got here to doApply(), the transaction
// machinery already incremented the account root sequence if that // machinery already incremented the account root sequence if that
@@ -125,7 +122,8 @@ TicketCreate::doApply()
sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount); sleAccountRoot->setFieldU32(sfTicketCount, oldTicketCount + ticketCount);
// Every added Ticket counts against the creator's reserve. // Every added Ticket counts against the creator's reserve.
increaseOwnerCount(view(), sleAccountRoot, {}, ticketCount, viewJ); std::optional<AccountRootEntryW> noSponsor;
sleAccountRoot.increaseOwnerCount(noSponsor, ticketCount);
// TicketCreate is the only transaction that can cause an account root's // TicketCreate is the only transaction that can cause an account root's
// Sequence field to increase by more than one. October 2018. // Sequence field to increase by more than one. October 2018.
@@ -2,6 +2,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -186,8 +187,8 @@ Clawback::preclaim(PreclaimContext const& ctx)
auto const clawAmount = ctx.tx[sfAmount]; auto const clawAmount = ctx.tx[sfAmount];
AccountID const holder = clawAmount.holds<Issue>() ? clawAmount.getIssuer() : ctx.tx[sfHolder]; AccountID const holder = clawAmount.holds<Issue>() ? clawAmount.getIssuer() : ctx.tx[sfHolder];
auto const sleIssuer = ctx.view.read(keylet::account(issuer)); auto const sleIssuer = AccountRootEntryR(issuer, ctx.view);
auto const sleHolder = ctx.view.read(keylet::account(holder)); auto const sleHolder = AccountRootEntryR(holder, ctx.view);
if (!sleIssuer || !sleHolder) if (!sleIssuer || !sleHolder)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -0,0 +1,220 @@
#include <xrpl/tx/transactors/token/ConfidentialMPTHolderKeyUpdate.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <bit>
#include <cstdint>
namespace xrpl {
bool
ConfidentialMPTHolderKeyUpdate::checkExtraFeatures(PreflightContext const& ctx)
{
// Holder key update is only meaningful when confidential transfers are enabled.
return ctx.rules.enabled(featureConfidentialTransfer);
}
std::uint32_t
ConfidentialMPTHolderKeyUpdate::getFlagsMask(PreflightContext const& ctx)
{
return tfConfidentialMPTHolderKeyUpdateMask;
}
NotTEC
ConfidentialMPTHolderKeyUpdate::preflight(PreflightContext const& ctx)
{
bool const rotation = ctx.tx.isFlag(tfHolderKeyRotation);
bool const recovery = ctx.tx.isFlag(tfHolderKeyRecovery);
bool const cancel = ctx.tx.isFlag(tfCancelRecovery);
// Exactly one of the three mode flags must be set.
static constexpr auto modeFlags = tfHolderKeyRotation | tfHolderKeyRecovery | tfCancelRecovery;
if (std::popcount(ctx.tx.getFlags() & modeFlags) != 1)
return temINVALID_FLAG;
// The issuer cannot hold confidential balances.
if (ctx.tx[sfAccount] == MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer())
return temMALFORMED;
bool const hasHolderKey = ctx.tx.isFieldPresent(sfHolderEncryptionKey);
bool const hasSpending = ctx.tx.isFieldPresent(sfConfidentialBalanceSpending);
bool const hasInbox = ctx.tx.isFieldPresent(sfConfidentialBalanceInbox);
bool const hasProof = ctx.tx.isFieldPresent(sfZKProof);
if (cancel)
{
// Cancel mode only revokes a pending recovery authorization; it
// carries no key material, balances, or proof.
if (hasHolderKey || hasSpending || hasInbox || hasProof)
return temMALFORMED;
return tesSUCCESS;
}
// Rotation and Recovery both require a holder key and a proof.
if (!hasHolderKey || !hasProof)
return temMALFORMED;
// Rotation mode requires re-encrypted balances; Recovery mode must not
// provide them since the holder cannot decrypt the current ones.
if (rotation && (!hasSpending || !hasInbox))
return temMALFORMED;
if (recovery && (hasSpending || hasInbox))
return temMALFORMED;
if (hasSpending &&
ctx.tx[sfConfidentialBalanceSpending].length() != kEcGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
if (hasInbox && ctx.tx[sfConfidentialBalanceInbox].length() != kEcGamalEncryptedTotalLength)
return temBAD_CIPHERTEXT;
if (!isValidCompressedECPoint(ctx.tx[sfHolderEncryptionKey]))
return temMALFORMED;
if (hasSpending && !isValidCiphertext(ctx.tx[sfConfidentialBalanceSpending]))
return temBAD_CIPHERTEXT;
if (hasInbox && !isValidCiphertext(ctx.tx[sfConfidentialBalanceInbox]))
return temBAD_CIPHERTEXT;
return tesSUCCESS;
}
XRPAmount
ConfidentialMPTHolderKeyUpdate::calculateBaseFee(ReadView const& view, STTx const& tx)
{
return Transactor::calculateBaseFee(view, tx, kConfidentialFeeMultiplier);
}
TER
ConfidentialMPTHolderKeyUpdate::preclaim(PreclaimContext const& ctx)
{
auto const account = ctx.tx[sfAccount];
auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;
if (!sleIssuance->isFlag(lsfMPTCanHoldConfidentialBalance))
return tecNO_PERMISSION;
auto const sleMptoken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
if (!sleMptoken->isFieldPresent(sfHolderEncryptionKey) ||
!sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) ||
!sleMptoken->isFieldPresent(sfConfidentialBalanceInbox))
{
return tecNO_PERMISSION;
}
if (ctx.tx.isFlag(tfCancelRecovery))
{
// Nothing to cancel if no recovery is pending.
if (!sleMptoken->isFieldPresent(sfRecoveryKey))
return tecNO_PERMISSION;
return tesSUCCESS;
}
if (ctx.tx[sfHolderEncryptionKey] == (*sleMptoken)[sfHolderEncryptionKey])
return tecDUPLICATE;
// Recovery mode: reject if a recovery is already pending
if (ctx.tx.isFlag(tfHolderKeyRecovery) && sleMptoken->isFieldPresent(sfRecoveryKey))
return tecNO_PERMISSION;
return tesSUCCESS;
}
TER
ConfidentialMPTHolderKeyUpdate::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
if (!sleMptoken)
{
// LCOV_EXCL_START
UNREACHABLE(
"xrpl::ConfidentialMPTHolderKeyUpdate::doApply : preclaim already validated the "
"MPToken exists");
return tecINTERNAL;
// LCOV_EXCL_STOP
}
if (ctx_.tx.isFlag(tfCancelRecovery))
{
// The holder revokes their pending recovery authorization; the
// current key and balances are left untouched.
sleMptoken->makeFieldAbsent(sfRecoveryKey);
view().update(sleMptoken);
return tesSUCCESS;
}
auto const newPubKey = ctx_.tx[sfHolderEncryptionKey];
if (ctx_.tx.isFlag(tfHolderKeyRotation))
{
// Replace the key and balances in rotation mode. Rotation proves the
// holder still has the old key, so any pending recovery is cancelled.
(*sleMptoken)[sfHolderEncryptionKey] = newPubKey;
(*sleMptoken)[sfConfidentialBalanceSpending] = ctx_.tx[sfConfidentialBalanceSpending];
(*sleMptoken)[sfConfidentialBalanceInbox] = ctx_.tx[sfConfidentialBalanceInbox];
sleMptoken->makeFieldAbsent(sfRecoveryKey);
incrementConfidentialVersion(*sleMptoken);
}
else if (ctx_.tx.isFlag(tfHolderKeyRecovery))
{
// Recovery mode: the holder cannot decrypt their current balances,
// so only the pending recovery key is recorded. The balances are
// rewritten separately by the issuer via ConfidentialMPTRecoverBalance.
(*sleMptoken)[sfRecoveryKey] = newPubKey;
}
else
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::ConfidentialMPTHolderKeyUpdate::doApply : invalid mode");
return tecINTERNAL;
// LCOV_EXCL_STOP
}
view().update(sleMptoken);
return tesSUCCESS;
}
void
ConfidentialMPTHolderKeyUpdate::visitInvariantEntry(bool, SLE::ConstRef, SLE::ConstRef)
{
}
bool
ConfidentialMPTHolderKeyUpdate::finalizeInvariants(
STTx const&,
TER,
XRPAmount,
ReadView const&,
beast::Journal const&)
{
return true;
}
} // namespace xrpl
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h> #include <xrpl/protocol/ConfidentialTransfer.h>
@@ -167,7 +168,7 @@ ConfidentialMPTSend::preclaim(PreclaimContext const& ctx)
// Check if destination account exists // Check if destination account exists
auto const destination = ctx.tx[sfDestination]; auto const destination = ctx.tx[sfDestination];
auto const sleDst = ctx.view.read(keylet::account(destination)); auto const sleDst = AccountRootEntryR(destination, ctx.view);
if (!sleDst) if (!sleDst)
return tecNO_TARGET; return tecNO_TARGET;
@@ -292,7 +293,7 @@ ConfidentialMPTSend::preclaim(PreclaimContext const& ctx)
// Check deposit preauth before the expensive ZK proof verification. // Check deposit preauth before the expensive ZK proof verification.
// Uses read-only view. // Uses read-only view.
auto const preauthErr = auto const preauthErr =
checkDepositPreauth(ctx.tx, ctx.view, account, destination, sleDst, ctx.j); checkDepositPreauth(ctx.tx, ctx.view, account, destination, sleDst.rawSle(), ctx.j);
if (!isTesSuccess(preauthErr)) if (!isTesSuccess(preauthErr))
return preauthErr; return preauthErr;
@@ -309,7 +310,7 @@ ConfidentialMPTSend::doApply()
auto sleDestinationMPToken = view().peek(keylet::mptoken(mptIssuanceID, destination)); auto sleDestinationMPToken = view().peek(keylet::mptoken(mptIssuanceID, destination));
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID)); auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
auto const sleDestAcct = view().read(keylet::account(destination)); auto const sleDestAcct = AccountRootEntryW(destination, view());
if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance || !sleDestAcct) if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance || !sleDestAcct)
{ {
@@ -1,6 +1,7 @@
#include <xrpl/tx/transactors/token/MPTokenAuthorize.h> #include <xrpl/tx/transactors/token/MPTokenAuthorize.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -120,7 +121,7 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx)
return tesSUCCESS; return tesSUCCESS;
} }
auto const sleHolder = ctx.view.read(keylet::account(*holderID)); auto const sleHolder = AccountRootEntryR(*holderID, ctx.view);
if (!sleHolder) if (!sleHolder)
return tecNO_DST; return tecNO_DST;
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff. Show more