Compare commits

..
Author SHA1 Message Date
Mayukha Vadari 51401e1f57 Document transferRate return value 2026-10-05 15:50:45 +01:00
Mayukha Vadari 8891288bf1 refactor: Move transferRate onto AccountRootEntry
Add the const member AccountRootEntry::transferRate(). The
transferRate(view, issuer) free function stays as the shim and keeps
returning kParityRate when the account does not exist.
2026-10-05 15:50:07 +01:00
Mayukha Vadari 821567f4ad refactor: Move xrpLiquid onto AccountRootEntry
Move the xrpLiquid body to the const member
AccountRootEntry::xrpLiquid(ownerCountAdj). It returns zero when the
entry does not exist, as before. The xrpLiquid(view, id, adj, j) free
function stays as a one-line inline shim that passes the journal to the
entry constructor.
2026-10-05 15:50:03 +01:00
Mayukha Vadari dcd32a6f6a Document isGlobalFrozen return value 2026-10-05 15:49:56 +01:00
Mayukha Vadari 14368cf1f3 refactor: Move isGlobalFrozen onto AccountRootEntry
Add the const member AccountRootEntry::isGlobalFrozen(), which reads
lsfGlobalFreeze. The isGlobalFrozen(view, issuer) free function stays
as the shim and keeps its XRP and missing-account handling.
2026-10-05 15:49:28 +01:00
Mayukha Vadari 95323edaf3 Document checkDestinationAndTag parameters and return value 2026-10-05 15:49:19 +01:00
Mayukha Vadari 8940046ca8 refactor: Move checkDestinationAndTag onto AccountRootEntry
Replace the entry-taking checkDestinationAndTag free function with the
const member AccountRootEntry::checkDestinationAndTag(hasDestinationTag).
The body is short, so it is inline in the header.
2026-10-05 15:48:35 +01:00
Mayukha Vadari f27581a6bb Document createPseudoAccount parameters and return value 2026-10-05 15:48:28 +01:00
Mayukha Vadari b0a1f81256 refactor: Move createPseudoAccount onto AccountRootEntry
Replace the free createPseudoAccount function with the static factory
AccountRootEntryW::createPseudoAccount. It returns the new entry
instead of a raw SLE pointer; every caller only reads sfAccount from
the result, so the call sites change only in name.

The factory builds the account with newSLE() and insert() on the
entry. pseudoAccountAddress and getPseudoAccountFields stay free.
2026-10-05 15:47:46 +01:00
Mayukha Vadari d21cf46021 Document isPseudoAccount return value 2026-10-05 15:47:38 +01:00
Mayukha Vadari b3e0936504 refactor: Move isPseudoAccount onto AccountRootEntry
Replace the entry-taking isPseudoAccount free function with the const
member AccountRootEntry::isPseudoAccount(). The (view, AccountID)
overload stays as a one-line shim. The invariant-only
isPseudoAccountSle(SLE) helper is unchanged; VaultInvariant.cpp changes
only because its call used the removed overload.
2026-10-05 15:46:52 +01:00
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
183 changed files with 2794 additions and 2936 deletions

No files matched your search

-5
View File
@@ -30,11 +30,6 @@ Version 3.5.0 is not yet released.
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive a message with `type` `mptTransaction` for each validated transaction whose metadata affects a subscribed issuance; the message has the same fields as the `transactions` stream. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
### Bugfixes in 3.5.0
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
## XRP Ledger server version 3.4.0
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
Generated
+6 -6
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1791130267,
"narHash": "sha256-1sjwcQMcgAbmiip/VivBSiK8p2bTkQGOvCuO7vnwfx4=",
"lastModified": 1781173989,
"narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9013764fcc0ea99fa16cf7aa7decf8a5c3889dd2",
"rev": "8c91a71d13451abc40eb9dae8910f972f979852f",
"type": "github"
},
"original": {
@@ -47,11 +47,11 @@
]
},
"locked": {
"lastModified": 1791189933,
"narHash": "sha256-Y8eN7ki0Urx4Ojf4w1xSpWg8uk/3lJqK+E0Oth77rH0=",
"lastModified": 1784611586,
"narHash": "sha256-OfqgY+0hp/zseZB7uyH0U8kIDPS4scZZCyAurEplvG0=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "e60029353d0c48d216bc4b065168ccd4079c166f",
"rev": "14f58845249f3552a89b07772626b8d3c632fa86",
"type": "github"
},
"original": {
+56 -147
View File
@@ -1,31 +1,29 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/beast/type_name.h>
#include <boost/core/type_name.hpp>
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace xrpl {
/**
* Manages all counted object types.
*
* Counters register themselves on a lock-free intrusive list maintained
* by this object when constructed. Because counters are never destroyed
* or removed, the ABA problem does not apply.
*
* The registry is iterable as a forward range.
*/
class CountedObjects
{
public:
static CountedObjects&
getInstance() noexcept;
using Entry = std::pair<std::string, int>;
using List = std::vector<Entry>;
[[nodiscard]] List
getCounts(int minimumThreshold) const;
public:
/**
* Implementation for @ref CountedObject.
@@ -35,147 +33,68 @@ public:
class Counter
{
public:
Counter(std::string name) noexcept;
Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
{
// Insert ourselves at the front of the lock-free linked list
CountedObjects& instance = CountedObjects::getInstance();
Counter* head = nullptr;
// Counters are intrusive list nodes whose addresses are published
// in the registry; they must never be copied or moved. The atomic
// members already force this, but we make it explicit.
Counter(Counter const&) = delete;
Counter&
operator=(Counter const&) = delete;
Counter(Counter&&) = delete;
Counter&
operator=(Counter&&) = delete;
do
{
head = instance.head_.load();
next_ = head;
} while (instance.head_.exchange(this) != head);
std::uint32_t
++instance.count_;
}
~Counter() noexcept = default;
int
increment() noexcept
{
auto const newCount = count_.fetch_add(1, std::memory_order::relaxed) + 1;
XRPL_ASSERT(newCount != 0, "xrpl::CountedObjects::Counter::increment : no overflow");
auto maxCount = maxCount_.load(std::memory_order::relaxed);
while (newCount > maxCount &&
!maxCount_.compare_exchange_weak(maxCount, newCount, std::memory_order::relaxed))
{
}
return newCount;
return ++count_;
}
std::uint32_t
int
decrement() noexcept
{
auto const prev = count_.fetch_sub(1, std::memory_order::relaxed);
XRPL_ASSERT(prev != 0, "xrpl::CountedObjects::Counter::decrement : no underflow");
return prev - 1;
return --count_;
}
[[nodiscard]] std::uint32_t
count() const noexcept
[[nodiscard]] int
getCount() const noexcept
{
return count_.load(std::memory_order::relaxed);
return count_.load();
}
[[nodiscard]] std::uint32_t
max() const noexcept
[[nodiscard]] Counter*
getNext() const noexcept
{
return std::max(
count_.load(std::memory_order::relaxed),
maxCount_.load(std::memory_order::relaxed));
return next_;
}
[[nodiscard]] std::string const&
name() const noexcept
getName() const noexcept
{
return name_;
}
private:
friend class CountedObjects;
Counter* next_ = nullptr;
std::atomic<std::uint32_t> count_ = 0;
std::atomic<std::uint32_t> maxCount_ = 0;
std::string const name_;
std::atomic<int> count_;
Counter* next_;
};
class Iterator
{
public:
using value_type = Counter const;
using reference = value_type&;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
explicit Iterator(Counter* c = nullptr) noexcept : current_(c)
{
}
reference
operator*() const noexcept
{
return *current_;
}
pointer
operator->() const noexcept
{
return current_;
}
Iterator&
operator++() noexcept
{
current_ = current_->next_;
return *this;
}
Iterator
operator++(int) noexcept
{
auto tmp = *this;
++*this;
return tmp;
}
bool
operator==(Iterator const&) const noexcept = default;
private:
Counter* current_;
};
constexpr CountedObjects() noexcept = default;
[[nodiscard]] auto
begin() const noexcept
{
return Iterator{head_.load(std::memory_order::acquire)};
}
[[nodiscard]] auto
end() const noexcept
{
return Iterator{};
}
private:
std::atomic<Counter*> head_ = nullptr;
CountedObjects() noexcept;
~CountedObjects() noexcept = default;
private:
std::atomic<int> count_;
std::atomic<Counter*> head_;
};
/** The global counted object registry. */
inline constinit CountedObjects gCountedObjects;
inline CountedObjects::Counter::Counter(std::string name) noexcept
: next_(gCountedObjects.head_.load(std::memory_order::relaxed)), name_(std::move(name))
{
while (!gCountedObjects.head_.compare_exchange_weak(
next_, this, std::memory_order::release, std::memory_order::relaxed))
;
}
//------------------------------------------------------------------------------
/**
@@ -184,37 +103,27 @@ inline CountedObjects::Counter::Counter(std::string name) noexcept
* Derived classes have their instances counted automatically. This is used
* for reporting purposes.
*
* The constructors are private and `Object` is befriended so that the
* CRTP parameter must be the deriving class itself: a copy-paste error
* like `class B : public CountedObject<A>` fails to compile instead of
* silently polluting A's count.
*
* @note This class has no move operations by design: a derived class's
* move constructor falls back to the copy constructor for this
* base, so the newly created instance is counted. This keeps the
* invariant that count is the number of outstanding subobjects.
*
* @warning Counted objects constructed during dynamic initialization of
* other translation units may have their increments discarded when
* counter itself is dynamically initialized. Do not create counted
* objects before main() begins.
*
* @ingroup basics
*/
template <class Object>
requires std::is_class_v<Object>
class CountedObject
{
static inline CountedObjects::Counter counter{boost::core::type_name<Object>()};
private:
static auto&
getCounter() noexcept
{
static CountedObjects::Counter kC{beast::typeName<Object>()};
return kC;
}
CountedObject() noexcept
{
counter.increment();
getCounter().increment();
}
CountedObject(CountedObject const&) noexcept
{
counter.increment();
getCounter().increment();
}
CountedObject&
@@ -223,7 +132,7 @@ class CountedObject
public:
~CountedObject() noexcept
{
counter.decrement();
getCounter().decrement();
}
friend Object;
+2 -3
View File
@@ -3,11 +3,11 @@
#pragma once
#include <xrpl/basics/ByteUtilities.h>
#include <xrpl/beast/type_name.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <boost/align.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/core/type_name.hpp>
#include <boost/predef.h>
#include <algorithm>
@@ -16,7 +16,6 @@
#include <cstring>
#include <mutex>
#include <stdexcept>
#include <typeinfo>
#include <vector>
#if BOOST_OS_LINUX
@@ -336,7 +335,7 @@ public:
}) != cfg.end())
{
throw std::runtime_error(
"SlabAllocatorSet<" + boost::core::type_name<Type>() + ">: duplicate slab size");
"SlabAllocatorSet<" + beast::typeName<Type>() + ">: duplicate slab size");
}
for (auto const& c : cfg)
+2 -3
View File
@@ -1,8 +1,7 @@
#pragma once
#include <xrpl/basics/sanitizers.h>
#include <boost/core/type_name.hpp>
#include <xrpl/beast/type_name.h>
#include <exception>
#include <string>
@@ -56,7 +55,7 @@ Throw(Args&&... args)
std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception.");
E e(std::forward<Args>(args)...);
logThrow("Throwing exception of type " + boost::core::type_name<E>() + ": " + e.what());
logThrow(std::string("Throwing exception of type " + beast::typeName<E>() + ": ") + e.what());
throw std::move(e);
}
-200
View File
@@ -1,200 +0,0 @@
#pragma once
#include <concepts>
#include <type_traits>
#include <utility>
namespace xrpl {
/**
* Opt-in bitwise operators for scoped enumerations.
*
* Provides `&`, `|`, `^`, `~` and the corresponding compound assignment forms
* for any scoped enumeration with an unsigned underlying type that opts in by
* specializing @ref OptIn:
*
* @code
* namespace xrpl {
*
* enum class MyFlags : std::uint32_t { a = 1, b = 2, c = 4 };
*
* template <>
* struct enum_bitops::OptIn<MyFlags> : std::true_type {};
*
* } // namespace xrpl
* @endcode
*
* The operators are constexpr and noexcept, and return the enumeration type.
*
* @par Where the specialization may be declared
* In namespace xrpl, or at global scope with full qualification, and after
* the enumeration is defined but before the operators are first used. This
* cannot be done in a nested namespace or at class scope; for enumerations
* nested in a class this means after the class definition.
*
* @par Where the operators are found
* The operators are declared in namespace xrpl, so argument-dependent lookup
* will find them only for enumerations declared in xrpl or nested in a class
* declared directly in xrpl. Enumerations in a nested namespace will only be
* found from code inside xrpl, and only if no enclosing scope declares an
* operator of the same name; elsewhere they require using-declarations.
*/
namespace enum_bitops {
/**
* Types that may be opted in to the bitwise operators.
*
* Satisfied by a scoped enumeration whose underlying type is an unsigned
* integer type.
*
* @tparam T The type to test.
*/
template <typename T>
concept Eligible = std::is_scoped_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>;
/**
* Opt-in switch for the bitwise operators.
*
* The primary template derives from std::false_type. Specialize it to derive
* from std::true_type to enable the operators for an @ref Eligible enumeration.
*
* @tparam T The scoped enumeration to opt in.
*/
template <Eligible T>
struct OptIn : std::false_type
{
};
/**
* Enumerations for which the bitwise operators are enabled.
*
* Satisfied when @p T satisfies @ref Eligible and @ref OptIn has been
* specialized for it to derive from std::true_type.
*
* @tparam T The type to test.
*/
template <typename T>
concept Candidate = Eligible<T> && OptIn<T>::value;
} // namespace enum_bitops
// These are declared in xrpl, not in enum_bitops, so that argument-dependent
// lookup finds them for enumerations whose associated namespace is xrpl.
/**
* @name Bitwise operators for opted-in scoped enumerations
*
* Each operator applies the corresponding built-in operator to the
* underlying values and converts the result back to the enumeration type.
* Both operands must have the same enumeration type; there is no implicit
* conversion to or from the underlying type.
*/
/** @{ */
/**
* Bitwise AND.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in both @p lhs and @p rhs.
*/
template <enum_bitops::Candidate T>
constexpr T
operator&(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) & std::to_underlying(rhs));
}
/**
* Bitwise OR.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in @p lhs, in @p rhs, or in both.
*/
template <enum_bitops::Candidate T>
constexpr T
operator|(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) | std::to_underlying(rhs));
}
/**
* Bitwise exclusive OR.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in exactly one of @p lhs and @p rhs.
*/
template <enum_bitops::Candidate T>
constexpr T
operator^(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) ^ std::to_underlying(rhs));
}
/**
* Bitwise complement.
*
* The result has every bit of the underlying type that is clear in
* @p val, including bits that no enumerator names. It is intended for
* clearing flags, as in `flags & ~flag`.
*
* @param val The operand.
* @return The complement of @p val.
*/
template <enum_bitops::Candidate T>
constexpr T
operator~(T val) noexcept
{
return static_cast<T>(~std::to_underlying(val));
}
/**
* Bitwise AND assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator&=(T& lhs, T rhs) noexcept
{
lhs = lhs & rhs;
return lhs;
}
/**
* Bitwise OR assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator|=(T& lhs, T rhs) noexcept
{
lhs = lhs | rhs;
return lhs;
}
/**
* Bitwise exclusive OR assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator^=(T& lhs, T rhs) noexcept
{
lhs = lhs ^ rhs;
return lhs;
}
/** @} */
} // namespace xrpl
+46 -151
View File
@@ -2,212 +2,107 @@
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
#include <concepts>
#include <cstdint>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
namespace xrpl {
/** Every value of @p Src can be represented by @p Dest.
// safe_cast adds compile-time checks to a static_cast to ensure that
// the destination can hold all values of the source. This is particularly
// handy when the source or destination is an enumeration type.
Given two integral types, the cast is safe when the destination can
hold every possible value of the source.
template <class Src, class Dest>
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
(std::is_signed_v<Src> || std::is_unsigned_v<Dest>) &&
(std::is_signed_v<Src> != std::is_signed_v<Dest> ? sizeof(Dest) > sizeof(Src)
: sizeof(Dest) >= sizeof(Src));
Comparing the bounds requires care: we use @c std::cmp_less_equal and
@c std::cmp_greater_equal; the plain relational operators would apply
arithmetic conversions, resulting in incorrect results when comparing
across signedness.
Because @c std::cmp_* requires standard signed or unsigned integer
arguments, which excludes character types and bool, we first widen
all type bounds to the maximum-width integer type while preserving
signedness.
@note Extended integer types, like __int128 on gcc, cannot be safely
widened to a standard integer type, so the concept will reject
them.
*/
template <typename Src, typename Dest>
concept SafeToCast = std::is_integral_v<Src> && std::is_integral_v<Dest> && []() consteval {
using WideSrc = std::conditional_t<std::is_signed_v<Src>, std::intmax_t, std::uintmax_t>;
using WideDest = std::conditional_t<std::is_signed_v<Dest>, std::intmax_t, std::uintmax_t>;
// Note: this guard must be an evaluated branch, and not a
// static_assert. The lambda body is outside the immediate
// context, so a substitution-time failure here would be a
// hard error rather than leaving the concept unsatisfied.
if constexpr (sizeof(Src) > sizeof(WideSrc) || sizeof(Dest) > sizeof(WideDest))
{
return false;
}
else
{
return std::cmp_less_equal(
static_cast<WideDest>(std::numeric_limits<Dest>::min()),
static_cast<WideSrc>(std::numeric_limits<Src>::min())) &&
std::cmp_greater_equal(
static_cast<WideDest>(std::numeric_limits<Dest>::max()),
static_cast<WideSrc>(std::numeric_limits<Src>::max()));
}
}();
/** Compile-time-checked static_cast that rejects non-value preserving casts.
@note There is deliberately no enum-to-enum overload, and no overload
returning the underlying type of an enum. For the latter, use
@c std::to_underlying.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{
static_assert(
SafeToCast<Src, Dest>, "This cast is not value-preserving. Please use unsafeCast instead.");
std::is_signed_v<Dest> || std::is_unsigned_v<Src>, "Cannot cast signed to unsigned");
constexpr unsigned kNotSame = std::is_signed_v<Dest> != std::is_signed_v<Src>;
static_assert(
sizeof(Dest) >= sizeof(Src) + kNotSame,
"Destination is too small to hold all values of source");
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
{
return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
{
return safeCast<Dest>(std::to_underlying(s));
return safeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
/** @} */
/** Integral-to-integral cast that is known to be narrowing or sign-erasing.
// unsafe_cast explicitly flags a static_cast as not necessarily able to hold
// all values of the source. It includes a compile-time check so that if
// underlying types become safe, it can be converted to a safe_cast.
This explicitly flags a conversion that can lose information for some
values of the source type, where the call site accepts that loss (or
truncation is the intended behavior).
The compile-time check ensures the cast remains "unsafe": if the types
involved later change such that the conversion becomes inherently
value-preserving, the static assertion fires with instructions to
migrate the call site to @ref safeCast.
If the conversion's safety depends on a runtime precondition rather
than on the types, or varies across instantiations of generic code,
use @ref checkedCast instead.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{
static_assert(
!SafeToCast<Src, Dest>, "This cast is value-preserving. Please use safeCast instead.");
!SafeToCast<Src, Dest>,
"Only unsafe if casting signed to unsigned or "
"destination is too small");
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
{
return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
{
return unsafeCast<Dest>(std::to_underlying(s));
}
/** @} */
/** Integral-to-integral cast when the caller has performed a bounds check.
This documents that a runtime precondition or external invariant, which
is not necessarily visible to the compiler, guarantees that the requested
conversion is value-preserving for the values that can actually occur.
This is primarily meant for generic code, where the same expression may
be value-preserving for one instantiation but not for another, making
both @ref safeCast and @ref unsafeCast unusable.
Unlike @ref safeCast and @ref unsafeCast, this imposes no static check
on the type relationship: the caller's claim is about runtime values,
not about types.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return static_cast<Dest>(checkedCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return checkedCast<Dest>(std::to_underlying(s));
return unsafeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
/** @} */
/** Downcast within a class hierarchy, verified in debug builds.
Performs a static_cast down a hierarchy, but in debug builds verifies
via dynamic_cast that the object's dynamic type actually permits the
downcast. Both build modes execute the same conversion; debug builds
merely add the check.
The pointer form passes null through unchanged, as dynamic_cast does.
@note The check requires @p Src to be polymorphic; in release builds
an invalid downcast is undefined behavior on use, exactly as
with a bare static_cast.
*/
/** @{ */
template <class Dest, class Src>
requires(
std::is_pointer_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_pointer_t<Dest>, Src> &&
(std::is_const_v<std::remove_pointer_t<Dest>> || !std::is_const_v<Src>) &&
(std::is_volatile_v<std::remove_pointer_t<Dest>> || !std::is_volatile_v<Src>))
requires std::is_pointer_v<Dest>
inline Dest
safeDowncast(Src* s) noexcept
{
XRPL_ASSERT(
s == nullptr || dynamic_cast<Dest>(s) != nullptr, "xrpl::safeDowncast : valid downcast");
#ifdef NDEBUG
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
#else
auto* result = dynamic_cast<Dest>(s);
XRPL_ASSERT(result != nullptr, "xrpl::safeDowncast : pointer downcast is valid");
return result;
#endif
}
template <class Dest, class Src>
requires(
std::is_lvalue_reference_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_reference_t<Dest>, Src>)
requires std::is_lvalue_reference_v<Dest>
inline Dest
safeDowncast(Src& s) noexcept
{
return *safeDowncast<std::add_pointer_t<std::remove_reference_t<Dest>>>(std::addressof(s));
#ifndef NDEBUG
XRPL_ASSERT(
dynamic_cast<std::add_pointer_t<std::remove_reference_t<Dest>>>(&s) != nullptr,
"xrpl::safeDowncast : reference downcast is valid");
#endif
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
}
/** @} */
} // namespace xrpl
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <cstdlib>
#include <string>
#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
#include <cxxabi.h>
#endif
namespace beast {
template <typename T>
std::string
typeName()
{
using TR = std::remove_reference_t<T>;
std::string name = typeid(TR).name();
#ifndef _MSC_VER
if (auto s = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr))
{
name = s;
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
std::free(s);
}
#endif
if (std::is_const_v<TR>)
name += " const";
if (std::is_volatile_v<TR>)
name += " volatile";
if (std::is_lvalue_reference_v<T>)
{
name += "&";
}
else if (std::is_rvalue_reference_v<T>)
{
name += "&&";
}
return name;
}
} // namespace beast
+109 -36
View File
@@ -2,24 +2,26 @@
#pragma once
#include <compare>
#include <concepts>
namespace beast {
/**
* Zero allows classes to offer efficient comparisons to zero.
*
* Zero is a struct to allow classes to efficiently compare with zero without
* requiring an rvalue construction.
*
* It's often the case that we have classes which combine a number and a unit.
* In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
* like t > 1 or t != 1 do not. Comparing against kZero expresses exactly that,
* without constructing a T.
* like t > 1 or t != 1 do not.
*
* A type T participates if either `t.signum()` or an unqualified `signum(t)`
* found by argument-dependent lookup returns an integer that is negative,
* zero, or positive according to the sign of t. Both `t == kZero` and
* `kZero == t` work, as do all six relational operators in either order.
* The class Zero allows such comparisons to be easily made.
*
* The comparing class T either needs to have a method called signum() which
* returns a positive number, 0, or a negative; or there needs to be a signum
* function which resolves in the namespace which takes an instance of T and
* returns a positive, zero or negative number.
*/
struct Zero
{
explicit Zero() = default;
@@ -28,44 +30,115 @@ struct Zero
inline constexpr Zero kZero{};
/**
* Default implementation of signum: call the member function.
* Default implementation of signum calls the method on the class.
*/
template <class T>
requires requires(T const& t) {
{ t.signum() } -> std::integral;
}
[[nodiscard]] constexpr auto
signum(T const& t) noexcept(noexcept(t.signum()))
template <typename T>
auto
signum(T const& t)
{
return t.signum();
}
namespace detail {
namespace detail::zero_helper {
/**
* A type with a usable signum: either the member-based default above, or a
* `signum(t)` overload in T's own namespace, found by ADL. A user overload
* that is a better match than the template wins, as usual.
*/
// For argument dependent lookup to function properly, calls to signum must
// be made from a namespace that does not include overloads of the function..
template <class T>
concept HasSignum = requires(T const& t) {
{ signum(t) } -> std::integral;
};
} // namespace detail
template <detail::HasSignum T>
[[nodiscard]] constexpr bool
operator==(T const& t, Zero) noexcept(noexcept(signum(t)))
auto
callSignum(T const& t)
{
return signum(t) == 0;
return signum(t);
}
template <detail::HasSignum T>
[[nodiscard]] constexpr std::strong_ordering
operator<=>(T const& t, Zero) noexcept(noexcept(signum(t)))
} // namespace detail::zero_helper
// Handle operators where T is on the left side using signum.
template <typename T>
bool
operator==(T const& t, Zero)
{
return signum(t) <=> 0;
return detail::zero_helper::callSignum(t) == 0;
}
template <typename T>
bool
operator!=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) != 0;
}
template <typename T>
bool
operator<(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) < 0;
}
template <typename T>
bool
operator>(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) > 0;
}
template <typename T>
bool
operator>=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) >= 0;
}
template <typename T>
bool
operator<=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) <= 0;
}
// Handle operators where T is on the right side by
// reversing the operation, so that T is on the left side.
template <typename T>
bool
operator==(Zero, T const& t)
{
return t == kZero;
}
template <typename T>
bool
operator!=(Zero, T const& t)
{
return t != kZero;
}
template <typename T>
bool
operator<(Zero, T const& t)
{
return t > kZero;
}
template <typename T>
bool
operator>(Zero, T const& t)
{
return t < kZero;
}
template <typename T>
bool
operator>=(Zero, T const& t)
{
return t <= kZero;
}
template <typename T>
bool
operator<=(Zero, T const& t)
{
return t >= kZero;
}
} // namespace beast
+53 -12
View File
@@ -1,7 +1,7 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/enum_bitops.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/ReadView.h>
@@ -22,39 +22,80 @@
namespace xrpl {
enum class ApplyFlags : std::uint32_t {
None = 0x00,
// Bitwise flag enum with existing operator overloads
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum ApplyFlags : std::uint32_t {
TapNone = 0x00,
// This is a local transaction with the
// fail_hard flag set.
FailHard = 0x10,
TapFailHard = 0x10,
// This is not the transaction's last pass
// Transaction can be retried, soft failures allowed
Retry = 0x20,
TapRetry = 0x20,
// Transaction came from a privileged source
Unlimited = 0x400,
TapUnlimited = 0x400,
// Transaction is executing as part of a batch
Batch = 0x800,
TapBatch = 0x800,
// Transaction shouldn't be applied
// Signatures shouldn't be checked
DryRun = 0x1000,
TapDryRun = 0x1000,
// Transaction is being preflighted as the payload of a
// TransactionProposalCreate. Its signatures are collected on-ledger
// afterward, so signature-presence checks (e.g. Batch signer matching)
// are skipped at proposal-creation time (On-Chain Cosigner spec
// §5.3.1.2).
Proposal = 0x2000
TapProposal = 0x2000
};
template <>
struct enum_bitops::OptIn<ApplyFlags> : std::true_type
constexpr ApplyFlags
operator|(ApplyFlags const& lhs, ApplyFlags const& rhs)
{
};
return safeCast<ApplyFlags>(
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) |
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
}
static_assert((TapFailHard | TapRetry) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
static_assert((TapRetry | TapFailHard) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
constexpr ApplyFlags
operator&(ApplyFlags const& lhs, ApplyFlags const& rhs)
{
return safeCast<ApplyFlags>(
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) &
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
}
static_assert((TapFailHard & TapRetry) == TapNone, "ApplyFlags operator &");
static_assert((TapRetry & TapFailHard) == TapNone, "ApplyFlags operator &");
constexpr ApplyFlags
operator~(ApplyFlags const& flags)
{
return safeCast<ApplyFlags>(~safeCast<std::underlying_type_t<ApplyFlags>>(flags));
}
static_assert(~TapRetry == safeCast<ApplyFlags>(0xFFFFFFDFu), "ApplyFlags operator ~");
inline ApplyFlags
operator|=(ApplyFlags& lhs, ApplyFlags const& rhs)
{
lhs = lhs | rhs;
return lhs;
}
inline ApplyFlags
operator&=(ApplyFlags& lhs, ApplyFlags const& rhs)
{
lhs = lhs & rhs;
return lhs;
}
//------------------------------------------------------------------------------
+2 -1
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Keylet.h>
@@ -211,7 +212,7 @@ canWithdraw(
ReadView const& view,
AccountID const& from,
AccountID const& to,
SLE::ConstRef toSle,
AccountRootEntryR const& toSle,
STAmount const& amount,
bool hasDestinationTag,
std::optional<std::vector<UInt256>> const& credentialIDs = std::nullopt);
@@ -1,5 +1,6 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
@@ -7,9 +8,23 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <expected>
#include <optional>
namespace xrpl {
struct Adjustment
{
std::int32_t ownerCountDelta = 0;
std::int32_t accountCountDelta = 0;
};
template <typename ViewT>
class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT>
{
@@ -27,6 +42,223 @@ public:
: 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;
/**
* Returns true if and only if this entry is a pseudo-account of any kind
* (i.e. carries at least one field flagged with SField::kSmdPseudoAccount).
*
* Returns false if the entry:
* - is NOT a pseudo-account OR
* - is NOT a ltACCOUNT_ROOT OR
* - does not exist
*
* @return true if and only if this entry is a pseudo-account of any kind.
*/
[[nodiscard]] bool
isPseudoAccount() const;
/**
* Calculate the liquid XRP balance of this account.
*
* This is the amount of XRP that the holder is able to freely spend. It
* subtracts reserve requirements. Returns zero if the entry does not exist.
*
* ownerCountAdj adjusts the owner count in case the caller calculates
* before ledger entries are added or removed. Positive to add, negative
* to subtract.
*
* @param ownerCountAdj Positive to add to count, negative to reduce count
* @return The liquid XRP amount available to the account
*/
[[nodiscard]] XRPAmount
xrpLiquid(std::int32_t ownerCountAdj = 0) const;
/**
* Returns true if this account has the global freeze flag set.
*
* @return true if this account has the global freeze flag set.
*/
[[nodiscard]] bool
isGlobalFrozen() const
{
return (*this)->isFlag(lsfGlobalFreeze);
}
/**
* Returns this account's IOU transfer fee as Rate, or kParityRate if it
* sets none. Rate specifies the fee as fractions of 1 billion. For
* example, 1% transfer rate is represented as 1,010,000,000.
*
* @return This account's IOU transfer fee, or kParityRate if it sets none.
*/
[[nodiscard]] Rate
transferRate() const
{
if ((*this)->isFieldPresent(sfTransferRate))
return Rate{(*this)->getFieldU32(sfTransferRate)};
return kParityRate;
}
/**
* Checks the destination and tag.
*
* - Checks that the entry exists.
* - If the entry requires a destination tag, checks that there is a tag.
*
* @param hasDestinationTag Whether the transaction supplies a destination tag
* @return tecNO_DST if the entry does not exist; tecDST_TAG_NEEDED if the
* entry requires a destination tag and hasDestinationTag is false;
* tesSUCCESS otherwise
*/
[[nodiscard]] TER
checkDestinationAndTag(bool hasDestinationTag) const
{
if (!this->exists())
return tecNO_DST;
// The tag is basically account-specific information we don't
// understand, but we can require someone to fill it in.
if ((*this)->isFlag(lsfRequireDestTag) && !hasDestinationTag)
return tecDST_TAG_NEEDED; // Cannot send without a tag
return tesSUCCESS;
}
/**
* 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;
/**
* Create pseudo-account, storing pseudoOwnerKey into ownerField.
*
* The list of valid ownerField is maintained in AccountRootHelpers.cpp and
* the caller to this function must perform necessary amendment check(s)
* before using a field. The amendment check is **not** performed in
* createPseudoAccount.
*
* @param view The ledger view to create the pseudo-account in
* @param pseudoOwnerKey The key of the object that owns the pseudo-account,
* stored into ownerField
* @param ownerField The field linking the pseudo-account to its owner
* @return The newly created pseudo-account, or tecDUPLICATE if an account
* already exists at the derived pseudo-account address
*/
[[nodiscard]] static std::expected<AccountRootEntry<ApplyView>, TER>
createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const& ownerField)
requires Base::kIsWritable;
private:
void
adjustOwnerCountSigned(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::int32_t adjustment)
requires Base::kIsWritable;
};
using AccountRootEntryR = AccountRootEntry<ReadView>;
@@ -1,17 +1,11 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/SLEBase.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STVector256.h>
#include <cstddef>
#include <optional>
namespace xrpl {
@@ -31,31 +25,6 @@ public:
: Base(keylet::skip(), view, j)
{
}
/**
* Looks up a hash `diff` slots back from the most recent entry in the
* sfHashes vector (diff == 0 is the most recent entry).
*
* Callers decide which skip list entry to read and how to translate a
* target ledger sequence into `diff`; this only does the bounds check
* and vector indexing shared by both the recent (stride 1) and distant
* (stride 256) skip lists.
*
* @param diff how many slots back from the most recent hash to look up;
* 0 is the most recent hash.
* @return the hash at that slot, or std::nullopt if the entry does not
* exist or `diff` is out of range.
*/
[[nodiscard]] std::optional<UInt256>
hashAt(std::size_t diff) const
{
if (!this->exists())
return std::nullopt;
STVector256 const vec = (*this)->getFieldV256(sfHashes);
if (vec.size() > diff)
return vec[vec.size() - diff - 1];
return std::nullopt;
}
};
using LedgerHashesEntryR = LedgerHashesEntry<ReadView>;
+63 -198
View File
@@ -4,16 +4,14 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <expected>
#include <optional>
#include <vector>
@@ -43,33 +41,61 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer);
* @param j Journal for logging
* @return The liquid XRP amount available to the account
*/
[[nodiscard]] XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
struct Adjustment
[[nodiscard]] inline XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
{
std::int32_t ownerCountDelta = 0;
std::int32_t accountCountDelta = 0;
};
return AccountRootEntryR(id, view, j).xrpLiquid(ownerCountAdj);
}
namespace detail {
/**
* 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
* 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"
* Shared by AccountRootEntry::ownerCount() and the owner-count helpers.
*
* @param view The ledger view to read from
* @param sle The ledger entry for the account
* @param currentOwnerCount The owner count before adjustment
* @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 adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to
* subtract.
* @return The account reserve amount in drops
* @return The adjusted owner count
*/
[[nodiscard]] XRPAmount
accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjustment adj = {});
std::uint32_t
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.
@@ -84,114 +110,9 @@ accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjust
[[nodiscard]] inline XRPAmount
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.
*
@@ -209,36 +130,13 @@ increaseOwnerCount(
std::uint32_t count,
beast::Journal j)
{
increaseOwnerCount(
view,
view.peek(keylet::account(account)),
sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(),
count,
j);
AccountRootEntryW accountSle(account, view, j);
std::optional<AccountRootEntryW> sponsorSle;
if (sponsor)
sponsorSle.emplace(*sponsor, view);
accountSle.increaseOwnerCount(sponsorSle, count);
}
/**
* 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.
*
@@ -256,12 +154,11 @@ decreaseOwnerCount(
std::uint32_t count,
beast::Journal j)
{
decreaseOwnerCount(
view,
view.peek(keylet::account(account)),
sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(),
count,
j);
AccountRootEntryW accountSle(account, view, j);
std::optional<AccountRootEntryW> sponsorSle;
if (sponsor)
sponsorSle.emplace(*sponsor, view);
accountSle.decreaseOwnerCount(sponsorSle, count);
}
/**
@@ -281,7 +178,7 @@ decreaseOwnerCount(
void
decreaseOwnerCountForObject(
ApplyView& view,
SLE::Ref accountSle,
AccountRootEntryW& accountSle,
SLE::Ref objectSle,
std::uint32_t count,
beast::Journal j);
@@ -303,7 +200,7 @@ decreaseOwnerCountForObject(
std::uint32_t count,
beast::Journal j)
{
SLE::Ref accountSle = view.peek(keylet::account(account));
AccountRootEntryW accountSle(account, view);
decreaseOwnerCountForObject(view, accountSle, objectSle, count, j);
}
@@ -357,45 +254,13 @@ pseudoAccountAddress(ReadView const& view, UInt256 const& pseudoOwnerKey);
[[nodiscard]] std::vector<SField const*> const&
getPseudoAccountFields();
/**
* Returns true if and only if sleAcct is a pseudo-account of any kind
* (i.e. carries at least one field flagged with SField::kSmdPseudoAccount).
*
* Returns false if sleAcct is:
* - NOT a pseudo-account OR
* - NOT a ltACCOUNT_ROOT OR
* - null pointer
*/
[[nodiscard]] bool
isPseudoAccount(SLE::const_pointer sleAcct);
/**
* Convenience overload that reads the account from the view.
*/
[[nodiscard]] inline bool
isPseudoAccount(ReadView const& view, AccountID const& accountId)
{
return isPseudoAccount(view.read(keylet::account(accountId)));
return AccountRootEntryR(accountId, view).isPseudoAccount();
}
/**
* Create pseudo-account, storing pseudoOwnerKey into ownerField.
*
* The list of valid ownerField is maintained in AccountRootHelpers.cpp and
* the caller to this function must perform necessary amendment check(s)
* before using a field. The amendment check is **not** performed in
* createPseudoAccount.
*/
[[nodiscard]] std::expected<SLE::pointer, TER>
createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const& ownerField);
/**
* Checks the destination and tag.
*
* - Checks that the SLE is not null.
* - If the SLE requires a destination tag, checks that there is a tag.
*/
[[nodiscard]] TER
checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag);
} // namespace xrpl
+62 -31
View File
@@ -4,7 +4,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.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/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -27,6 +27,32 @@
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>
TER
escrowUnlockApplyHelper(
@@ -70,18 +96,17 @@ escrowUnlockApplyHelper<Issue>(
if (!ctx.view.exists(trustLineKey) && createAsset)
{
// 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)
return sponsorSle.error(); // LCOV_EXCL_LINE
if (auto const ret = checkReserve(
ctx,
sleDest,
xrpBalance,
*sponsorSle,
{.ownerCountDelta = 1},
journal,
tecNO_LINE_INSUF_RESERVE);
auto& sponsor = *sponsorSle;
if (auto const ret = destSle.checkReserve(
ctx, xrpBalance, sponsor, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE);
!isTesSuccess(ret))
{
JLOG(journal.trace()) << "Trust line does not exist. "
@@ -94,22 +119,22 @@ escrowUnlockApplyHelper<Issue>(
initialBalance.get<Issue>().account = noAccount();
if (TER const ter = trustCreate(
ctx.view, // payment sandbox
recvLow, // is dest low?
issuer, // source
receiver, // destination
trustLineKey.key, // ledger index
sleDest, // Account to add to
false, // authorize account
!sleDest->isFlag(lsfDefaultRipple), //
false, // freeze trust line
false, // deep freeze trust line
initialBalance, // zero initial balance
Issue(currency, receiver), // limit of zero
0, // quality in
0, // quality out
*sponsorSle, // sponsor
journal); // journal
ctx.view, // payment sandbox
recvLow, // is dest low?
issuer, // source
receiver, // destination
trustLineKey.key, // ledger index
sleDest, // Account to add to
false, // authorize account
!sleDest->isFlag(lsfDefaultRipple), //
false, // freeze trust line
false, // deep freeze trust line
initialBalance, // zero initial balance
Issue(currency, receiver), // limit of zero
0, // quality in
0, // quality out
sponsor ? sponsor->mutableRawSle() : SLE::pointer(), // sponsor
journal); // journal
!isTesSuccess(ter))
{
return ter; // LCOV_EXCL_LINE
@@ -204,23 +229,29 @@ escrowUnlockApplyHelper<MPTIssue>(
auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver);
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)
return sponsorSle.error(); // LCOV_EXCL_LINE
if (auto const ret = checkReserve(
ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal);
auto& sponsor = *sponsorSle;
if (auto const ret = destSle.checkReserve(ctx, xrpBalance, sponsor, {.ownerCountDelta = 1});
!isTesSuccess(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))
{
return ter; // LCOV_EXCL_LINE
}
// 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)
+12 -11
View File
@@ -2,6 +2,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/SField.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.
*
* @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
* be loaded (an already-checked invariant).
*/
std::expected<SLE::pointer, TER>
std::expected<std::optional<AccountRootEntryW>, TER>
getTxReserveSponsor(ApplyViewContext ctx);
/**
@@ -66,29 +67,29 @@ getTxReserveSponsor(ApplyViewContext ctx);
*
* @param view The ledger read view
* @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
* 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);
/**
* The transaction's reserve sponsor for the given account, if applicable.
*
* 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
* (non-pseudo) account; otherwise it returns a null sponsor pointer. This is
* this returns the tx reserve sponsor entry only when accountSle is the tx's
* own (non-pseudo) account; otherwise it returns nullopt. This is
* the single source of truth for the "sponsor applies to tx.Account only" rule
* that the sponsor-deriving helper overloads in AccountRootHelpers rely on.
*
* @param ctx The apply-view context (view + tx)
* @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)
*/
[[nodiscard]] std::expected<SLE::pointer, TER>
getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle);
[[nodiscard]] std::expected<std::optional<AccountRootEntryW>, TER>
getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle);
/**
* 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 sle The ledger entry whose sponsor field is inspected
* @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(
ApplyView& view,
SLE::ConstRef sle,
+16
View File
@@ -101,6 +101,22 @@ operator<<(std::ostream& os, AccountID const& x)
return os;
}
/**
* Initialize the global cache used to map AccountID to base58 conversions.
*
* The cache is optional and need not be initialized. But because conversion
* is expensive (it requires a SHA-256 operation) in most cases the overhead
* of the cache is worth the benefit.
*
* @param count The number of entries the cache should accommodate. Zero will
* disable the cache, releasing any memory associated with it.
*
* @note The function will only initialize the cache the first time it is
* invoked. Subsequent invocations do nothing.
*/
void
initAccountIdCache(std::size_t count);
} // namespace xrpl
//------------------------------------------------------------------------------
+2 -2
View File
@@ -1,10 +1,10 @@
#pragma once
#include <xrpl/basics/contract.h>
#include <xrpl/beast/type_name.h>
#include <xrpl/protocol/SOTemplate.h>
#include <boost/container/flat_map.hpp>
#include <boost/core/type_name.hpp>
#include <algorithm>
#include <cstddef>
@@ -84,7 +84,7 @@ public:
* Derived classes will load the object with all the known formats.
*/
private:
KnownFormats() : name_(boost::core::type_name<Derived>())
KnownFormats() : name_(beast::typeName<Derived>())
{
}
+51 -56
View File
@@ -2,7 +2,6 @@
#include <xrpl/basics/CountedObject.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/enum_bitops.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h>
@@ -16,7 +15,6 @@
#include <cstdint>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
@@ -24,8 +22,18 @@ namespace xrpl {
class STPathElement final : public CountedObject<STPathElement>
{
unsigned int type_;
AccountID accountID_;
PathAsset assetID_;
AccountID issuerID_;
bool isOffer_;
std::size_t hashValue_;
public:
enum class Type : std::uint8_t {
// Bitwise values (typeCurrency | typeMPT)
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum Type {
TypeNone = 0x00,
TypeAccount = 0x01, // Rippling through an account (vs taking an offer).
TypeCurrency = 0x10, // Currency follows.
@@ -37,8 +45,6 @@ public:
// Combination of all types.
};
using enum Type;
STPathElement();
STPathElement(STPathElement const&) = default;
STPathElement&
@@ -56,42 +62,42 @@ public:
bool forceAsset = false);
STPathElement(
Type type,
unsigned int uType,
AccountID const& account,
PathAsset const& asset,
AccountID const& issuer);
[[nodiscard]] Type
getNodeType() const noexcept;
[[nodiscard]] std::uint32_t
getNodeType() const;
[[nodiscard]] bool
isOffer() const noexcept;
isOffer() const;
[[nodiscard]] bool
isAccount() const noexcept;
isAccount() const;
[[nodiscard]] bool
hasIssuer() const noexcept;
hasIssuer() const;
[[nodiscard]] bool
hasCurrency() const noexcept;
hasCurrency() const;
[[nodiscard]] bool
hasMPT() const noexcept;
hasMPT() const;
[[nodiscard]] bool
hasAsset() const noexcept;
hasAsset() const;
[[nodiscard]] bool
isNone() const noexcept;
isNone() const;
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
// class of offers.
[[nodiscard]] AccountID const&
getAccountID() const noexcept;
getAccountID() const;
[[nodiscard]] PathAsset const&
getPathAsset() const noexcept;
getPathAsset() const;
[[nodiscard]] Currency const&
getCurrency() const;
@@ -100,35 +106,17 @@ public:
getMPTID() const;
[[nodiscard]] AccountID const&
getIssuerID() const noexcept;
getIssuerID() const;
[[nodiscard]] bool
isType(Type pe) const noexcept;
isType(Type const& pe) const;
friend bool
operator==(STPathElement const& lhs, STPathElement const& rhs) noexcept
{
return lhs.isType(TypeAccount) == rhs.isType(TypeAccount) &&
lhs.hashValue_ == rhs.hashValue_ && lhs.accountID_ == rhs.accountID_ &&
lhs.assetID_ == rhs.assetID_ && lhs.issuerID_ == rhs.issuerID_;
}
bool
operator==(STPathElement const& t) const;
private:
static std::size_t
getHash(STPathElement const& element);
Type type_;
AccountID accountID_;
PathAsset assetID_;
AccountID issuerID_;
bool isOffer_;
std::size_t hashValue_;
};
template <>
struct enum_bitops::OptIn<STPathElement::Type> : std::true_type
{
};
template <class Hasher>
@@ -136,7 +124,7 @@ void
hash_append(Hasher& h, STPathElement const& e) noexcept
{
using beast::hash_append;
hash_append(h, e.isType(STPathElement::TypeAccount));
hash_append(h, (e.getNodeType() & STPathElement::TypeAccount) != 0u);
hash_append(h, e.getAccountID());
hash_append(h, e.getPathAsset());
hash_append(h, e.getIssuerID());
@@ -416,11 +404,11 @@ inline STPathElement::STPathElement(
}
inline STPathElement::STPathElement(
Type type,
unsigned int uType,
AccountID const& account,
PathAsset const& asset,
AccountID const& issuer)
: type_(type)
: type_(uType)
, accountID_(account)
, assetID_(asset)
, issuerID_(issuer)
@@ -435,56 +423,56 @@ inline STPathElement::STPathElement(
hashValue_ = getHash(*this);
}
inline STPathElement::Type
STPathElement::getNodeType() const noexcept
inline std::uint32_t
STPathElement::getNodeType() const
{
return type_;
}
inline bool
STPathElement::isOffer() const noexcept
STPathElement::isOffer() const
{
return isOffer_;
}
inline bool
STPathElement::isAccount() const noexcept
STPathElement::isAccount() const
{
return !isOffer();
}
inline bool
STPathElement::isType(Type pe) const noexcept
STPathElement::isType(Type const& pe) const
{
return (type_ & pe) != STPathElement::TypeNone;
return (type_ & pe) != 0u;
}
inline bool
STPathElement::hasIssuer() const noexcept
STPathElement::hasIssuer() const
{
return isType(STPathElement::TypeIssuer);
}
inline bool
STPathElement::hasCurrency() const noexcept
STPathElement::hasCurrency() const
{
return isType(STPathElement::TypeCurrency);
}
inline bool
STPathElement::hasMPT() const noexcept
STPathElement::hasMPT() const
{
return isType(STPathElement::TypeMpt);
}
inline bool
STPathElement::hasAsset() const noexcept
STPathElement::hasAsset() const
{
return isType(STPathElement::TypeAsset);
}
inline bool
STPathElement::isNone() const noexcept
STPathElement::isNone() const
{
return getNodeType() == STPathElement::TypeNone;
}
@@ -492,13 +480,13 @@ STPathElement::isNone() const noexcept
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
// class of offers.
inline AccountID const&
STPathElement::getAccountID() const noexcept
STPathElement::getAccountID() const
{
return accountID_;
}
inline PathAsset const&
STPathElement::getPathAsset() const noexcept
STPathElement::getPathAsset() const
{
return assetID_;
}
@@ -516,11 +504,18 @@ STPathElement::getMPTID() const
}
inline AccountID const&
STPathElement::getIssuerID() const noexcept
STPathElement::getIssuerID() const
{
return issuerID_;
}
inline bool
STPathElement::operator==(STPathElement const& t) const
{
return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hashValue_ == t.hashValue_ &&
accountID_ == t.accountID_ && assetID_ == t.assetID_ && issuerID_ == t.issuerID_;
}
// ------------ STPath ------------
inline STPath::STPath(std::vector<STPathElement> p) : path_(std::move(p))
-1
View File
@@ -392,7 +392,6 @@ JSS(max_ledger); // in/out: LedgerCleaner
JSS(max_queue_size); // out: TxQ
JSS(max_spend_drops); // out: AccountInfo
JSS(max_spend_drops_total); // out: AccountInfo
JSS(maximum);
JSS(mean); // out: get_aggregate_price
JSS(median); // out: get_aggregate_price
JSS(median_fee); // out: TxQ
+1 -2
View File
@@ -51,8 +51,7 @@ public:
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()})
: ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal)
{
XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
}
std::reference_wrapper<ServiceRegistry> registry;
+8 -11
View File
@@ -59,11 +59,10 @@ public:
, parentBatchId(parentBatchId)
, j(j)
{
XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::Batch, "Batch apply flag should be set");
XRPL_ASSERT((flags & TapBatch) == TapBatch, "Batch apply flag should be set");
XRPL_ASSERT_IF(
(flags & ApplyFlags::Proposal) != ApplyFlags::None,
(flags & ApplyFlags::DryRun) != ApplyFlags::None,
(flags & TapProposal) != TapNone,
(flags & TapDryRun) != TapNone,
"xrpl::PreflightContext : proposal preflight implies dry run");
}
@@ -75,11 +74,10 @@ public:
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: registry(registry), tx(tx), rules(std::move(rules)), flags(flags), j(j)
{
XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
XRPL_ASSERT_IF(
(flags & ApplyFlags::Proposal) != ApplyFlags::None,
(flags & ApplyFlags::DryRun) != ApplyFlags::None,
(flags & TapProposal) != TapNone,
(flags & TapDryRun) != TapNone,
"xrpl::PreflightContext : proposal preflight implies dry run");
}
@@ -118,7 +116,7 @@ public:
, j(j)
{
XRPL_ASSERT(
parentBatchId.has_value() == ((flags & ApplyFlags::Batch) == ApplyFlags::Batch),
parentBatchId.has_value() == ((flags & TapBatch) == TapBatch),
"Parent Batch ID should be set if batch apply flag is set");
}
@@ -131,8 +129,7 @@ public:
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: PreclaimContext(registry, view, preflightResult, tx, flags, std::nullopt, j)
{
XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
}
PreclaimContext&
+1 -1
View File
@@ -41,7 +41,7 @@ struct ApplyResult
inline bool
isTecClaimHardFail(TER ter, ApplyFlags flags)
{
return isTecClaim(ter) && ((flags & ApplyFlags::Retry) == ApplyFlags::None);
return isTecClaim(ter) && ((flags & TapRetry) == 0u);
}
/**
@@ -27,6 +27,18 @@
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
/**
* @brief Prototype for invariant check implementations.
+3 -2
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -25,7 +26,7 @@ checkFreeze(
XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts");
// check freeze
if (auto sle = view.read(keylet::account(dst)))
if (auto sle = AccountRootEntryR(dst, view))
{
if (sle->isFlag(lsfGlobalFreeze))
{
@@ -49,7 +50,7 @@ checkFreeze(
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))
{
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
@@ -2,6 +2,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -12,6 +13,7 @@
#include <xrpl/tx/Transactor.h>
#include <cstdint>
#include <optional>
namespace xrpl {
@@ -56,8 +58,8 @@ private:
Keylet const& sponsorshipKeylet,
AccountID const& sponsorID,
AccountID const& sponseeID,
SLE::Ref sponsorAccSle,
SLE::Ref reserveSponsorAccSle);
AccountRootEntryW& sponsorAccSle,
std::optional<AccountRootEntryW>& reserveSponsorAccSle);
};
} // namespace xrpl
+73 -73
View File
@@ -2,144 +2,144 @@ Detected OS: macos (Darwin arm64)
Core build tools:
✅ cmake
cmake version 4.4.3
/nix/store/q85csxf4s4shx89zif097h1ql4ax1rrz-cmake-4.4.3/bin/cmake
cmake version 4.1.2
/nix/store/gvabsb4yqb5xsqzqph54rijnn4zpihnp-cmake-4.1.2/bin/cmake
✅ conan
Conan version 2.32.0
/nix/store/921jqsgbilixmr3xchj95si0jl8pi13z-conan-2.32.0/bin/conan
Conan version 2.28.1
/nix/store/9jiyxmkpwmn6dcqs0765s83riw3l5ail-conan-2.28.1/bin/conan
✅ git
git version 2.55.0
/nix/store/gw7c7m0dwca5lg4152x9lpmxpj171bzw-git-2.55.0/bin/git
git version 2.54.0
/nix/store/a14yxcqvv9x2l9mllgpirzhvz93pgprg-git-2.54.0/bin/git
✅ python3
Python 3.14.7
/nix/store/0kcws5blwbdyxqxjd1w8hjipz61xny3k-python3-3.14.7/bin/python3.14
Python 3.13.13
/nix/store/ygxqin6ydzjfawywqpp5pal8wv6sf5bh-python3-3.13.13/bin/python3.13
Development tooling:
✅ ccache
ccache version 4.13.6
/nix/store/ydr5nlzxp1djb5256y0zzqa87vz55gsk-ccache-4.13.6/bin/ccache
/nix/store/57davyvs6p6dkrl3svzwg1ph18wsy4cz-ccache-4.13.6/bin/ccache
✅ clang
clang version 22.1.8
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang
clang version 22.1.7
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang
✅ clang-22
clang version 22.1.8
/nix/store/w826sqb98nnym6kzackg829664iqdlxj-clang-22/bin/clang-22
clang version 22.1.7
/nix/store/rbap7zqq7mw00fyqa02p5rj7gqjp4w5i-clang-22/bin/clang-22
✅ clang++
clang version 22.1.8
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang++
clang version 22.1.7
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang++
✅ clang++-22
clang version 22.1.8
/nix/store/wnwyw3yfqrhpxij65bl3pa1x65q2zg3m-clang++-22/bin/clang++-22
clang version 22.1.7
/nix/store/v9haf787f7bcz0mq1sad4bpyx21pj6li-clang++-22/bin/clang++-22
✅ ClangBuildAnalyzer
ClangBuildAnalyzer 1.6.0
/nix/store/c7jjnw29ra78jdlzjww2m374izhy05n8-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
/nix/store/4l50ds9fa2mkvh7wg8qzrlbmjs12sb8l-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
✅ curl
curl 8.22.0 (aarch64-apple-darwin25.6.0) libcurl/8.22.0 OpenSSL/3.5.8 zlib/1.3.2 libssh2/1.11.1 nghttp2/1.70.0 mit-krb5/1.22.2
/nix/store/pq1h17abqy6q5y6k5dn9qj0fwb89flba-curl-8.22.0-bin/bin/curl
curl 8.20.0 (aarch64-apple-darwin25.3.0) libcurl/8.20.0 OpenSSL/3.6.2 zlib/1.3.2 libssh2/1.11.1 nghttp2/1.69.0 mit-krb5/1.22.1
/nix/store/kclq0czaxvsgh4ym9ld7b6iwy50l1snk-curl-8.20.0-bin/bin/curl
✅ file
file-5.48
/nix/store/y44vpziwbzfm8020dzlqqbybyyy7lb6g-file-5.48/bin/file
file-5.47
/nix/store/dax63li7wwcbqxxkkgzc4g2rx7d4w86x-file-5.47/bin/file
✅ less
less 710 (PCRE2 regular expressions)
/nix/store/3yn3dx5fwg5i0inqqzwnxq7f6lwhpjqa-less-710/bin/less
less 692 (PCRE2 regular expressions)
/nix/store/lvr16y75r1pdxpdv0aph5ak2yd0hkvqm-less-692/bin/less
✅ make
GNU Make 4.4.1
/nix/store/8l4wgxmhdxfvc9g18y76bnxl1ghgcp7n-gnumake-4.4.1/bin/make
/nix/store/8wwiw8pwyhrkzyq28hqzxfl4z84lks81-gnumake-4.4.1/bin/make
✅ netstat
present
/nix/store/57fqvgdav8lrmzndkwm7qrgksl01yj23-network_cmds-741.100.2/bin/netstat
/nix/store/qsd1kzqb0ahrk433vmyl245gp623j19s-network_cmds-730.80.3/bin/netstat
✅ ninja
1.13.2
/nix/store/infm4vlx71q83bqz0k559w1gn4b0rq4i-ninja-1.13.2/bin/ninja
/nix/store/bqykhrblarkj4fl0hz2mf8ngwfv6x6bz-ninja-1.13.2/bin/ninja
✅ perl
v5.42.3
/nix/store/7rvr6qjfzk2ybzn1imjw6c6qq1n1a9cr-perl-5.42.3/bin/perl
v5.42.0
/nix/store/js13ri9fvm0ajk1fpd3acigys2a9whdv-perl-5.42.0/bin/perl
✅ pkg-config
0.29.2
/nix/store/nhgylvj98klqggy54h7gbc3b2ia4a00y-pkg-config-wrapper-0.29.2/bin/pkg-config
/nix/store/lzrwr375jqhhbca116kja96xf1md83l8-pkg-config-wrapper-0.29.2/bin/pkg-config
✅ vim
VIM - Vi IMproved 9.2 (2026 Feb 14, compiled Jan 01 1980 00:00:00)
/nix/store/akaa9kqm3b8rlkv61kvsqr2l2j70rvxb-vim-9.2.1001/bin/vim
/nix/store/6vbkykg92w603c0sw3mkk7p7mfaawbns-vim-9.2.0389/bin/vim
✅ zip
Zip 3.0
/nix/store/dvawr36npnad4xa6dxi2ss0fw44aa82r-zip-3.0/bin/zip
/nix/store/z6ph729vcakbvz3wh8ln1wk6mi06w487-zip-3.0/bin/zip
✅ clang-apply-replacements
clang-apply-replacements version 22.1.8
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-apply-replacements
clang-apply-replacements version 22.1.7
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-apply-replacements
✅ clang-apply-replacements-22
clang-apply-replacements version 22.1.8
/nix/store/w4bz034k4l0w719rrmbwnqcgyjbswy81-clang-apply-replacements-22/bin/clang-apply-replacements-22
clang-apply-replacements version 22.1.7
/nix/store/m3ii69rca4077lf4wlk7m3jcag1fs577-clang-apply-replacements-22/bin/clang-apply-replacements-22
✅ clang-format
clang-format version 22.1.8
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-format
clang-format version 22.1.7
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-format
✅ clang-format-22
clang-format version 22.1.8
/nix/store/w7z346l0r8y36b6lc5i7jzj3w0581l2y-clang-format-22/bin/clang-format-22
clang-format version 22.1.7
/nix/store/4fawqy6ngqcsqd2ygyyzm93q0xy3f5gs-clang-format-22/bin/clang-format-22
✅ clang-tidy
LLVM version 22.1.8
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-tidy
LLVM version 22.1.7
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-tidy
✅ clang-tidy-22
LLVM version 22.1.8
/nix/store/0lv25qd3ddgjm3ndn70q61jfr6krlkl7-clang-tidy-22/bin/clang-tidy-22
LLVM version 22.1.7
/nix/store/jqw4280saixaxxihwdba9ldm2fsm6dr3-clang-tidy-22/bin/clang-tidy-22
✅ dot
dot - graphviz version 15.1.1 (0)
/nix/store/mc99a6bpbk2waym2cnw6ifmxli4ndb4x-graphviz-15.1.1/bin/dot
dot - graphviz version 12.2.1 (0)
/nix/store/ijb4fbnqa6wzlpqnhb6q9knqpf7qqn5z-graphviz-12.2.1/bin/dot
✅ doxygen
1.17.0
/nix/store/kz43yaj0wg6fx6cbrcmi2442z1ramy0c-doxygen-1.17.0/bin/doxygen
1.16.1
/nix/store/kbryjdpq9jizjb0ws0nzbf2h2ymbdiwm-doxygen-1.16.1/bin/doxygen
✅ gcovr
gcovr 8.4
/nix/store/czj1vxsizrbba280gg22cys40qxkqjgr-python3.14-gcovr-8.4/bin/gcovr
/nix/store/wn8jiyh9p0bybs96s4163qp3k8vfmczx-python3.13-gcovr-8.4/bin/gcovr
✅ gh
gh version 2.102.0 (2026-09-30)
/nix/store/s8bahl6zssvry93ihivfmk1l6c58byrr-gh-2.102.0/bin/gh
gh version 2.94.0 (nixpkgs)
/nix/store/fhnpw0hs0gjms1ha6ap02jq7rx13gkbp-gh-2.94.0/bin/gh
✅ git-cliff
git-cliff 2.14.2
/nix/store/k4m5yk0qb7hfslwsq1zjajz1d94j12bj-git-cliff-2.14.2/bin/git-cliff
git-cliff 2.13.1
/nix/store/cy0wwhgxa7yvrz97zydbq6sqmixc90fq-git-cliff-2.13.1/bin/git-cliff
✅ git-lfs
git-lfs/3.8.0 (3.8.0; darwin arm64; go 1.26.8)
/nix/store/wzixjr3yb40a13m2q654hm3bdjn6sal3-git-lfs-3.8.0/bin/git-lfs
git-lfs/3.7.1 (3.7.1; darwin arm64; go 1.26.3)
/nix/store/k9r7zjfjplqa4d5s71cqvf2iv73jd9mc-git-lfs-3.7.1/bin/git-lfs
✅ gpg
gpg (GnuPG) 2.4.9
/nix/store/0ldfwnwx385i5kh7cyvxx4mkvgjhs5rh-gnupg-2.4.9/bin/gpg
/nix/store/cgh6iwzz5jgx9z5whka4vgj210i6npc6-gnupg-2.4.9/bin/gpg
✅ pre-commit
pre-commit 4.6.2
/nix/store/ivh54xypg9qvd3ha9lis0i8p4nkf8n7a-pre-commit-4.6.2/bin/pre-commit
pre-commit 4.5.1
/nix/store/z3cca68620w0w10f090szgzdnmh1waf2-pre-commit-4.5.1/bin/pre-commit
✅ run-clang-tidy
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
/nix/store/3xss1mm3mh1hzcmg182na6ki1p5rnxf1-run-clang-tidy/bin/run-clang-tidy
/nix/store/4x28x911z2f9y7adqlh3qspp4a16dig7-run-clang-tidy/bin/run-clang-tidy
✅ run-clang-tidy-22
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
/nix/store/w68r9z07hcq4fwbfyq3yvc7qxm8aqfbl-run-clang-tidy-22/bin/run-clang-tidy-22
/nix/store/x8iymrh76sk5q91ryg5pa7i32s6gfh34-run-clang-tidy-22/bin/run-clang-tidy-22
Rust toolchain:
✅ cargo
cargo 1.97.1 (c980f4866 2026-06-30)
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/cargo
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/cargo
✅ cargo-audit
cargo-audit-audit 0.22.2
/nix/store/7jgrwj557l91940aj0ag4i924ima1cqn-cargo-audit-0.22.2/bin/cargo-audit
cargo-audit-audit 0.22.1
/nix/store/snwkga2f5gyf404h7mmp9wriwxb8v65f-cargo-audit-0.22.1/bin/cargo-audit
✅ cargo-llvm-cov
cargo-llvm-cov 0.9.0
/nix/store/5q1h285z8zzkw2qxig9a0b1avcsk1cf0-cargo-llvm-cov-0.9.0/bin/cargo-llvm-cov
cargo-llvm-cov 0.8.5
/nix/store/fpiqdh91gwyxalqp409ynm0s0g086w7w-cargo-llvm-cov-0.8.5/bin/cargo-llvm-cov
✅ cargo-nextest
cargo-nextest 0.9.146
/nix/store/69v3hx3k597hcxd2zy7yad7fg0aw5yxd-cargo-nextest-0.9.146/bin/cargo-nextest
cargo-nextest 0.9.137
/nix/store/ylz7m947mhkgsp6i7611id3s3gcd58nq-cargo-nextest-0.9.137/bin/cargo-nextest
✅ clippy-driver
clippy 0.1.97 (8bab26f4f6 2026-07-14)
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/clippy-driver
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/clippy-driver
✅ rust-analyzer
rust-analyzer 1.97.1 (8bab26f4 2026-07-14)
/nix/store/30rbyk8gx7v53fxqhs4pp0fvxfjycspr-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
/nix/store/j6apc5pmd0giy15da9p650r8zklslmvi-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
✅ rust-nightly
rustc 1.101.0-nightly (282215592 2026-10-04)
/nix/store/a9552rf3pylcnkijc1z0hf1lcnv50djq-rust-nightly/bin/rust-nightly
rustc 1.99.0-nightly (87e5904f5 2026-07-20)
/nix/store/fqpjz4l0nsnji8b2pz57mnj0akbp6hcl-rust-nightly/bin/rust-nightly
✅ rustc
rustc 1.97.1 (8bab26f4f 2026-07-14)
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/rustc
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/rustc
✅ rustfmt
rustfmt 1.9.0-stable (8bab26f4f6 2026-07-14)
/nix/store/0jmdipyn8266j9kqx2xxm2qkxlrlrjfc-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
/nix/store/5ymwgr9jqjz7zzbmj0j5vqbwcd3kp0vm-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
+1 -2
View File
@@ -21,8 +21,7 @@ let
)
);
toolchain =
if pkgs.stdenv.hostPlatform.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
toolchain = if pkgs.stdenv.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
in
{
default = pkgs.buildEnv {
+6 -7
View File
@@ -20,16 +20,15 @@ let
# Custom-glibc stdenvs, matching the CI environment. darwin has no custom
# glibc, so there they fall back to the plain nixpkgs stdenvs.
customGccStdenv = if pkgs.stdenv.hostPlatform.isLinux then linux.gccStdenv else plainGccStdenv;
customClangStdenv =
if pkgs.stdenv.hostPlatform.isLinux then linux.clangStdenv else plainClangStdenv;
customGccStdenv = if pkgs.stdenv.isLinux then linux.gccStdenv else plainGccStdenv;
customClangStdenv = if pkgs.stdenv.isLinux then linux.clangStdenv else plainClangStdenv;
# gcov matching each gcc shell, so `-Dcoverage=ON` builds work in the shell.
plainGcov = mkGcov {
name = "plain";
cc = gccPackage.cc;
};
customGccGcov = if pkgs.stdenv.hostPlatform.isLinux then linux.gcov else plainGcov;
customGccGcov = if pkgs.stdenv.isLinux then linux.gcov else plainGcov;
# Whole directory: init.sh locates the profiles relative to itself.
conanDir = ../conan;
@@ -52,7 +51,7 @@ let
# Not sdkEnv: a shell's stdenv already sets that up. Prepended so the stub
# beats the nixpkgs libresolv this shell's tooling drags in.
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin (
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.isDarwin (
pkgs.lib.concatLines (
pkgs.lib.mapAttrsToList (
name: value: ''export ${name}="${value} ''${${name}:-}"''
@@ -127,7 +126,7 @@ let
in
rec {
# macOS: Nix Clang. Linux: Nix GCC.
default = if pkgs.stdenv.hostPlatform.isDarwin then clang else gcc;
default = if pkgs.stdenv.isDarwin then clang else gcc;
# gcc/clang use the custom-glibc toolchain, matching CI. On darwin there is no
# custom glibc, so they fall back to the plain nixpkgs toolchain.
@@ -172,7 +171,7 @@ rec {
# The *-plain shells (stock nixpkgs toolchain) exist only on Linux: on darwin
# gcc/clang are already plain, so these would be redundant and are omitted, which
# makes `nix develop .#gcc-plain` fail there rather than silently aliasing gcc.
// pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
// pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
gcc-plain = makeShell {
shellName = "gcc-plain";
stdenv = plainGccStdenv;
+39
View File
@@ -0,0 +1,39 @@
#include <xrpl/basics/CountedObject.h>
#include <algorithm>
namespace xrpl {
CountedObjects&
CountedObjects::getInstance() noexcept
{
static CountedObjects kInstance;
return kInstance;
}
CountedObjects::CountedObjects() noexcept : count_(0), head_(nullptr)
{
}
CountedObjects::List
CountedObjects::getCounts(int minimumThreshold) const
{
List counts;
// When other operations are concurrent, the count
// might be temporarily less than the actual count.
counts.reserve(count_.load());
for (auto* ctr = head_.load(); ctr != nullptr; ctr = ctr->getNext())
{
if (ctr->getCount() >= minimumThreshold)
counts.emplace_back(ctr->getName(), ctr->getCount());
}
std::ranges::sort(counts);
return counts;
}
} // namespace xrpl
+1 -2
View File
@@ -11,7 +11,6 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/LedgerTiming.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
#include <xrpl/nodestore/NodeObject.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Fees.h>
@@ -561,7 +560,7 @@ Ledger::setup()
try
{
if (auto const sle = FeeSettingsEntryR(*this))
if (auto const sle = read(keylet::feeSettings()))
{
bool oldFees = false;
bool newFees = false;
+29 -19
View File
@@ -9,8 +9,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/LedgerHashesEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -116,8 +115,8 @@ isVaultPseudoAccountFrozenForIssuance(
view, {issuer, account}, assetOfHolding(issuanceSle, *sleHolding), depth + 1);
}
auto const mptIssuer = view.read(keylet::account(issuer));
if (mptIssuer == nullptr)
auto const mptIssuer = AccountRootEntryR(issuer, view);
if (!mptIssuer.exists())
{
// LCOV_EXCL_START
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
@@ -190,7 +189,7 @@ canTransferLPToken(
{
// 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.
auto const sleIssuer = view.read(keylet::account(lpTokenIssuer));
auto const sleIssuer = AccountRootEntryR(lpTokenIssuer, view);
if (!sleIssuer || !sleIssuer->isFieldPresent(sfAMMID))
return tesSUCCESS;
@@ -362,16 +361,17 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
if (int const diff = ledger.seq() - seq; diff <= 256)
{
// Within 256...
LedgerHashesEntryR const hashIndex(ledger, journal);
auto const hashIndex = ledger.read(keylet::skip());
if (hashIndex)
{
XRPL_ASSERT(
hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.seq() - 1),
"xrpl::hashOfSeq : matching ledger sequence");
if (auto const hash = hashIndex.hashAt(diff - 1))
return hash;
STVector256 vec = hashIndex->getFieldV256(sfHashes);
if (vec.size() >= diff)
return vec[vec.size() - diff];
JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " ("
<< hashIndex->getFieldV256(sfHashes).size() << "," << diff << ")";
<< vec.size() << "," << diff << ")";
}
else
{
@@ -387,14 +387,16 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
}
// in skiplist
LedgerHashesEntryR const hashIndex(keylet::skip(seq), ledger, journal);
auto const hashIndex = ledger.read(keylet::skip(seq));
if (hashIndex)
{
auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
XRPL_ASSERT(lastSeq >= seq, "xrpl::hashOfSeq : minimum last ledger");
XRPL_ASSERT((lastSeq & 0xff) == 0, "xrpl::hashOfSeq : valid last ledger");
if (auto const hash = hashIndex.hashAt((lastSeq - seq) >> 8))
return hash;
auto const diff = (lastSeq - seq) >> 8;
STVector256 vec = hashIndex->getFieldV256(sfHashes);
if (vec.size() > diff)
return vec[vec.size() - diff - 1];
}
JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " error";
return std::nullopt;
@@ -464,12 +466,12 @@ canWithdraw(
ReadView const& view,
AccountID const& from,
AccountID const& to,
SLE::ConstRef toSle,
AccountRootEntryR const& toSle,
STAmount const& amount,
bool hasDestinationTag,
std::optional<std::vector<UInt256>> const& credentialIDs)
{
if (auto const ret = checkDestinationAndTag(toSle, hasDestinationTag))
if (auto const ret = toSle.checkDestinationAndTag(hasDestinationTag))
return ret;
if (from == to)
@@ -514,7 +516,7 @@ canWithdraw(
bool hasDestinationTag,
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);
}
@@ -539,7 +541,7 @@ doWithdraw(
STAmount const& amount,
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
// is a payout to credit. Post-fixCleanup3_4_0, a zero-value withdraw
@@ -557,7 +559,8 @@ doWithdraw(
}
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;
}
@@ -580,14 +583,21 @@ doWithdraw(
// sponsor against the destination. accountSend can auto-create a holding
// for dstAcct; keying on the destination ensures a third-party destination's
// holding is never stamped with the tx's reserve sponsor.
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle);
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle);
if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE
// Move the funds directly from the broker's pseudo-account to the
// dstAcct
auto& sponsor = *sponsorSle;
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
@@ -0,0 +1,422 @@
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Zero.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 <algorithm>
#include <cstdint>
#include <expected>
#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>
XRPAmount
AccountRootEntry<ViewT>::xrpLiquid(std::int32_t ownerCountAdj) const
{
if (!this->exists())
return beast::kZero;
auto const& view = this->readView();
auto const j = this->journal();
AccountID const id = (*this)->getAccountID(sfAccount);
// Return balance minus reserve
std::uint32_t const currentOwnerCount = detail::confineOwnerCount(
view.ownerCountHook(id, OwnerCounts(this->rawSle())).count(), ownerCountAdj);
std::uint32_t const currentAccountCount = detail::accountCountImpl(*this, 0, j);
// Pseudo-accounts have no reserve requirement
auto const reserve = isPseudoAccount()
? XRPAmount{0}
: view.fees().accountReserve(currentOwnerCount, currentAccountCount);
auto const fullBalance = (*this)->getFieldAmount(sfBalance);
auto const balance = view.balanceHookIOU(id, xrpAccount(), fullBalance);
STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve;
JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id)
<< " amount=" << amount.getFullText()
<< " fullBalance=" << fullBalance.getFullText()
<< " balance=" << balance.getFullText() << " reserve=" << reserve
<< " ownerCount=" << currentOwnerCount << " ownerCountAdj=" << ownerCountAdj;
return amount.xrp();
}
template <typename ViewT>
bool
AccountRootEntry<ViewT>::isPseudoAccount() const
{
// Intentionally use defensive coding here because it's cheap and makes the
// semantics of true return value clean.
return this->exists() && (*this)->getType() == ltACCOUNT_ROOT &&
std::ranges::any_of(getPseudoAccountFields(), [this](SField const* sf) {
return (*this)->isFieldPresent(*sf);
});
}
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 <typename ViewT>
std::expected<AccountRootEntry<ApplyView>, TER>
AccountRootEntry<ViewT>::createPseudoAccount(
ApplyView& view,
UInt256 const& pseudoOwnerKey,
SField const& ownerField)
requires Base::kIsWritable
{
[[maybe_unused]]
auto const& fields = getPseudoAccountFields();
XRPL_ASSERT(
std::count_if(
fields.begin(),
fields.end(),
[&ownerField](SField const* sf) -> bool { return *sf == ownerField; }) == 1,
"xrpl::createPseudoAccount : valid owner field");
auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey);
if (accountId == beast::kZero)
return std::unexpected(tecDUPLICATE);
// Create pseudo-account.
AccountRootEntry<ApplyView> account(accountId, view);
account.newSLE();
account->setAccountID(sfAccount, accountId);
account->setFieldAmount(sfBalance, STAmount{});
// Pseudo-accounts can't submit transactions, so set the sequence number
// to 0 to make them easier to spot and verify, and add an extra level
// of protection.
std::uint32_t const seqno = //
view.rules().enabled(featureSingleAssetVault) || //
view.rules().enabled(featureLendingProtocol) //
? 0 //
: view.seq();
account->setFieldU32(sfSequence, seqno);
// Ignore reserves requirement, disable the master key, allow default
// rippling, and enable deposit authorization to prevent payments into
// pseudo-account.
account->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
// Link the pseudo-account with its owner object.
account->setFieldH256(ownerField, pseudoOwnerKey);
account.insert();
return account;
}
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/Sandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -641,7 +642,7 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
[&](Issue const& issue) {
if (isXRP(issue))
{
if (auto const sle = view.read(keylet::account(ammAccountID)))
if (auto const sle = AccountRootEntryR(ammAccountID, view))
return (*sle)[sfBalance];
}
else if (
@@ -757,7 +758,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
}
auto const ammAccountID = (*ammSle)[sfAccount];
auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
auto sleAMMRoot = AccountRootEntryW(ammAccountID, sb);
if (!sleAMMRoot)
{
// LCOV_EXCL_START
@@ -795,7 +796,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
}
sb.erase(ammSle);
sb.erase(sleAMMRoot);
sleAMMRoot.erase();
return tesSUCCESS;
}
+17 -401
View File
@@ -7,24 +7,17 @@
#include <xrpl/beast/utility/Zero.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/entries/AccountRootEntry.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/Rate.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/protocol/digest.h>
#include <algorithm>
#include <cstdint>
#include <expected>
#include <limits>
#include <memory>
#include <optional>
@@ -38,25 +31,19 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer)
{
if (isXRP(issuer))
return false;
if (auto const sle = view.read(keylet::account(issuer)))
return sle->isFlag(lsfGlobalFreeze);
if (auto const sle = AccountRootEntryR(issuer, view))
return sle.isGlobalFrozen();
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
confineOwnerCount(
std::uint32_t currentOwnerCount,
std::int32_t ownerCountAdj,
std::optional<AccountID> const& id = std::nullopt,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
std::optional<AccountID> const& id,
beast::Journal j)
{
std::uint32_t totalOwnerCount{currentOwnerCount + ownerCountAdj};
if (ownerCountAdj > 0)
@@ -91,10 +78,8 @@ confineOwnerCount(
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
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);
std::int64_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount);
@@ -131,234 +116,29 @@ adjustOwnerCountImpl(
{
std::uint32_t const currentOwnerCount = sle->at(sfield);
std::uint32_t const totalOwnerCount =
confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j);
detail::confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j);
sle->at(sfield) = totalOwnerCount;
view.update(sle);
return totalOwnerCount;
}
void
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
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
{
auto const sle = view.read(keylet::account(id));
if (sle == nullptr)
return beast::kZero;
// Return balance minus reserve
std::uint32_t const currentOwnerCount =
confineOwnerCount(view.ownerCountHook(id, OwnerCounts(sle)).count(), ownerCountAdj);
std::uint32_t const currentAccountCount = accountCountImpl(sle, 0, j);
// Pseudo-accounts have no reserve requirement
auto const reserve = isPseudoAccount(sle)
? XRPAmount{0}
: view.fees().accountReserve(currentOwnerCount, currentAccountCount);
auto const fullBalance = sle->getFieldAmount(sfBalance);
auto const balance = view.balanceHookIOU(id, xrpAccount(), fullBalance);
STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve;
JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id)
<< " amount=" << amount.getFullText()
<< " fullBalance=" << fullBalance.getFullText()
<< " balance=" << balance.getFullText() << " reserve=" << reserve
<< " ownerCount=" << currentOwnerCount << " ownerCountAdj=" << ownerCountAdj;
return amount.xrp();
}
} // namespace detail
Rate
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))
return Rate{sle->getFieldU32(sfTransferRate)};
if (sle)
return sle.transferRate();
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
decreaseOwnerCountForObject(
ApplyView& view,
SLE::Ref accountSle,
AccountRootEntryW& accountSle,
SLE::Ref objectSle,
std::uint32_t count,
beast::Journal j)
@@ -372,8 +152,8 @@ decreaseOwnerCountForObject(
if (!validObjectType)
return; // LCOV_EXCL_LINE
SLE::Ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle);
decreaseOwnerCount(view, accountSle, sponsorSle, count, j);
auto sponsorSle = getLedgerEntryReserveSponsor(view, objectSle);
accountSle.decreaseOwnerCount(sponsorSle, count);
}
void
@@ -393,107 +173,10 @@ adjustLoanBrokerOwnerCount(
if (delta == 0)
return; // LCOV_EXCL_LINE
adjustOwnerCountImpl(
detail::adjustOwnerCountImpl(
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
@@ -507,7 +190,7 @@ pseudoAccountAddress(ReadView const& view, UInt256 const& pseudoOwnerKey)
auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey);
rsh(hash.data(), hash.size());
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 beast::kZero;
@@ -545,71 +228,4 @@ getPseudoAccountFields()
return kPseudoFields;
}
[[nodiscard]] bool
isPseudoAccount(SLE::const_pointer sleAcct)
{
// Intentionally use defensive coding here because it's cheap and makes the
// semantics of true return value clean.
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
std::ranges::any_of(getPseudoAccountFields(), [&sleAcct](SField const* sf) {
return sleAcct->isFieldPresent(*sf);
});
}
std::expected<SLE::pointer, TER>
createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const& ownerField)
{
[[maybe_unused]]
auto const& fields = getPseudoAccountFields();
XRPL_ASSERT(
std::count_if(
fields.begin(),
fields.end(),
[&ownerField](SField const* sf) -> bool { return *sf == ownerField; }) == 1,
"xrpl::createPseudoAccount : valid owner field");
auto const accountId = pseudoAccountAddress(view, pseudoOwnerKey);
if (accountId == beast::kZero)
return std::unexpected(tecDUPLICATE);
// Create pseudo-account.
auto account = std::make_shared<SLE>(keylet::account(accountId));
account->setAccountID(sfAccount, accountId);
account->setFieldAmount(sfBalance, STAmount{});
// Pseudo-accounts can't submit transactions, so set the sequence number
// to 0 to make them easier to spot and verify, and add an extra level
// of protection.
std::uint32_t const seqno = //
view.rules().enabled(featureSingleAssetVault) || //
view.rules().enabled(featureLendingProtocol) //
? 0 //
: view.seq();
account->setFieldU32(sfSequence, seqno);
// Ignore reserves requirement, disable the master key, allow default
// rippling, and enable deposit authorization to prevent payments into
// pseudo-account.
account->setFieldU32(sfFlags, lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth);
// Link the pseudo-account with its owner object.
account->setFieldH256(ownerField, pseudoOwnerKey);
view.insert(account);
return account;
}
[[nodiscard]] TER
checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag)
{
if (toSle == nullptr)
return tecNO_DST;
// The tag is basically account-specific information we don't
// understand, but we can require someone to fill it in.
if (toSle->isFlag(lsfRequireDestTag) && !hasDestinationTag)
return tecDST_TAG_NEEDED; // Cannot send without a tag
return tesSUCCESS;
}
} // namespace xrpl
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
@@ -81,7 +82,7 @@ deleteSLE(ApplyView& view, SLE::Ref sleCredential, beast::Journal j)
auto delSLE = [&view, &sleCredential, j](
AccountID const& account, SField const& node, bool isOwner) -> TER {
auto const sleAccount = view.peek(keylet::account(account));
auto sleAccount = AccountRootEntryW(account, view);
if (!sleAccount)
{
// LCOV_EXCL_START
+15 -11
View File
@@ -7,6 +7,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -206,7 +207,7 @@ authorizeMPToken(
std::uint32_t flags,
std::optional<AccountID> holderID)
{
auto const sleAcct = ctx.view.peek(keylet::account(account));
auto sleAcct = AccountRootEntryW(account, ctx.view, journal);
if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -242,10 +243,10 @@ authorizeMPToken(
// - create the MPToken object for the holder
// A reserve sponsor only covers tx.Account's own objects.
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct);
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct);
if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE
auto const sponsorSle = *sponsorExp;
auto& sponsorSle = *sponsorExp;
// The reserve that is required to create the MPToken. Note
// 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 tx — the sponsor must always cover the reserve (via balance or prefunded
// budget), so this check always runs for sponsored transactions.
if (sponsorSle || ownerCount(sleAcct, journal) >= 2)
if (sponsorSle || sleAcct.ownerCount() >= 2)
{
if (auto const ret = checkReserve(
ctx, sleAcct, priorBalance, sponsorSle, {.ownerCountDelta = 1}, journal);
if (auto const ret =
sleAcct.checkReserve(ctx, priorBalance, sponsorSle, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
}
@@ -285,8 +286,8 @@ authorizeMPToken(
ctx.view.insert(mptoken);
// Update owner count.
increaseOwnerCount(ctx.view, sleAcct, sponsorSle, 1, journal);
addSponsorToLedgerEntry(mptoken, sponsorSle);
sleAcct.increaseOwnerCount(sponsorSle, 1);
addSponsorToLedgerEntry(mptoken, sponsorSle ? sponsorSle->rawSle() : SLE::const_pointer{});
return tesSUCCESS;
}
@@ -415,7 +416,7 @@ requireAuth(
}
// 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)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -1020,13 +1021,16 @@ checkCreateMPT(
{
return err;
}
auto const sleAcct = view.peek(keylet::account(holder));
auto sleAcct = AccountRootEntryW(holder, view, j);
if (!sleAcct)
{
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;
}
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -855,7 +856,7 @@ tokenOfferCreatePreclaim(
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");
if (auto minter = (*root)[~sfNFTokenMinter]; minter != acctID)
@@ -886,7 +887,7 @@ tokenOfferCreatePreclaim(
{
// If a destination is specified, the destination must already be in
// the ledger.
auto const sleDst = view.read(keylet::account(*dest));
auto const sleDst = AccountRootEntryR(*dest, view);
if (!sleDst)
return tecNO_DST;
@@ -898,7 +899,7 @@ tokenOfferCreatePreclaim(
if (owner)
{
auto const sleOwner = view.read(keylet::account(*owner));
auto const sleOwner = AccountRootEntryR(*owner, view);
// defensively check
// it should not be possible to specify owner that doesn't exist
@@ -937,9 +938,8 @@ tokenOfferCreateApply(
beast::Journal j,
std::uint32_t txFlags)
{
Keylet const acctKeylet = keylet::account(acctID);
if (auto const acct = view.read(acctKeylet);
priorBalance < accountReserve(view, acct, j, {.ownerCountDelta = 1}))
if (auto const acct = AccountRootEntryW(acctID, view, j);
priorBalance < acct.reserve({.ownerCountDelta = 1}))
return tecINSUFFICIENT_RESERVE;
auto const offerID = keylet::nftokenOffer(acctID, seqProxy);
@@ -1008,7 +1008,7 @@ checkTrustlineAuthorized(
if (view.rules().enabled(fixEnforceNFTokenTrustlineV2))
{
auto const issuerAccount = view.read(keylet::account(issue.account));
auto const issuerAccount = AccountRootEntryR(issue.account, view);
if (!issuerAccount)
{
JLOG(j.debug()) << "xrpl::nft::checkTrustlineAuthorized: can't "
@@ -1060,7 +1060,7 @@ checkTrustlineDeepFrozen(
if (view.rules().enabled(featureDeepFreeze))
{
auto const issuerAccount = view.read(keylet::account(issue.account));
auto const issuerAccount = AccountRootEntryR(issue.account, view);
if (!issuerAccount)
{
JLOG(j.debug()) << "xrpl::nft::checkTrustlineDeepFrozen: can't "
@@ -6,6 +6,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.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
auto const sle = view.peek(keylet::account(src));
auto sle = AccountRootEntryW(src, view);
if (!sle)
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");
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
decreaseOwnerCountForObject(view, sle, slep, 1, j);
view.update(sle);
sle.update();
// Remove PayChan from ledger
view.erase(slep);
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -133,14 +134,14 @@ isFrozen(
{
if (isXRP(currency))
return false;
auto sle = view.read(keylet::account(issuer));
if (sle && sle->isFlag(lsfGlobalFreeze))
auto const sleIssuer = AccountRootEntryR(issuer, view);
if (sleIssuer && sleIssuer->isFlag(lsfGlobalFreeze))
return true;
if (issuer != account)
{
// Check if the issuer froze the line
sle = view.read(keylet::trustLine(account, issuer, currency));
if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
auto const sleLine = view.read(keylet::trustLine(account, issuer, currency));
if (sleLine && sleLine->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
return true;
}
return false;
@@ -238,7 +239,7 @@ trustCreate(
XRPL_ASSERT(
sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID),
"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)
return tecNO_TARGET;
@@ -283,7 +284,11 @@ trustCreate(
}
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);
@@ -349,7 +354,7 @@ updateTrustLine(
if (!state)
return false;
auto sle = view.peek(keylet::account(sender));
auto sle = AccountRootEntryW(sender, view, j);
if (!sle)
return false;
@@ -376,9 +381,9 @@ updateTrustLine(
{
// VFALCO Where is the line being deleted?
// Clear the reserve of the sender, possibly delete the line!
auto const currentSponsor =
auto currentSponsor =
getLedgerEntryReserveSponsor(view, state, bSenderHigh ? sfHighSponsor : sfLowSponsor);
decreaseOwnerCount(view, sle, currentSponsor, 1, j);
sle.decreaseOwnerCount(currentSponsor, 1);
// Clear reserve flag.
state->clearFlag(senderReserveFlag);
@@ -464,7 +469,7 @@ issueIOU(
finalBalance.get<Issue>().account = noAccount();
auto const receiverAccount = view.peek(keylet::account(account));
auto receiverAccount = AccountRootEntryW(account, view);
if (!receiverAccount)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -476,7 +481,7 @@ issueIOU(
issue.account,
account,
index.key,
receiverAccount,
receiverAccount.mutableRawSle(),
false,
noRipple,
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
// 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))
{
if (trustLine)
@@ -609,8 +614,8 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
auto const& issuerId = issue.getIssuer();
if (issuerId == from || issuerId == to)
return tesSUCCESS;
auto const sleIssuer = view.read(keylet::account(issuerId));
if (sleIssuer == nullptr)
auto const sleIssuer = AccountRootEntryR(issuerId, view);
if (!sleIssuer.exists())
return tefINTERNAL; // LCOV_EXCL_LINE
auto const isRippleDisabled = [&](AccountID account) -> bool {
@@ -665,8 +670,8 @@ addEmptyHolding(
if (isGlobalFrozen(ctx.view, issuerId))
return tecFROZEN; // LCOV_EXCL_LINE
auto const sleSrc = ctx.view.peek(keylet::account(srcId));
auto const sleDst = ctx.view.peek(keylet::account(dstId));
auto const sleSrc = AccountRootEntryW(srcId, ctx.view);
auto sleDst = AccountRootEntryW(dstId, ctx.view, journal);
if (!sleDst || !sleSrc)
return tefINTERNAL; // LCOV_EXCL_LINE
// Create path: DefaultRipple is still required. terNO_RIPPLE is
@@ -681,20 +686,14 @@ addEmptyHolding(
return tecDUPLICATE;
// A reserve sponsor only covers tx.Account's own objects.
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst);
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst);
if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE
auto const sponsorSle = *sponsorExp;
auto& sponsorSle = *sponsorExp;
// Can the account cover the trust line reserve ?
if (auto const ret = checkReserve(
ctx,
sleDst,
priorBalance,
sponsorSle,
{.ownerCountDelta = 1},
journal,
tecNO_LINE_INSUF_RESERVE);
if (auto const ret = sleDst.checkReserve(
ctx, priorBalance, sponsorSle, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE);
!isTesSuccess(ret))
{
return ret;
@@ -706,7 +705,7 @@ addEmptyHolding(
srcId,
dstId,
index.key,
sleDst,
sleDst.mutableRawSle(),
/*bAuth=*/false,
/*bNoRipple=*/true,
/*bFreeze=*/false,
@@ -715,7 +714,7 @@ addEmptyHolding(
/*saLimit=*/STAmount{Issue{currency, dstId}},
/*uQualityIn=*/0,
/*uQualityOut=*/0,
sponsorSle,
sponsorSle ? sponsorSle->mutableRawSle() : SLE::pointer{},
journal);
}
@@ -728,7 +727,7 @@ removeEmptyHolding(
{
if (issue.native())
{
auto const sle = ctx.view.read(keylet::account(accountID));
auto const sle = AccountRootEntryW(accountID, ctx.view);
if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -753,13 +752,14 @@ removeEmptyHolding(
if (line->isFlag(lsfLowReserve))
{
// 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)
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
// is about to be deleted, but this will make the metadata reflect an
// accurate state at the time of deletion.
@@ -770,13 +770,14 @@ removeEmptyHolding(
if (line->isFlag(lsfHighReserve))
{
// 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)
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
// is about to be deleted, but this will make the metadata reflect an
// accurate state at the time of deletion.
@@ -805,8 +806,8 @@ deleteAMMTrustLine(
auto const& [low, high] = std::minmax(
sleState->getFieldAmount(sfLowLimit).getIssuer(),
sleState->getFieldAmount(sfHighLimit).getIssuer());
auto sleLow = view.peek(keylet::account(low));
auto sleHigh = view.peek(keylet::account(high));
auto sleLow = AccountRootEntryW(low, view, j);
auto sleHigh = AccountRootEntryW(high, view, j);
if (!sleLow || !sleHigh)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -825,7 +826,7 @@ deleteAMMTrustLine(
if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
return terNO_AMM;
auto const sponsorSle =
auto sponsorSle =
getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor);
if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter))
@@ -838,7 +839,7 @@ deleteAMMTrustLine(
if (!sleState->isFlag(uFlags))
return tecINTERNAL; // LCOV_EXCL_LINE
decreaseOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, 1, j);
(!ammLow ? sleLow : sleHigh).decreaseOwnerCount(sponsorSle, 1);
return tesSUCCESS;
}
+20 -20
View File
@@ -3,7 +3,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/ledger/helpers/ProposalHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -76,7 +76,7 @@ getTxReserveSponsorID(STTx const& tx)
return {};
}
std::expected<SLE::pointer, TER>
std::expected<std::optional<AccountRootEntryW>, TER>
getTxReserveSponsor(ApplyViewContext ctx)
{
auto const sponsorID = getTxReserveSponsorID(ctx.tx);
@@ -85,17 +85,17 @@ getTxReserveSponsor(ApplyViewContext ctx)
XRPL_ASSERT(
ctx.view.rules().enabled(featureSponsor),
"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
if (!sle)
if (!sle.exists())
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
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)
{
auto const sponsorID = getTxReserveSponsorID(tx);
@@ -104,18 +104,18 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx)
XRPL_ASSERT(
view.rules().enabled(featureSponsor),
"xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
auto sle = view.read(keylet::account(*sponsorID));
auto sle = AccountRootEntryR(*sponsorID, view);
// already checked in Transactor::checkSponsor
if (!sle)
if (!sle.exists())
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
return sle;
}
return SLE::pointer();
return std::nullopt;
}
std::expected<SLE::pointer, TER>
getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle)
std::expected<std::optional<AccountRootEntryW>, TER>
getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle)
{
// A reserve sponsor only covers tx.Account's own objects.
if (ctx.view.rules().enabled(fixCleanup3_2_0))
@@ -132,8 +132,8 @@ getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle)
"xrpl::getEffectiveTxReserveSponsor : accountSle exists and is account type");
}
if (isPseudoAccount(accountSle) || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount])
return SLE::pointer();
if (accountSle.isPseudoAccount() || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount])
return std::nullopt;
return getTxReserveSponsor(ctx);
}
@@ -151,7 +151,7 @@ getLedgerEntryReserveSponsorID(SLE::ConstRef sle, SF_ACCOUNT const& field)
return {};
}
SLE::pointer
std::optional<AccountRootEntryW>
getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT const& field)
{
auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field);
@@ -160,9 +160,9 @@ getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT cons
XRPL_ASSERT(
view.rules().enabled(featureSponsor),
"xrpl::getLedgerEntryReserveSponsor : sponsor exists + Sponsor enabled");
return view.peek(keylet::account(*sponsorID));
return AccountRootEntryW(*sponsorID, view);
}
return {};
return std::nullopt;
}
void
@@ -185,16 +185,16 @@ addSponsorToLedgerEntry(SLE::Ref sle, SLE::ConstRef sponsorSle, SF_ACCOUNT const
void
addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::Ref sle, SF_ACCOUNT const& field)
{
// getTxReserveSponsor yields a null pointer when the tx is not
// reserve-sponsored, so addSponsorToLedgerEntry becomes a no-op then. The
// error case (tecINTERNAL) is an already-checked invariant; skip stamping.
// getTxReserveSponsor yields nullopt when the tx is not reserve-sponsored,
// so addSponsorToLedgerEntry becomes a no-op then. The error case
// (tecINTERNAL) is an already-checked invariant; skip stamping.
auto const sponsorSle = getTxReserveSponsor(ctx);
if (sponsorSle && *sponsorSle)
{
XRPL_ASSERT(
ctx.view.rules().enabled(featureSponsor),
"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/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -293,7 +294,7 @@ getLineIfUsable(
// we need to check if the associated assets have been frozen
if (view.rules().enabled(fixFrozenLPTokenTransfer))
{
auto const sleIssuer = view.read(keylet::account(issuer));
auto const sleIssuer = AccountRootEntryR(issuer, view);
if (!sleIssuer)
{
return nullptr; // LCOV_EXCL_LINE
@@ -562,7 +563,7 @@ canAddHolding(ReadView const& view, Issue const& issue)
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)
{
return terNO_ACCOUNT;
@@ -738,6 +739,8 @@ directSendNoFeeIOU(
auto const senderFreezeFlag = bSenderHigh ? lsfHighFreeze : lsfLowFreeze;
auto const receiverReserveFlag = bSenderHigh ? lsfLowReserve : lsfHighReserve;
AccountRootEntryW senderSle(uSenderID, view, j);
// FIXME This NEEDS to be cleaned up and simplified. It's impossible
// for anyone to understand.
if (saBefore > beast::kZero
@@ -746,8 +749,7 @@ directSendNoFeeIOU(
// Sender is zero or negative.
&& sleRippleState->isFlag(senderReserveFlag)
// Sender reserve is set.
&& sleRippleState->isFlag(senderNoRippleFlag) !=
view.read(keylet::account(uSenderID))->isFlag(lsfDefaultRipple) &&
&& sleRippleState->isFlag(senderNoRippleFlag) != senderSle->isFlag(lsfDefaultRipple) &&
!sleRippleState->isFlag(senderFreezeFlag) &&
!sleRippleState->getFieldAmount(bSenderHigh ? sfHighLimit : sfLowLimit)
// Sender trust limit is 0.
@@ -758,9 +760,9 @@ directSendNoFeeIOU(
// Sender quality out is 0.
{
// Clear the reserve of the sender, possibly delete the line!
auto const currentSponsor = getLedgerEntryReserveSponsor(
auto currentSponsor = getLedgerEntryReserveSponsor(
view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor);
decreaseOwnerCount(view, view.peek(keylet::account(uSenderID)), currentSponsor, 1, j);
senderSle.decreaseOwnerCount(currentSponsor, 1);
removeSponsorFromLedgerEntry(
sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor);
@@ -805,7 +807,7 @@ directSendNoFeeIOU(
<< to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : "
<< saAmount.getFullText();
auto const sleAccount = view.peek(keylet::account(uReceiverID));
auto sleAccount = AccountRootEntryW(uReceiverID, view);
if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -817,7 +819,7 @@ directSendNoFeeIOU(
uSenderID,
uReceiverID,
index.key,
sleAccount,
sleAccount.mutableRawSle(),
false,
noRipple,
false,
@@ -1010,10 +1012,12 @@ accountSendIOU(
*/
TER terResult(tesSUCCESS);
SLE::pointer const sender =
uSenderID != beast::kZero ? view.peek(keylet::account(uSenderID)) : SLE::pointer();
SLE::pointer const receiver =
uReceiverID != beast::kZero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer();
SLE::pointer const sender = uSenderID != beast::kZero
? AccountRootEntryW(uSenderID, view).mutableRawSle()
: SLE::pointer();
SLE::pointer const receiver = uReceiverID != beast::kZero
? AccountRootEntryW(uReceiverID, view).mutableRawSle()
: SLE::pointer();
if (auto stream = j.trace())
{
@@ -1106,8 +1110,9 @@ accountSendMultiIOU(
* ensure that transfers are balanced.
*/
SLE::pointer const sender =
senderID != beast::kZero ? view.peek(keylet::account(senderID)) : SLE::pointer();
SLE::pointer const sender = senderID != beast::kZero
? AccountRootEntryW(senderID, view).mutableRawSle()
: SLE::pointer();
if (auto stream = j.trace())
{
@@ -1138,8 +1143,9 @@ accountSendMultiIOU(
if (!amount || (senderID == receiverID))
continue;
SLE::pointer const receiver =
receiverID != beast::kZero ? view.peek(keylet::account(receiverID)) : SLE::pointer();
SLE::pointer const receiver = receiverID != beast::kZero
? AccountRootEntryW(receiverID, view).mutableRawSle()
: SLE::pointer();
if (auto stream = j.trace())
{
@@ -1591,8 +1597,8 @@ transferXRP(
XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver");
XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP");
SLE::pointer const sender = view.peek(keylet::account(from));
SLE::pointer const receiver = view.peek(keylet::account(to));
SLE::pointer const sender = AccountRootEntryW(from, view).mutableRawSle();
SLE::pointer const receiver = AccountRootEntryW(to, view).mutableRawSle();
if (!sender || !receiver)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -317,8 +317,8 @@ public:
}
else
{
status = checkedCast<Status>(
safeCast<int>(Status::CustomCode) + safeCast<int>(getStatus.code()));
status = static_cast<Status>(
static_cast<int>(Status::CustomCode) + unsafeCast<int>(getStatus.code()));
JLOG(journal.error()) << getStatus.ToString();
}
+61 -114
View File
@@ -8,8 +8,6 @@
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/tokens.h>
#include <algorithm>
#include <array>
#include <atomic>
#include <cstdint>
#include <cstring>
@@ -21,136 +19,85 @@
namespace xrpl {
namespace {
namespace detail {
/** If true, we implement a cache for account ID lookups.
This is generally a good idea and can improve performance, especially
when serving client requests.
The cache may generate warnings under some sanitizers; if you want to
disable caching, set this variable to false.
/**
* Caches the base58 representations of AccountIDs
*/
constexpr bool cacheAccountIDs = true;
/** Represents a cached entry for the given AccountID.
We keep the size and alignment of the structure at 64 bytes to
make sure it fits within a cache line and avoid false sharing.
*/
struct alignas(64) CachedAccountID
class AccountIdCache
{
/** The sequence lock for the entry.
private:
struct CachedAccountID
{
AccountID id;
char encoding[40] = {0};
};
An even value means that the data is stable, whereas an odd
value indicates the data is being actively modified.
// The actual cache
std::vector<CachedAccountID> cache_;
We use a 32-bit value to ensure that the counter will never
return to an observed value during a read window.
*/
std::atomic<std::uint32_t> seq{0};
// We use a hash function designed to resist algorithmic complexity attacks
HardenedHash<> hasher_;
/** The account id. */
AccountID id{};
// 64 spinlocks, packed into a single 64-bit value
std::atomic<std::uint64_t> locks_ = 0;
/** The length of the cached encoding.
public:
AccountIdCache(std::size_t count) : cache_(count)
{
// This is non-binding, but we try to avoid wasting memory that
// is caused by overallocation.
cache_.shrink_to_fit();
}
This field disambiguates collisions between ACCOUNT_ZERO and
never-written-to slots, since they would otherwise share the
same encoded bits.
*/
std::uint8_t len = 0;
std::string
toBase58(AccountID const& id)
{
auto const index = hasher_(id) % cache_.size();
/** The actual string encoding. */
char encoding[39] = {0};
PackedSpinlock sl(locks_, index % 64);
{
std::scoped_lock const lock(sl);
// The check against the first character of the encoding ensures
// that we don't mishandle the case of the all-zero account:
if (cache_[index].encoding[0] != 0 && cache_[index].id == id)
return cache_[index].encoding;
}
auto ret = encodeBase58Token(TokenType::AccountID, id.data(), id.size());
XRPL_ASSERT(ret.size() <= 38, "xrpl::detail::AccountIdCache : maximum result size");
{
std::scoped_lock const lock(sl);
cache_[index].id = id;
std::strcpy(cache_[index].encoding, ret.c_str());
}
return ret;
}
};
static_assert(sizeof(CachedAccountID) == 64 && alignof(CachedAccountID) == 64);
} // namespace detail
// The actual cache to use: a simple, direct-mapped, best-effort construct.
//
// We do not use a hardened hash for this because an AccountID is already
// just cryptographically generated (i.e. uniformly distributed) bits and
// there is no amplification, since collisions do not carry an additional
// cost.
//
// The size is a "nice" power of 2 because it makes the calculation of an
// index easier.
constinit std::array<CachedAccountID, 65536> gAccountIdCache{};
static std::unique_ptr<detail::AccountIdCache> gAccountIdCache;
} // namespace
void
initAccountIdCache(std::size_t count)
{
if (!gAccountIdCache && count != 0)
gAccountIdCache = std::make_unique<detail::AccountIdCache>(count);
}
std::string
toBase58(AccountID const& v)
{
// Since the input bits are generated by a cryptographically secure
// hash construct, we can just pick any subset of them, no muss, no
// fuss.
auto const index = [&v]() -> std::uint16_t {
auto const data = v.data();
if (gAccountIdCache)
return gAccountIdCache->toBase58(v);
return static_cast<std::uint16_t>(data[0]) | (static_cast<std::uint16_t>(data[1]) << 8);
}();
if constexpr (cacheAccountIDs)
{
auto& slot = gAccountIdCache[index];
// This is the optimistic, store-free read path: a collision with a
// concurrent writer will be detected because the seq recheck fails
// and we fall through to the slow path.
//
// These unsynchronized reads of the slot fields are, formally, data
// races and, technically speaking, result in undefined behavior and
// a tool like TSAN might flag them. These should not have an impact
// on the correctness of the returned data: the sequence lock checks
// ensure we discard any data that was in flux.
if (auto const s0 = slot.seq.load(std::memory_order::acquire);
(s0 & 1) == 0 && slot.len != 0 && slot.id == v)
{
char buf[38];
auto const len = std::min<std::size_t>(slot.len, sizeof(buf));
std::memcpy(buf, slot.encoding, len);
// Prevents the loads above from sinking below the recheck; the
// acquire on s0 constrains later operations, not earlier ones.
std::atomic_thread_fence(std::memory_order::acquire);
// Everything read from the slot — the gates above and the bytes in
// buf — precedes this recheck and is validated by it. No slot access
// may follow it.
if (slot.seq.load(std::memory_order::relaxed) == s0)
return std::string(buf, len);
}
}
auto ret = encodeBase58Token(TokenType::AccountID, v.data(), v.size());
XRPL_ASSERT(!ret.empty() && ret.size() <= 38, "xrpl::toBase58 : maximum result size");
if constexpr (cacheAccountIDs)
{
auto& slot = gAccountIdCache[index];
// The CAS to an odd value excludes other writers; if it fails, another
// thread wrote or is writing into this slot. That is not a problem: we
// just don't store our result into the cache this time.
if (auto s0 = slot.seq.load(std::memory_order::relaxed); (s0 & 1) == 0 &&
slot.seq.compare_exchange_strong(s0, s0 + 1, std::memory_order::acquire))
{
slot.id = v;
slot.len = static_cast<std::uint8_t>(ret.size());
std::copy_n(ret.data(), ret.size(), slot.encoding);
// The acquire on the CAS above keeps the writes from becoming visible
// before the seqlock transitions to an odd value, which means readers
// can detect an in-progress write. This release publishes them before
// the seqlock transitions to an even value.
slot.seq.store(s0 + 2, std::memory_order::release);
}
}
return ret;
return encodeBase58Token(TokenType::AccountID, v.data(), v.size());
}
template <>
+45 -73
View File
@@ -5,7 +5,6 @@
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/hash/uhash.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
@@ -18,13 +17,9 @@
#include <xrpl/protocol/jss.h>
#include <algorithm>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
@@ -41,10 +36,10 @@ STPathElement::getHash(STPathElement const& element)
// important. We don't even really need to fully hash the whole
// base_uint here, as a few bytes would do for our use.
//
// The note above is only true because the result of this function is only
// used by the STPathElement equality operator as a fast-reject prefilter,
// ahead of the field comparisons that then decide the answer. Do not use
// it to key a container.
// The note above is only true because the result of this function reaches
// nothing but STPathElement::operator==, where it is a fast-reject
// prefilter ahead of the field comparisons that decide the answer. Do not
// use it to key a container.
for (auto const x : element.getAccountID())
hashAccount += (hashAccount * 257) ^ x;
@@ -111,38 +106,12 @@ STPathSet::operator=(STPathSet const& other)
STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
{
using enum STPathElement::Type;
auto parsePathElementType = [](std::underlying_type_t<STPathElement::Type> byte) noexcept
-> std::optional<STPathElement::Type> {
if (byte == std::to_underlying(TypeBoundary))
return TypeBoundary;
constexpr auto knownBits = static_cast<std::underlying_type_t<STPathElement::Type>>(
std::to_underlying(TypeAccount) | std::to_underlying(TypeCurrency) |
std::to_underlying(TypeIssuer) | std::to_underlying(TypeMpt));
if ((byte & ~knownBits) != 0)
return std::nullopt;
return static_cast<STPathElement::Type>(byte);
};
std::vector<STPathElement> path;
std::optional<STPathElement::Type> type;
do
for (;;)
{
type = parsePathElementType(sit.get8());
int const iType = sit.get8();
if (!type)
{
JLOG(debugLog().error()) << "Bad path element in pathset";
Throw<std::runtime_error>("bad path element");
}
if (type == TypeNone || type == TypeBoundary)
if (iType == STPathElement::TypeNone || iType == STPathElement::TypeBoundary)
{
if (path.empty())
{
@@ -153,11 +122,28 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
// Move rather than converting the vector to an STPath by copy.
value_.emplace_back(std::move(path));
path.clear();
if (iType == STPathElement::TypeNone)
return;
}
else if ((iType & ~STPathElement::TypeAll) != 0)
{
JLOG(debugLog().error()) << "Bad path element " << iType << " in pathset";
Throw<std::runtime_error>("bad path element");
}
else
{
bool const hasCurrency = ((*type & TypeCurrency) == TypeCurrency);
bool const hasMPT = ((*type & TypeMpt) == TypeMpt);
auto const hasAccount = (iType & STPathElement::TypeAccount) != 0u;
auto const hasCurrency = (iType & STPathElement::TypeCurrency) != 0u;
auto const hasIssuer = (iType & STPathElement::TypeIssuer) != 0u;
auto const hasMPT = (iType & STPathElement::TypeMpt) != 0u;
AccountID account;
PathAsset asset;
AccountID issuer;
if (hasAccount)
account = sit.get160();
if (hasCurrency && hasMPT)
{
@@ -165,26 +151,18 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
Throw<std::runtime_error>("bad path element: MPT and Currency");
}
AccountID account;
AccountID issuer;
if ((*type & TypeAccount) == TypeAccount)
account = sit.get160();
PathAsset asset;
if (hasCurrency)
asset = Currency::fromRaw(sit.get160());
if (hasMPT)
asset = sit.get192();
if ((*type & TypeIssuer) == TypeIssuer)
if (hasIssuer)
issuer = sit.get160();
path.emplace_back(account, asset, issuer, hasCurrency || hasMPT);
}
} while (type != TypeNone);
}
}
STBase*
@@ -238,22 +216,24 @@ STPath::getJson(JsonOptions) const
for (auto const& it : path_)
{
json::Value elem(json::ValueType::Object);
auto const iType = it.getNodeType();
elem[jss::type] = safeCast<std::uint32_t>(it.getNodeType());
elem[jss::type] = iType;
if (it.isType(STPathElement::TypeAccount))
if ((iType & STPathElement::TypeAccount) != 0u)
elem[jss::account] = to_string(it.getAccountID());
XRPL_ASSERT(
!(it.hasCurrency() && it.hasMPT()),
((iType & STPathElement::TypeCurrency) == 0u) ||
((iType & STPathElement::TypeMpt) == 0u),
"xrpl::STPath::getJson : not type Currency and MPT");
if (it.hasCurrency())
if ((iType & STPathElement::TypeCurrency) != 0u)
elem[jss::currency] = to_string(it.getCurrency());
if (it.hasMPT())
if ((iType & STPathElement::TypeMpt) != 0u)
elem[jss::mpt_issuance_id] = to_string(it.getMPTID());
if (it.hasIssuer())
if ((iType & STPathElement::TypeIssuer) != 0u)
elem[jss::issuer] = to_string(it.getIssuerID());
ret.append(elem);
@@ -285,42 +265,34 @@ STPathSet::add(Serializer& s) const
XRPL_ASSERT(getFName().fieldType == STI_PATHSET, "xrpl::STPathSet::add : valid field type");
bool first = true;
// Workaround, since `Serializer` does not accept strongly-typed enums
// yet. When support is added, the static_assert will trigger, and the
// lambda can be removed entirely.
auto const toByte = [](std::same_as<STPathElement::Type> auto t) noexcept {
static_assert(
!requires(Serializer& ser) { ser.add8(t); },
"Serializer::add8 now accepts STPathElement::Type directly; remove toByte.");
return std::to_underlying(t);
};
for (auto const& spPath : value_)
{
if (!first)
s.add8(toByte(STPathElement::TypeBoundary));
s.add8(STPathElement::TypeBoundary);
for (auto const& speElement : spPath)
{
s.add8(toByte(speElement.getNodeType()));
int const iType = speElement.getNodeType();
if (speElement.isType(STPathElement::TypeAccount))
s.add8(iType);
if ((iType & STPathElement::TypeAccount) != 0u)
s.addBitString(speElement.getAccountID());
if (speElement.hasMPT())
if ((iType & STPathElement::TypeMpt) != 0u)
s.addBitString(speElement.getMPTID());
if (speElement.hasCurrency())
if ((iType & STPathElement::TypeCurrency) != 0u)
s.addBitString(speElement.getCurrency());
if (speElement.hasIssuer())
if ((iType & STPathElement::TypeIssuer) != 0u)
s.addBitString(speElement.getIssuerID());
}
first = false;
}
s.add8(toByte(STPathElement::TypeNone));
s.add8(STPathElement::TypeNone);
}
} // namespace xrpl
+3 -4
View File
@@ -38,7 +38,7 @@ ApplyContext::ApplyContext(
, parentBatchId_(parentBatchId)
{
XRPL_ASSERT(
parentBatchId.has_value() == ((flags_ & ApplyFlags::Batch) == ApplyFlags::Batch),
parentBatchId.has_value() == ((flags_ & TapBatch) == TapBatch),
"Parent Batch ID should be set if batch apply flag is set");
view_.emplace(&base_, flags_);
}
@@ -53,14 +53,13 @@ std::optional<TxMeta>
ApplyContext::apply(TER ter)
{
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
return view_->apply(
base_, tx, ter, parentBatchId_, (flags_ & ApplyFlags::DryRun) != ApplyFlags::None, journal);
return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & TapDryRun) != 0u, journal);
}
void
ApplyContext::addOrderBook(Book const& book)
{
if ((flags_ & ApplyFlags::DryRun) == ApplyFlags::None)
if ((flags_ & TapDryRun) == TapNone)
registry.get().getOrderBookDB().addOrderBook(book);
}
+27 -28
View File
@@ -11,6 +11,7 @@
#include <xrpl/json/to_string.h> // IWYU pragma: keep
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DelegateHelpers.h>
@@ -134,7 +135,7 @@ preflightCheckSigningKey(STObject const& sigObject, beast::Journal j)
std::optional<NotTEC>
preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j)
{
if ((flags & ApplyFlags::DryRun) != ApplyFlags::None) // simulation
if ((flags & TapDryRun) != 0u) // simulation
{
std::optional<Slice> const signature = sigObject[~sfTxnSignature];
if (signature && !signature->empty())
@@ -510,8 +511,7 @@ Transactor::minimumFee(
Fees const& fees,
ApplyFlags flags)
{
return scaleFeeLoad(
baseFee, registry.getFeeTrack(), fees, (flags & ApplyFlags::Unlimited) != ApplyFlags::None);
return scaleFeeLoad(baseFee, registry.getFeeTrack(), fees, (flags & TapUnlimited) != 0u);
}
TER
@@ -522,7 +522,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
auto const feePaid = ctx.tx[sfFee].xrp();
if ((ctx.flags & ApplyFlags::Batch) != ApplyFlags::None)
if ((ctx.flags & TapBatch) != 0u)
{
if (feePaid == beast::kZero)
return tesSUCCESS;
@@ -587,7 +587,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
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;
}
else
@@ -653,7 +653,7 @@ Transactor::payFee()
XRPAmount spendable = balance;
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
spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero;
}
@@ -701,7 +701,7 @@ Transactor::checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j
{
auto const id = tx.getAccountID(sfAccount);
auto const sle = view.read(keylet::account(id));
auto const sle = AccountRootEntryR(id, view);
if (!sle)
{
@@ -766,7 +766,7 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
{
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)
{
@@ -836,7 +836,7 @@ Transactor::ticketDelete(
// Update the account root's TicketCount. If the ticket count drops to
// zero remove the (optional) field.
auto sleAccount = view.peek(keylet::account(account));
auto sleAccount = AccountRootEntryW(account, view);
if (!sleAccount)
{
// LCOV_EXCL_START
@@ -886,19 +886,19 @@ Transactor::apply()
// If the transactor requires a valid account and the transaction doesn't
// 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
// that allow zero account.
XRPL_ASSERT(
sle != nullptr || accountID_ == beast::kZero,
sle.exists() || accountID_ == beast::kZero,
"xrpl::Transactor::apply : non-null SLE or zero account");
if (sle)
{
preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp();
TER result = consumeSeqProxy(sle);
TER result = consumeSeqProxy(sle.mutableRawSle());
if (!isTesSuccess(result))
return result;
@@ -909,7 +909,7 @@ Transactor::apply()
if (sle->isFieldPresent(sfAccountTxnID))
sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID());
view().update(sle);
sle.update();
}
return doApply();
@@ -926,11 +926,11 @@ Transactor::checkSign(
bool permitUncreatedAccount)
{
{
auto const sle = view.read(keylet::account(idAccount));
auto const sle = AccountRootEntryR(idAccount, view);
if ((view.rules().enabled(featureLendingProtocol) ||
view.rules().enabled(featureBatchV1_1) || view.rules().enabled(fixCleanup3_3_0)) &&
isPseudoAccount(sle))
sle.isPseudoAccount())
{
// Pseudo-accounts can't sign transactions. This check is gated on a
// few different amendments so that it takes effect as soon as any of
@@ -952,8 +952,7 @@ Transactor::checkSign(
return tesSUCCESS;
}
if (((flags & ApplyFlags::DryRun) != ApplyFlags::None) && pkSigner.empty() &&
!sigObject.isFieldPresent(sfSigners))
if (((flags & TapDryRun) != 0u) && pkSigner.empty() && !sigObject.isFieldPresent(sfSigners))
{
// simulate: skip signature validation when neither SigningPubKey nor
// Signers are provided
@@ -993,7 +992,7 @@ Transactor::checkSign(
// Look up the account.
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const sleAccount = view.read(keylet::account(idAccount));
auto const sleAccount = AccountRootEntryR(idAccount, view);
if (!sleAccount)
{
// An account that does not exist yet can only be authorized by its own
@@ -1006,7 +1005,7 @@ Transactor::checkSign(
return tesSUCCESS;
}
return checkSingleSign(view, idSigner, idAccount, sleAccount, j);
return checkSingleSign(view, idSigner, idAccount, sleAccount.rawSle(), j);
}
NotTEC
@@ -1124,7 +1123,7 @@ Transactor::checkMultiSign(
}
XRPL_ASSERT(
(flags & ApplyFlags::DryRun) != ApplyFlags::None || !spk.empty(),
(flags & TapDryRun) || !spk.empty(),
"xrpl::Transactor::checkMultiSign : non-empty signer or "
"simulation");
AccountID const signingAcctIDFromPubKey =
@@ -1155,7 +1154,7 @@ Transactor::checkMultiSign(
// In any of these cases we need to know whether the account is in
// the ledger. Determine that now.
auto const sleTxSignerRoot = view.read(keylet::account(txSignerAcctID));
auto const sleTxSignerRoot = AccountRootEntryR(txSignerAcctID, view);
if (signingAcctIDFromPubKey == txSignerAcctID)
{
@@ -1298,7 +1297,7 @@ Transactor::reset(XRPAmount fee)
{
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
// is missing then we can't very well charge it a fee, can we?
@@ -1333,7 +1332,7 @@ Transactor::reset(XRPAmount fee)
XRPAmount spendable = balance;
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
spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero;
}
@@ -1366,13 +1365,13 @@ Transactor::reset(XRPAmount fee)
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");
if (isTesSuccess(ter))
{
view().update(txnAcct);
if (payerSle != txnAcct)
txnAcct.update();
if (payerSle != txnAcct.mutableRawSle())
view().update(payerSle);
}
@@ -1606,7 +1605,7 @@ Transactor::operator()()
if (ctx_.size() > kOversizeMetaDataCap)
result = tecOVERSIZE;
if (isTecClaim(result) && ((view().flags() & ApplyFlags::FailHard) != ApplyFlags::None))
if (isTecClaim(result) && ((view().flags() & TapFailHard) != 0u))
{
// If the TapFailHard flag is set, a tec result
// must not do anything
@@ -1686,7 +1685,7 @@ Transactor::operator()()
// Once we call apply, we will no longer be able to look at view()
metadata = ctx_.apply(result);
if ((ctx_.flags() & ApplyFlags::DryRun) != ApplyFlags::None)
if ((ctx_.flags() & TapDryRun) != 0u)
return logger(result, false, std::move(metadata));
return logger(result, canApply, std::move(metadata));
+2 -2
View File
@@ -215,7 +215,7 @@ applyBatchTransactions(
auto applyOneTransaction = [&registry, &j, &parentBatchId, &batchView](STTx const& tx) {
OpenView perTxBatchView(kBatchView, batchView);
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, ApplyFlags::Batch, j);
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, TapBatch, j);
XRPL_ASSERT(
ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)),
"Inner transaction should not be applied");
@@ -274,7 +274,7 @@ applyTransaction(
{
// Returns false if the transaction has need not be retried.
if (retryAssured)
flags = flags | ApplyFlags::Retry;
flags = flags | TapRetry;
JLOG(j.debug()) << "TXN " << txn.getTransactionID() << (retryAssured ? "/retry" : "/final");
+2 -4
View File
@@ -346,8 +346,7 @@ preflight(
// builds; re-check the same invariant here so a release build can't
// silently skip a proposed transaction's signature-presence checks
// outside of a dry run.
if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
{
// LCOV_EXCL_START
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
@@ -378,8 +377,7 @@ preflight(
PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
// See the comment in the other preflight() overload above.
if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
{
// LCOV_EXCL_START
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
+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
ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after)
{
@@ -742,7 +751,7 @@ ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after)
{
accountsCreated_++;
accountSeq_ = (*after)[sfSequence];
pseudoAccount_ = isPseudoAccount(after);
pseudoAccount_ = isPseudoAccountSle(after);
flags_ = after->getFlags();
}
}
@@ -1020,9 +1029,9 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstR
if (after && after->getType() == ltACCOUNT_ROOT)
{
bool const isPseudo = [&]() {
// isPseudoAccount checks that any of the pseudo-account fields are
// set.
if (isPseudoAccount(after))
// isPseudoAccountSle checks that any of the pseudo-account fields
// are set.
if (isPseudoAccountSle(after))
return true;
// Not all pseudo-accounts have a zero sequence, but all accounts
// with a zero sequence had better be pseudo-accounts.
+2 -1
View File
@@ -23,6 +23,7 @@
#include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/invariants/InvariantCheck.h>
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
#include <algorithm>
@@ -893,7 +894,7 @@ ValidMPTTransfer::visitEntry(
// no trace of its pseudo-account status in the post-transaction view
// isAuthorized() sees at finalize() time.
if (before && before->getType() == ltACCOUNT_ROOT)
pseudoAccountsBefore_[before->at(sfAccount)] = isPseudoAccount(before);
pseudoAccountsBefore_[before->at(sfAccount)] = isPseudoAccountSle(before);
}
bool
+2 -2
View File
@@ -6,7 +6,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/VaultHelpers.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -740,7 +740,7 @@ ValidVault::finalize(
return false;
}
if (!isPseudoAccount(sleSharesIssuer))
if (!AccountRootEntryR(sleSharesIssuer, view).isPseudoAccount())
{
JLOG(j.fatal()) //
<< "Invariant failed: shares issuer must be a "
+1 -1
View File
@@ -985,7 +985,7 @@ std::optional<std::variant<Quality, AMMOffer<TIn, TOut>>>
BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
{
// This can be simplified (and sped up) if directories are never empty.
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
BookTip bt(sb, book_);
auto const lobQuality = bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
// Multi-path offer generates an offer with the quality
+3 -2
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -836,7 +837,7 @@ DirectStepI<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH;
}
auto const sleSrc = ctx.view.read(keylet::account(src_));
auto const sleSrc = AccountRootEntryR(src_, ctx.view);
if (!sleSrc)
{
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/core/ServiceRegistry.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -865,7 +866,7 @@ MPTEndpointStep<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH;
}
auto const sleSrc = ctx.view.read(keylet::account(src_));
auto const sleSrc = AccountRootEntryR(src_, ctx.view);
if (!sleSrc)
{
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 static_cast<TDerived const*>(this)->check(ctx, sleSrc);
return static_cast<TDerived const*>(this)->check(ctx, sleSrc.rawSle());
}
template <class TDerived>
+27 -13
View File
@@ -57,8 +57,10 @@ checkNear(IOUAmount const& expected, IOUAmount const& actual)
static bool
isXRPAccount(STPathElement const& pe)
{
return pe.getNodeType() == STPathElement::TypeAccount && isXRP(pe.getAccountID());
}
if (pe.getNodeType() != STPathElement::TypeAccount)
return false;
return isXRP(pe.getAccountID());
};
static std::pair<TER, std::unique_ptr<Step>>
toStep(
@@ -69,8 +71,11 @@ toStep(
{
auto& j = ctx.j;
if (ctx.isFirst && e1->isAccount() && e1->hasCurrency() && e1->getPathAsset().isXRP())
if (ctx.isFirst && e1->isAccount() &&
((e1->getNodeType() & STPathElement::TypeCurrency) != 0u) && e1->getPathAsset().isXRP())
{
return makeXrpEndpointStep(ctx, e1->getAccountID());
}
if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
return makeXrpEndpointStep(ctx, e2->getAccountID());
@@ -112,9 +117,15 @@ toStep(
// LCOV_EXCL_STOP
}
XRPL_ASSERT(e2->hasAsset() || e2->hasIssuer(), "xrpl::toStep : currency or issuer");
PathAsset const outAsset = e2->hasAsset() ? e2->getPathAsset() : curAsset;
auto const outIssuer = e2->hasIssuer() ? e2->getIssuerID() : curAsset.getIssuer();
XRPL_ASSERT(
(e2->getNodeType() & STPathElement::TypeAsset) ||
(e2->getNodeType() & STPathElement::TypeIssuer),
"xrpl::toStep : currency or issuer");
PathAsset const outAsset =
((e2->getNodeType() & STPathElement::TypeAsset) != 0u) ? e2->getPathAsset() : curAsset;
auto const outIssuer = ((e2->getNodeType() & STPathElement::TypeIssuer) != 0u)
? e2->getIssuerID()
: curAsset.getIssuer();
if (isXRP(curAsset) && outAsset.isXRP())
{
@@ -186,15 +197,16 @@ toStrand(
for (std::size_t i = 0; i < path.size(); ++i)
{
auto const& pe = path[i];
auto const t = pe.getNodeType();
if ((pe.getNodeType() & ~STPathElement::TypeAll) != STPathElement::TypeNone || pe.isNone())
if (((t & ~STPathElement::TypeAll) != 0u) || (t == 0u))
return {temBAD_PATH, Strand{}};
bool const hasAccount = pe.isType(STPathElement::TypeAccount);
bool const hasIssuer = pe.hasIssuer();
bool const hasCurrency = pe.hasCurrency();
bool const hasMPT = pe.hasMPT();
bool const hasAsset = pe.hasAsset();
bool const hasAccount = (t & STPathElement::TypeAccount) != 0u;
bool const hasIssuer = (t & STPathElement::TypeIssuer) != 0u;
bool const hasCurrency = (t & STPathElement::TypeCurrency) != 0u;
bool const hasMPT = (t & STPathElement::TypeMpt) != 0u;
bool const hasAsset = (t & STPathElement::TypeAsset) != 0u;
if (hasAccount && (hasIssuer || hasCurrency))
return {temBAD_PATH, Strand{}};
@@ -238,7 +250,9 @@ toStrand(
}();
// Currency or MPT
auto hasAsset = [](STPathElement const& pe) { return pe.hasAsset(); };
auto hasAsset = [](STPathElement const pe) {
return pe.getNodeType() & STPathElement::TypeAsset;
};
std::vector<STPathElement> normPath;
// reserve enough for the path, the implied source, destination,
+2 -1
View File
@@ -4,6 +4,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -342,7 +343,7 @@ XRPEndpointStep<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH;
}
auto sleAcc = ctx.view.read(keylet::account(acc_));
auto sleAcc = AccountRootEntryR(acc_, ctx.view);
if (!sleAcc)
{
JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
@@ -9,6 +9,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
@@ -224,7 +225,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
AccountID const account{ctx.tx[sfAccount]};
AccountID const dst{ctx.tx[sfDestination]};
auto sleDst = ctx.view.read(keylet::account(dst));
auto sleDst = AccountRootEntryR(dst, ctx.view);
if (!sleDst)
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");
if (!sleAccount)
return terNO_ACCOUNT;
@@ -353,11 +354,11 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
TER
AccountDelete::doApply()
{
auto src = view().peek(keylet::account(accountID_));
auto src = AccountRootEntryW(accountID_, view());
XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account");
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");
if (!src || !dst)
@@ -365,8 +366,8 @@ AccountDelete::doApply()
if (ctx_.tx.isFieldPresent(sfCredentialIDs))
{
if (auto err =
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dstID, dst, ctx_.journal);
if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), accountID_, dstID, dst.rawSle(), ctx_.journal);
!isTesSuccess(err))
return err;
}
@@ -407,7 +408,7 @@ AccountDelete::doApply()
if (src->isFieldPresent(sfSponsor))
{
auto const sponsorID = src->getAccountID(sfSponsor);
auto sponsorSle = view().peek(keylet::account(sponsorID));
auto sponsorSle = AccountRootEntryW(sponsorID, view());
if (!sponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -425,7 +426,7 @@ AccountDelete::doApply()
return tefINTERNAL; // LCOV_EXCL_LINE
}
sponsorSle->at(sfSponsoringAccountCount) = sponsoringAccountCount - 1;
view().update(sponsorSle);
sponsorSle.update();
// Following line might look redundant, but without it, sfSponsor
// would end up remaining in after-ltAccountRoot during the
@@ -448,8 +449,8 @@ AccountDelete::doApply()
if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
dst->clearFlag(lsfPasswordSpent);
view().update(dst);
view().erase(src);
dst.update();
src.erase();
return tesSUCCESS;
}
@@ -6,6 +6,7 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -170,7 +171,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
{
auto const id = ctx.tx[sfAccount];
auto const sle = ctx.view.read(keylet::account(id));
auto const sle = AccountRootEntryR(id, ctx.view);
if (!sle)
return terNO_ACCOUNT;
@@ -187,8 +188,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
{
JLOG(ctx.j.trace()) << "Retry: Owner directory not empty.";
return ((ctx.flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terOWNERS}
: TER{tecOWNERS};
return ((ctx.flags & TapRetry) != 0u) ? TER{terOWNERS} : TER{tecOWNERS};
}
}
@@ -225,7 +225,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
TER
AccountSet::doApply()
{
auto const sle = view().peek(keylet::account(accountID_));
auto sle = AccountRootEntryW(accountID_, view());
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -583,7 +583,7 @@ AccountSet::doApply()
if (uFlagsIn != uFlagsOut)
sle->setFieldU32(sfFlags, uFlagsOut);
ctx_.view().update(sle);
sle.update();
return tesSUCCESS;
}
@@ -3,6 +3,7 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/PublicKey.h>
@@ -25,7 +26,7 @@ SetRegularKey::calculateBaseFee(ReadView const& view, STTx const& tx)
{
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))
{
@@ -53,7 +54,7 @@ SetRegularKey::preflight(PreflightContext const& ctx)
TER
SetRegularKey::doApply()
{
auto const sle = view().peek(keylet::account(accountID_));
auto sle = AccountRootEntryW(accountID_, view());
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -73,7 +74,7 @@ SetRegularKey::doApply()
sle->makeFieldAbsent(sfRegularKey);
}
ctx_.view().update(sle);
sle.update();
return tesSUCCESS;
}
@@ -6,6 +6,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -213,8 +214,9 @@ removeSignersFromLedger(
// LCOV_EXCL_STOP
}
AccountRootEntryW accountSle(accountKeylet, view);
decreaseOwnerCountForObject(
view, view.peek(accountKeylet), signers, removeFromOwnerCount, registry.getJournal("View"));
view, accountSle, signers, removeFromOwnerCount, registry.getJournal("View"));
view.erase(signers);
@@ -309,7 +311,7 @@ SignerListSet::replaceSignerList()
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_))
return ter;
auto const sle = view().peek(accountKeylet);
auto sle = AccountRootEntryW(accountKeylet, view(), ctx_.journal);
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -319,12 +321,8 @@ SignerListSet::replaceSignerList()
// We check the reserve against the starting balance because we want to
// allow dipping into the reserve to pay fees. This behavior is consistent
// with TicketCreate.
if (auto const ret = checkReserve(
ctx_.getApplyViewContext(),
sle,
preFeeBalance_,
{.ownerCountDelta = kAddedOwnerCount},
ctx_.journal);
if (auto const ret = sle.checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = kAddedOwnerCount});
!isTesSuccess(ret))
return ret;
@@ -333,7 +331,6 @@ SignerListSet::replaceSignerList()
view().insert(signerList);
writeSignersToSLE(signerList, flags);
auto viewJ = ctx_.registry.get().getJournal("View");
// Add the signer list to the account's directory.
auto const page =
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_));
@@ -348,7 +345,7 @@ SignerListSet::replaceSignerList()
// If we succeeded, the new entry counts against the
// creator's reserve.
increaseOwnerCount(ctx_.getApplyViewContext(), sle, kAddedOwnerCount, viewJ);
sle.increaseOwnerCount(ctx_.getApplyViewContext(), kAddedOwnerCount);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), signerList);
return tesSUCCESS;
}
@@ -359,7 +356,7 @@ SignerListSet::destroySignerList()
auto const accountKeylet = keylet::account(accountID_);
// Destroying the signer list is only allowed if either the master key
// is enabled or there is a regular key.
SLE::pointer const ledgerEntry = view().peek(accountKeylet);
auto const ledgerEntry = AccountRootEntryW(accountKeylet, view());
if (!ledgerEntry)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -10,6 +10,7 @@
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -125,8 +126,7 @@ checkAttestationPublicKey(
AccountID const accountFromPK = calcAccountID(pk);
if (auto const sleAttestationSigningAccount =
view.read(keylet::account(attestationSignerAccount)))
if (auto const sleAttestationSigningAccount = AccountRootEntryR(attestationSignerAccount, view))
{
if (accountFromPK == attestationSignerAccount)
{
@@ -404,7 +404,7 @@ transferHelper(
return tesSUCCESS;
auto const dstK = keylet::account(dst);
if (auto sleDst = psb.read(dstK))
if (auto sleDst = AccountRootEntryR(dstK, psb))
{
// Check dst tag and deposit auth
@@ -430,13 +430,13 @@ transferHelper(
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");
if (!sleSrc)
return tecINTERNAL; // LCOV_EXCL_LINE
{
auto const reserve = accountReserve(psb, sleSrc, j);
auto const reserve = sleSrc.reserve();
auto const availableBalance = [&]() -> STAmount {
STAmount curBal = (*sleSrc)[sfBalance];
@@ -455,7 +455,7 @@ transferHelper(
}
}
auto sleDst = psb.peek(dstK);
auto sleDst = AccountRootEntryW(dstK, psb);
if (!sleDst)
{
if (canCreate == CanCreateDstPolicy::No)
@@ -470,17 +470,17 @@ transferHelper(
}
// Create the account.
sleDst = std::make_shared<SLE>(dstK);
sleDst.newSLE();
sleDst->setAccountID(sfAccount, dst);
sleDst->setFieldU32(sfSequence, psb.seq());
psb.insert(sleDst);
sleDst.insert();
}
(*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt;
(*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt;
psb.update(sleSrc);
psb.update(sleDst);
sleSrc.update();
sleDst.update();
return tesSUCCESS;
}
@@ -724,7 +724,7 @@ finalizeClaimHelper(
auto const cidOwner = (*sleClaimID)[sfAccount];
{
// Remove the claim id
auto const sleOwner = outerSb.peek(keylet::account(cidOwner));
auto sleOwner = AccountRootEntryW(cidOwner, outerSb);
auto const page = (*sleClaimID)[sfOwnerNode];
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();
AccountID const thisDoor = sleBridge[sfAccount];
auto const sleDoor = [&] { return view.read(keylet::account(thisDoor)); }();
auto const sleDoor = [&] { return AccountRootEntryR(thisDoor, view); }();
if (!sleDoor)
{
@@ -1036,7 +1036,7 @@ applyCreateAccountAttestations(
// Check reserve
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)
return std::unexpected(tecINSUFFICIENT_RESERVE);
@@ -1145,7 +1145,9 @@ applyCreateAccountAttestations(
return tecINTERNAL; // LCOV_EXCL_LINE
// 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.update(sleDoor);
}
@@ -1429,7 +1431,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
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)
return tecNO_ISSUER;
@@ -1442,12 +1444,12 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
{
// Check reserve
auto const sleAcc = ctx.view.read(keylet::account(account));
auto const sleAcc = AccountRootEntryR(account, ctx.view, ctx.j);
if (!sleAcc)
return terNO_ACCOUNT;
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)
return tecINSUFFICIENT_RESERVE;
@@ -1464,7 +1466,7 @@ XChainCreateBridge::doApply()
auto const reward = ctx_.tx[sfSignatureReward];
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)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1492,10 +1494,11 @@ XChainCreateBridge::doApply()
(*sleBridge)[sfOwnerNode] = *page;
}
increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal);
std::optional<AccountRootEntryW> noSponsor;
sleAcct.increaseOwnerCount(noSponsor, 1);
ctx_.view().insert(sleBridge);
ctx_.view().update(sleAcct);
sleAcct.update();
return tesSUCCESS;
}
@@ -1575,7 +1578,7 @@ BridgeModify::doApply()
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
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)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1633,7 +1636,7 @@ XChainClaim::preclaim(PreclaimContext const& ctx)
return tecNO_ENTRY;
}
if (!ctx.view.read(keylet::account(ctx.tx[sfDestination])))
if (!AccountRootEntryR(ctx.tx[sfDestination], ctx.view))
{
return tecNO_DST;
}
@@ -1738,7 +1741,7 @@ XChainClaim::doApply()
// `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
// 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 sleClaimID = psb.peek(claimIDKeylet);
@@ -1919,7 +1922,7 @@ XChainCommit::doApply()
auto const amount = ctx_.tx[sfAmount];
auto const bridgeSpec = ctx_.tx[sfXChainBridge];
auto const sleAccount = psb.read(keylet::account(account));
auto const sleAccount = AccountRootEntryR(account, psb);
if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1990,12 +1993,12 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx)
{
// Check reserve
auto const sleAcc = ctx.view.read(keylet::account(account));
auto const sleAcc = AccountRootEntryR(account, ctx.view, ctx.j);
if (!sleAcc)
return terNO_ACCOUNT;
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)
return tecINSUFFICIENT_RESERVE;
}
@@ -2011,7 +2014,7 @@ XChainCreateClaimID::doApply()
auto const reward = ctx_.tx[sfSignatureReward];
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)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -2053,11 +2056,12 @@ XChainCreateClaimID::doApply()
(*sleClaimID)[sfOwnerNode] = *page;
}
increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal);
std::optional<AccountRootEntryW> noSponsor;
sleAcct.increaseOwnerCount(noSponsor, 1);
ctx_.view().insert(sleClaimID);
ctx_.view().update(sleBridge);
ctx_.view().update(sleAcct);
sleAcct.update();
return tesSUCCESS;
}
@@ -2195,7 +2199,7 @@ XChainCreateAccountCommit::doApply()
STAmount const reward = ctx_.tx[sfSignatureReward];
STXChainBridge const bridge = ctx_.tx[sfXChainBridge];
auto const sle = psb.peek(keylet::account(account));
auto const sle = AccountRootEntryW(account, psb);
if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE
+22 -18
View File
@@ -7,6 +7,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -112,8 +113,8 @@ CheckCash::preclaim(PreclaimContext const& ctx)
// LCOV_EXCL_STOP
}
{
auto const sleSrc = ctx.view.read(keylet::account(srcId));
auto const sleDst = ctx.view.read(keylet::account(dstId));
auto const sleSrc = AccountRootEntryR(srcId, ctx.view);
auto const sleDst = AccountRootEntryR(dstId, ctx.view);
if (!sleSrc || !sleDst)
{
// If the check exists this should never occur.
@@ -201,7 +202,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
auto const sleTrustLine =
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)
{
JLOG(ctx.j.warn()) << "Can't receive IOUs from "
@@ -253,7 +254,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
return tesSUCCESS;
},
[&](MPTIssue const& issue) -> TER {
auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
auto const sleIssuer = AccountRootEntryR(issuerId, ctx.view);
if (!sleIssuer)
{
JLOG(ctx.j.warn()) << "Can't receive MPTs from "
@@ -406,23 +407,18 @@ CheckCash::doApply()
optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)};
auto applyViewContext = ApplyViewContext({.view = psb, .tx = ctx_.tx});
auto const sponsorSle = getTxReserveSponsor(applyViewContext);
auto sponsorSle = getTxReserveSponsor(applyViewContext);
if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE
// Check reserve. Return destination account SLE if enough reserve,
// otherwise return nullptr.
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?
if (auto const ret = checkReserve(
applyViewContext,
sleDst,
preFeeBalance_,
*sponsorSle,
{.ownerCountDelta = 1},
j_);
if (auto const ret = sleDst.checkReserve(
applyViewContext, preFeeBalance_, *sponsorSle, {.ownerCountDelta = 1});
!isTesSuccess(ret))
{
JLOG(j_.trace()) << "Trust line does not exist. "
@@ -430,7 +426,7 @@ CheckCash::doApply()
return nullptr;
}
return sleDst;
return sleDst.mutableRawSle();
};
std::optional<Keylet> trustLineKey;
@@ -486,8 +482,10 @@ CheckCash::doApply()
Issue(currency, accountID_), // limit of zero
0, // quality in
0, // quality out
*sponsorSle, // sponsor
viewJ); // journal
sponsorSle->has_value() // sponsor
? (*sponsorSle)->mutableRawSle()
: SLE::pointer{},
viewJ); // journal
!isTesSuccess(ter))
{
return ter;
@@ -533,8 +531,14 @@ CheckCash::doApply()
if (sleDst == nullptr)
return tecINSUFFICIENT_RESERVE;
if (auto const err =
checkCreateMPT(psb, mptID, accountID_, *sponsorSle, 0, j_);
if (auto const err = checkCreateMPT(
psb,
mptID,
accountID_,
sponsorSle->has_value() ? (*sponsorSle)->mutableRawSle()
: SLE::pointer{},
0,
j_);
!isTesSuccess(err))
{
return err;
@@ -4,7 +4,7 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -79,7 +79,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
{
AccountID const dstId{ctx.tx[sfDestination]};
AccountID const srcId{ctx.tx[sfAccount]};
auto const sleDst = ctx.view.read(keylet::account(dstId));
auto const sleDst = AccountRootEntryR(dstId, ctx.view);
if (!sleDst)
{
JLOG(ctx.j.warn()) << "Destination account does not exist.";
@@ -94,7 +94,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
// because all writes to pseudo-account discriminator fields **are**
// amendment gated, hence the behaviour of this check will always match the
// currently active amendments.
if (isPseudoAccount(sleDst))
if (sleDst.isPseudoAccount())
return tecNO_PERMISSION;
if (sleDst->isFlag(lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag))
@@ -187,15 +187,15 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
TER
CheckCreate::doApply()
{
auto const sle = view().peek(keylet::account(accountID_));
auto sle = AccountRootEntryW(accountID_, view(), ctx_.journal);
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
// A check counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the
// reserve to pay fees.
if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, ctx_.journal);
if (auto const ret =
sle.checkReserve(ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
// Note that we use the value from the sequence or ticket as the
@@ -220,7 +220,6 @@ CheckCreate::doApply()
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
// destination's owner directory.
if (dstAccountId != accountID_)
@@ -251,7 +250,7 @@ CheckCreate::doApply()
}
// 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);
return tesSUCCESS;
}
@@ -2,6 +2,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -87,8 +88,8 @@ CredentialAccept::doApply()
AccountID const issuer{ctx_.tx[sfIssuer]};
// Both exist as credential object exist itself (checked in preclaim)
auto const sleSubject = view().peek(keylet::account(accountID_));
auto const sleIssuer = view().peek(keylet::account(issuer));
auto sleSubject = AccountRootEntryW(accountID_, view(), j_);
auto sleIssuer = AccountRootEntryW(issuer, view());
if (!sleSubject || !sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -97,13 +98,8 @@ CredentialAccept::doApply()
if (!txSponsorSle)
return txSponsorSle.error(); // LCOV_EXCL_LINE
if (auto const ret = checkReserve(
ctx_.getApplyViewContext(),
sleSubject,
preFeeBalance_,
*txSponsorSle,
{.ownerCountDelta = 1},
j_);
if (auto const ret = sleSubject.checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, *txSponsorSle, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
@@ -130,7 +126,7 @@ CredentialAccept::doApply()
removeSponsorFromLedgerEntry(sleCred);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
increaseOwnerCount(ctx_.getApplyViewContext(), sleSubject, 1, j_);
sleSubject.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
view().update(sleCred);
return tesSUCCESS;
@@ -4,7 +4,7 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -84,7 +84,7 @@ CredentialCreate::preclaim(PreclaimContext const& ctx)
auto const credType(ctx.tx[sfCredentialType]);
auto const subject = ctx.tx[sfSubject];
auto const subjectSle = ctx.view.read(keylet::account(subject));
auto const subjectSle = AccountRootEntryR(subject, ctx.view);
if (!subjectSle)
{
@@ -98,7 +98,7 @@ CredentialCreate::preclaim(PreclaimContext const& ctx)
return tecDUPLICATE;
}
if (ctx.view.rules().enabled(fixCleanup3_3_0) && isPseudoAccount(subjectSle))
if (ctx.view.rules().enabled(fixCleanup3_3_0) && subjectSle.isPseudoAccount())
{
JLOG(ctx.j.trace()) << "Subject is a pseudo-account.";
return tecPSEUDO_ACCOUNT;
@@ -134,12 +134,12 @@ CredentialCreate::doApply()
sleCred->setFieldU32(sfExpiration, *optExp);
}
auto const sleIssuer = view().peek(keylet::account(accountID_));
auto sleIssuer = AccountRootEntryW(accountID_, view(), j_);
if (!sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), sleIssuer, preFeeBalance_, {.ownerCountDelta = 1}, j_);
if (auto const ret = sleIssuer.checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
@@ -159,7 +159,7 @@ CredentialCreate::doApply()
return tecDIR_FULL;
sleCred->setFieldU64(sfIssuerNode, *page);
increaseOwnerCount(ctx_.getApplyViewContext(), sleIssuer, 1, j_);
sleIssuer.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
}
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -52,11 +53,11 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount])))
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)
return tecNO_TARGET;
if (isPseudoAccount(sleAuthorize))
if (sleAuthorize.isPseudoAccount())
return tecPSEUDO_ACCOUNT;
// Deleting the delegate object is invalid if it doesn’t exist.
@@ -72,7 +73,7 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
TER
DelegateSet::doApply()
{
auto const sleOwner = ctx_.view().peek(keylet::account(accountID_));
auto sleOwner = AccountRootEntryW(accountID_, ctx_.view(), ctx_.journal);
if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -98,12 +99,8 @@ DelegateSet::doApply()
if (permissions.empty())
return tecINTERNAL; // LCOV_EXCL_LINE
if (auto const ret = checkReserve(
ctx_.getApplyViewContext(),
sleOwner,
preFeeBalance_,
{.ownerCountDelta = 1},
ctx_.journal);
if (auto const ret = sleOwner.checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
@@ -133,7 +130,7 @@ DelegateSet::doApply()
(*sle)[sfDestinationNode] = *destPage;
ctx_.view().insert(sle);
increaseOwnerCount(ctx_.getApplyViewContext(), sleOwner, 1, ctx_.journal);
sleOwner.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sle);
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
auto const sleOwner = view.peek(keylet::account(delegator));
auto sleOwner = AccountRootEntryW(delegator, view);
if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE
+3 -2
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -119,7 +120,7 @@ AMMBid::preclaim(PreclaimContext const& ctx)
{
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.";
return terNO_ACCOUNT;
@@ -208,7 +209,7 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa
auto validOwner = [&](AccountID const& account) {
// Valid range is 0-19 but the tailing slot pays MinSlotPrice
// 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 {
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -102,11 +103,11 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
{
auto const asset = ctx.tx[sfAsset];
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)
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;
auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
@@ -178,7 +179,7 @@ AMMClawback::applyGuts(Sandbox& sb)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const ammAccount = (*ammSle)[sfAccount];
auto const accountSle = sb.read(keylet::account(ammAccount));
auto const accountSle = AccountRootEntryR(ammAccount, sb);
if (!accountSle)
return tecINTERNAL; // LCOV_EXCL_LINE
+5 -4
View File
@@ -5,6 +5,7 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -133,7 +134,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
if (asset.holds<MPTIssue>() || isXRP(asset))
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 false;
@@ -173,7 +174,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
}
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 false;
};
@@ -237,7 +238,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
[&](Issue const& issue) -> TER {
if (isXRP(issue))
return tesSUCCESS;
auto const sle = ctx.view.read(keylet::account(issue.account));
auto const sle = AccountRootEntryR(issue.account, ctx.view);
if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE
if (sle->isFlag(lsfAllowTrustLineClawback))
@@ -263,7 +264,7 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
auto const ammKeylet = keylet::amm(amount.asset(), amount2.asset());
// Mitigate same account exists possibility
auto const maybeAccount = createPseudoAccount(sb, ammKeylet.key, sfAMMID);
auto const maybeAccount = AccountRootEntryW::createPseudoAccount(sb, ammKeylet.key, sfAMMID);
// AMM account already exists (should not happen)
if (!maybeAccount)
{
@@ -6,8 +6,8 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -344,7 +344,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
if (!ammSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const ammAccountID = (*ammSle)[sfAccount];
auto const accountSle = sb.read(keylet::account(ammAccountID));
auto const accountSle = AccountRootEntryR(ammAccountID, sb);
if (!accountSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
@@ -691,16 +691,15 @@ AMMWithdraw::withdraw(
reserveHandling == ReserveHandling::IgnoreReserve)
return tesSUCCESS;
auto sleAccount = view.peek(keylet::account(account));
auto sleAccount = AccountRootEntryW(account, view, journal);
if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const balance = (*sleAccount)[sfBalance]->xrp();
// See also TrustSet::doApply() and MPTokenAuthorize::authorize()
XRPAmount const reserve(
(ownerCount(sleAccount, journal) < 2)
? XRPAmount(beast::kZero)
: accountReserve(view, sleAccount, journal, {.ownerCountDelta = 1}));
(sleAccount.ownerCount() < 2) ? XRPAmount(beast::kZero)
: sleAccount.reserve({.ownerCountDelta = 1}));
auto const balanceAdj = std::max(priorBalance, balance);
if (balanceAdj < reserve)
@@ -1,6 +1,8 @@
#include <xrpl/tx/transactors/dex/OfferCancel.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/protocol/Indexes.h>
#include <xrpl/protocol/SField.h>
@@ -33,7 +35,7 @@ OfferCancel::preclaim(PreclaimContext const& ctx)
auto const id = ctx.tx[sfAccount];
auto const offerSequence = ctx.tx[sfOfferSequence];
auto const sle = ctx.view.read(keylet::account(id));
auto const sle = AccountRootEntryR(id, ctx.view);
if (!sle)
return terNO_ACCOUNT;
@@ -54,7 +56,7 @@ OfferCancel::doApply()
{
auto const offerSequence = ctx_.tx[sfOfferSequence];
auto const sle = view().read(keylet::account(accountID_));
auto const sle = AccountRootEntryW(accountID_, view());
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
+13 -16
View File
@@ -9,7 +9,7 @@
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/Sandbox.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/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -181,7 +181,7 @@ OfferCreate::preclaim(PreclaimContext const& ctx)
auto const cancelSequence = ctx.tx[~sfOfferSequence];
auto const sleCreator = ctx.view.read(keylet::account(id));
auto const sleCreator = AccountRootEntryR(id, ctx.view);
if (!sleCreator)
return terNO_ACCOUNT;
@@ -288,15 +288,14 @@ OfferCreate::checkAcceptAsset(
// Only valid for custom currencies
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)
{
JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: "
<< to_string(asset.getIssuer());
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_ACCOUNT}
: TER{tecNO_ISSUER};
return ((flags & TapRetry) != 0u) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER};
}
// An account cannot create a trustline to itself, so no line can exist
@@ -319,8 +318,7 @@ OfferCreate::checkAcceptAsset(
{
JLOG(j.debug()) << "delay: can't receive IOUs from issuer with "
"DisallowIncomingTrustline set";
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_LINE}
: TER{tecNO_LINE};
return ((flags & TapRetry) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE};
}
}
@@ -328,8 +326,7 @@ OfferCreate::checkAcceptAsset(
{
if (!trustLine)
{
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_LINE}
: TER{tecNO_LINE};
return ((flags & TapRetry) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE};
}
// Entries have a canonical representation, determined by a
@@ -345,8 +342,7 @@ OfferCreate::checkAcceptAsset(
JLOG(j.debug()) << "delay: can't receive IOUs from "
"issuer without auth.";
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_AUTH}
: TER{tecNO_AUTH};
return ((flags & TapRetry) != 0u) ? TER{terNO_AUTH} : TER{tecNO_AUTH};
}
}
@@ -711,14 +707,14 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
// Not XRP or MPT
if (!saTakerPays.integral())
{
auto const sle = sb.read(keylet::account(uPaysIssuerID));
auto const sle = AccountRootEntryR(uPaysIssuerID, sb);
if (sle && sle->isFieldPresent(sfTickSize))
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
}
// Not XRP or MPT
if (!saTakerGets.integral())
{
auto const sle = sb.read(keylet::account(uGetsIssuerID));
auto const sle = AccountRootEntryR(uGetsIssuerID, sb);
if (sle && sle->isFieldPresent(sfTickSize))
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
}
@@ -888,12 +884,12 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
return {tesSUCCESS, true};
}
auto const sleCreator = sb.peek(keylet::account(accountID_));
auto sleCreator = AccountRootEntryW(accountID_, sb, viewJ);
if (!sleCreator)
return {tefINTERNAL, false};
{
XRPAmount const reserve = accountReserve(sb, sleCreator, viewJ, {.ownerCountDelta = 1});
XRPAmount const reserve = sleCreator.reserve({.ownerCountDelta = 1});
if (preFeeBalance_ < reserve)
{
// If we are here, the signing account had an insufficient reserve
@@ -927,7 +923,8 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
}
// 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()) << " : "
<< to_string(saTakerGets.asset())
+2 -1
View File
@@ -4,6 +4,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
@@ -46,7 +47,7 @@ DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, b
// LCOV_EXCL_STOP
}
auto const sleOwner = view.peek(keylet::account(owner));
auto sleOwner = AccountRootEntryW(owner, view);
if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE
+7 -6
View File
@@ -3,7 +3,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.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/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
@@ -20,6 +20,7 @@
#include <cstddef>
#include <memory>
#include <optional>
namespace xrpl {
@@ -65,15 +66,14 @@ DIDSet::preflight(PreflightContext const& ctx)
static TER
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)
return tefINTERNAL; // LCOV_EXCL_LINE
// Check reserve availability for new object creation
{
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
auto const reserve =
accountReserve(ctx.view(), sleAccount, ctx.journal, {.ownerCountDelta = 1});
auto const reserve = sleAccount.reserve({.ownerCountDelta = 1});
if (balance < reserve)
return tecINSUFFICIENT_RESERVE;
@@ -90,8 +90,9 @@ addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*sle)[sfOwnerNode] = *page;
}
increaseOwnerCount(ctx.view(), sleAccount, {}, 1, ctx.journal);
ctx.view().update(sleAccount);
std::optional<AccountRootEntryW> noSponsor;
sleAccount.increaseOwnerCount(noSponsor, 1);
sleAccount.update();
return tesSUCCESS;
}
@@ -3,7 +3,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -95,12 +95,13 @@ EscrowCancel::preclaim(PreclaimContext const& ctx)
if (ctx.view.rules().enabled(featureTokenEscrow))
{
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
EscrowEntryR const escrow(ctx.tx[sfOwner], seqProxy, ctx.view);
if (!escrow)
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy);
auto const slep = ctx.view.read(k);
if (!slep)
return tecNO_TARGET;
AccountID const account = (*escrow)[sfAccount];
STAmount const amount = (*escrow)[sfAmount];
AccountID const account = (*slep)[sfAccount];
STAmount const amount = (*slep)[sfAmount];
if (!isXRP(amount))
{
@@ -120,8 +121,9 @@ TER
EscrowCancel::doApply()
{
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
EscrowEntryW escrow(ctx_.tx[sfOwner], seqProxy, ctx_.view(), j_);
if (!escrow)
auto const k = keylet::escrow(ctx_.tx[sfOwner], seqProxy);
auto const slep = ctx_.view().peek(k);
if (!slep)
{
if (ctx_.view().rules().enabled(featureTokenEscrow))
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -132,19 +134,19 @@ EscrowCancel::doApply()
auto const now = ctx_.view().header().parentCloseTime;
// No cancel time specified: can't execute at all.
if (!(*escrow)[~sfCancelAfter])
if (!(*slep)[~sfCancelAfter])
return tecNO_PERMISSION;
// Too soon: can't execute before the cancel time.
if (!after(now, (*escrow)[sfCancelAfter]))
if (!after(now, (*slep)[sfCancelAfter]))
return tecNO_PERMISSION;
AccountID const account = (*escrow)[sfAccount];
AccountID const account = (*slep)[sfAccount];
// Remove escrow from owner directory
{
auto const page = (*escrow)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, escrow.key(), true))
auto const page = (*slep)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
@@ -154,10 +156,9 @@ EscrowCancel::doApply()
}
// Remove escrow from recipient's owner directory, if present.
if (auto const optPage = (*escrow)[~sfDestinationNode]; optPage)
if (auto const optPage = (*slep)[~sfDestinationNode]; optPage)
{
if (!ctx_.view().dirRemove(
keylet::ownerDir((*escrow)[sfDestination]), *optPage, escrow.key(), true))
if (!ctx_.view().dirRemove(keylet::ownerDir((*slep)[sfDestination]), *optPage, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -166,14 +167,14 @@ EscrowCancel::doApply()
}
}
auto const sle = ctx_.view().peek(keylet::account(account));
STAmount const amount = escrow->getFieldAmount(sfAmount);
auto sle = AccountRootEntryW(account, ctx_.view());
STAmount const amount = slep->getFieldAmount(sfAmount);
// The return can re-create a holding the owner deleted while the escrow
// was pending; the removed escrow must not be counted against its reserve.
bool const recycleReserve = ctx_.view().rules().enabled(fixCleanup3_4_0);
if (recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), sle, escrow.mutableRawSle(), 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal);
// Transfer amount back to the owner
if (isXRP(amount))
@@ -192,7 +193,7 @@ EscrowCancel::doApply()
return escrowUnlockApplyHelper<T>(
ctx_.getApplyViewContext(),
kParityRate,
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : escrow.mutableRawSle(),
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle.mutableRawSle() : slep,
preFeeBalance_,
amount,
issuer,
@@ -206,9 +207,9 @@ EscrowCancel::doApply()
return ret; // LCOV_EXCL_LINE
// Remove escrow from issuers owner directory, if present.
if (auto const optPage = (*escrow)[~sfIssuerNode]; optPage)
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
{
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, escrow.key(), true))
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -219,10 +220,10 @@ EscrowCancel::doApply()
}
if (!recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), sle, escrow.mutableRawSle(), 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal);
// Remove escrow from ledger
escrow.erase();
ctx_.view().erase(slep);
return tesSUCCESS;
}
@@ -7,8 +7,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -208,7 +207,7 @@ escrowCreatePreclaimHelper<Issue>(
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)
return tecNO_ISSUER;
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
@@ -348,7 +347,7 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
AccountID const account{ctx.tx[sfAccount]};
AccountID const dest{ctx.tx[sfDestination]};
auto const sled = ctx.view.read(keylet::account(dest));
auto const sled = AccountRootEntryR(dest, ctx.view);
if (!sled)
return tecNO_DST;
@@ -356,7 +355,7 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
// because all writes to pseudo-account discriminator fields **are**
// amendment gated, hence the behaviour of this check will always match the
// currently active amendments.
if (isPseudoAccount(sled))
if (sled.isPseudoAccount())
return tecNO_PERMISSION;
if (!isXRP(amount))
@@ -434,7 +433,7 @@ EscrowCreate::doApply()
if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter]))
return tecNO_PERMISSION;
auto const sle = ctx_.view().peek(keylet::account(accountID_));
auto sle = AccountRootEntryW(accountID_, ctx_.view(), j_);
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -448,7 +447,7 @@ EscrowCreate::doApply()
// unsponsored this hits the source branch and validates the
// source's pre-lock balance against base + (currentOC+1)*increment.
if (auto const ret =
checkReserve(ctx_.getApplyViewContext(), sle, balance, {.ownerCountDelta = 1}, j_);
sle.checkReserve(ctx_.getApplyViewContext(), balance, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
@@ -465,15 +464,15 @@ EscrowCreate::doApply()
// - sponsored: 0 — sponsor covers the new owner increment, so the
// source only owes reserve for its current owners.
// - unsponsored: 1 — source owes reserve including the new increment.
auto const sourceReserve = accountReserve(
ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
auto const sourceReserve =
sle.reserve({.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
if (balance - STAmount(amount).xrp() < sourceReserve)
return tecUNFUNDED;
}
// 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)
return tecNO_DST; // LCOV_EXCL_LINE
if (sled->isFlag(lsfRequireDestTag) && !ctx_.tx[~sfDestinationTag])
@@ -482,31 +481,30 @@ EscrowCreate::doApply()
// Create escrow in ledger. Note that we use the value from the
// sequence or ticket. For more explanation see comments in SeqProxy.h.
EscrowEntryW escrow(accountID_, ctx_.tx.getSeqProxy(), ctx_.view(), j_);
Keylet const escrowKeylet = escrow.keylet();
escrow.newSLE();
(*escrow)[sfAmount] = amount;
(*escrow)[sfAccount] = accountID_;
(*escrow)[~sfCondition] = ctx_.tx[~sfCondition];
(*escrow)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
(*escrow)[sfDestination] = ctx_.tx[sfDestination];
(*escrow)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
(*escrow)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
(*escrow)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
Keylet const escrowKeylet = keylet::escrow(accountID_, ctx_.tx.getSeqProxy());
auto const slep = std::make_shared<SLE>(escrowKeylet);
(*slep)[sfAmount] = amount;
(*slep)[sfAccount] = accountID_;
(*slep)[~sfCondition] = ctx_.tx[~sfCondition];
(*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
(*slep)[sfDestination] = ctx_.tx[sfDestination];
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
{
(*escrow)[sfSequence] = ctx_.tx.getSeqProxy().value();
(*slep)[sfSequence] = ctx_.tx.getSeqProxy().value();
}
if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
{
auto const xferRate = transferRate(ctx_.view(), amount);
if (xferRate != kParityRate)
(*escrow)[sfTransferRate] = xferRate.value;
(*slep)[sfTransferRate] = xferRate.value;
}
escrow.insert();
ctx_.view().insert(slep);
// Add escrow to sender's owner directory
{
@@ -514,7 +512,7 @@ EscrowCreate::doApply()
keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*escrow)[sfOwnerNode] = *page;
(*slep)[sfOwnerNode] = *page;
}
// If it's not a self-send, add escrow to recipient's owner directory.
@@ -525,7 +523,7 @@ EscrowCreate::doApply()
ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*escrow)[sfDestinationNode] = *page;
(*slep)[sfDestinationNode] = *page;
}
// IOU escrow objects are added to the issuer's owner directory to help
@@ -538,7 +536,7 @@ EscrowCreate::doApply()
ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*escrow)[sfIssuerNode] = *page;
(*slep)[sfIssuerNode] = *page;
}
// Deduct owner's balance
@@ -560,9 +558,9 @@ EscrowCreate::doApply()
}
// increment owner count
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), escrow.mutableRawSle());
ctx_.view().update(sle);
sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
sle.update();
return tesSUCCESS;
}
@@ -9,7 +9,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h>
@@ -212,12 +212,13 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
if (ctx.view.rules().enabled(featureTokenEscrow))
{
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
EscrowEntryR const escrow(ctx.tx[sfOwner], seqProxy, ctx.view);
if (!escrow)
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy);
auto const slep = ctx.view.read(k);
if (!slep)
return tecNO_TARGET;
AccountID const dest = (*escrow)[sfDestination];
STAmount const amount = (*escrow)[sfAmount];
AccountID const dest = (*slep)[sfDestination];
STAmount const amount = (*slep)[sfAmount];
if (!isXRP(amount))
{
@@ -237,8 +238,9 @@ TER
EscrowFinish::doApply()
{
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
EscrowEntryW escrow(ctx_.tx[sfOwner], seqProxy, ctx_.view(), j_);
if (!escrow)
auto const k = keylet::escrow(ctx_.tx[sfOwner], seqProxy);
auto const slep = ctx_.view().peek(k);
if (!slep)
{
if (ctx_.view().rules().enabled(featureTokenEscrow))
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -251,11 +253,11 @@ EscrowFinish::doApply()
auto const now = ctx_.view().header().parentCloseTime;
// Too soon: can't execute before the finish time
if ((*escrow)[~sfFinishAfter] && !after(now, (*escrow)[sfFinishAfter]))
if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
return tecNO_PERMISSION;
// Too late: can't execute after the cancel time
if ((*escrow)[~sfCancelAfter] && after(now, (*escrow)[sfCancelAfter]))
if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
return tecNO_PERMISSION;
// Check cryptocondition fulfillment
@@ -295,7 +297,7 @@ EscrowFinish::doApply()
return tecCRYPTOCONDITION_ERROR;
// Check against condition in the ledger entry:
auto const cond = (*escrow)[~sfCondition];
auto const cond = (*slep)[~sfCondition];
// If a condition wasn't specified during creation,
// one shouldn't be included now.
@@ -312,22 +314,22 @@ EscrowFinish::doApply()
}
// NOTE: Escrow payments cannot be used to fund accounts.
AccountID const destID = (*escrow)[sfDestination];
auto const sled = ctx_.view().peek(keylet::account(destID));
AccountID const destID = (*slep)[sfDestination];
auto sled = AccountRootEntryW(destID, ctx_.view());
if (!sled)
return tecNO_DST;
if (auto err =
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal);
if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), accountID_, destID, sled.rawSle(), ctx_.journal);
!isTesSuccess(err))
return err;
AccountID const account = (*escrow)[sfAccount];
AccountID const account = (*slep)[sfAccount];
// Remove escrow from owner directory
{
auto const page = (*escrow)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, escrow.key(), true))
auto const page = (*slep)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
@@ -337,9 +339,9 @@ EscrowFinish::doApply()
}
// Remove escrow from recipient's owner directory, if present.
if (auto const optPage = (*escrow)[~sfDestinationNode])
if (auto const optPage = (*slep)[~sfDestinationNode])
{
if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, escrow.key(), true))
if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -354,9 +356,9 @@ EscrowFinish::doApply()
bool const recycleReserve =
ctx_.view().rules().enabled(featureSponsor) || ctx_.view().rules().enabled(fixCleanup3_4_0);
if (recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), account, escrow.mutableRawSle(), 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
STAmount const amount = escrow->getFieldAmount(sfAmount);
STAmount const amount = slep->getFieldAmount(sfAmount);
// Transfer amount to destination
if (isXRP(amount))
{
@@ -367,8 +369,8 @@ EscrowFinish::doApply()
if (!ctx_.view().rules().enabled(featureTokenEscrow))
return temDISABLED; // LCOV_EXCL_LINE
Rate lockedRate = escrow->isFieldPresent(sfTransferRate)
? xrpl::Rate(escrow->getFieldU32(sfTransferRate))
Rate lockedRate = slep->isFieldPresent(sfTransferRate)
? xrpl::Rate(slep->getFieldU32(sfTransferRate))
: kParityRate;
auto const issuer = amount.getIssuer();
bool const createAsset = destID == accountID_;
@@ -377,7 +379,7 @@ EscrowFinish::doApply()
return escrowUnlockApplyHelper<T>(
ctx_.getApplyViewContext(),
lockedRate,
sled,
sled.mutableRawSle(),
preFeeBalance_,
amount,
issuer,
@@ -391,9 +393,9 @@ EscrowFinish::doApply()
return ret;
// Remove escrow from issuers owner directory, if present.
if (auto const optPage = (*escrow)[~sfIssuerNode]; optPage)
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
{
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, escrow.key(), true))
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -403,13 +405,13 @@ EscrowFinish::doApply()
}
}
ctx_.view().update(sled);
sled.update();
if (!recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), account, escrow.mutableRawSle(), 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
// Remove escrow from ledger
escrow.erase();
ctx_.view().erase(slep);
return tesSUCCESS;
}
@@ -6,6 +6,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.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
// pseudo-account, but we don't know yet whether it is.
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 (!sle)
@@ -319,7 +320,7 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
return tecINTERNAL; // tecINSUFFICIENT_FUNDS; LCOV_EXCL_LINE
// 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)
{
// LCOV_EXCL_START
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -161,7 +162,7 @@ LoanBrokerDelete::doApply()
if (auto ter = removeEmptyHolding(ctx_.getApplyViewContext(), brokerPseudoID, vaultAsset, j_))
return ter;
auto brokerPseudoSLE = view().peek(keylet::account(brokerPseudoID));
auto brokerPseudoSLE = AccountRootEntryW(brokerPseudoID, view());
if (!brokerPseudoSLE)
return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -183,10 +184,10 @@ LoanBrokerDelete::doApply()
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)
return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -5,7 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/ledger/helpers/VaultHelpers.h>
@@ -23,6 +23,7 @@
#include <xrpl/tx/Transactor.h>
#include <memory>
#include <optional>
#include <vector>
namespace xrpl {
@@ -236,7 +237,7 @@ LoanBrokerSet::doApply()
auto const vaultAsset = sleVault->at(sfAsset);
auto const sequence = tx.getSeqProxy();
auto owner = view.peek(keylet::account(accountID_));
auto owner = AccountRootEntryW(accountID_, view, j_);
if (!owner)
{
// This should be impossible
@@ -254,11 +255,13 @@ LoanBrokerSet::doApply()
// Increases the owner count by two: one for the LoanBroker object, and
// one for the pseudo-account.
increaseOwnerCount(view, owner, {}, 2, j_);
if (preFeeBalance_ < accountReserve(view, owner, j_))
std::optional<AccountRootEntryW> noSponsor;
owner.increaseOwnerCount(noSponsor, 2);
if (preFeeBalance_ < owner.reserve())
return tecINSUFFICIENT_RESERVE;
auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID);
auto maybePseudo =
AccountRootEntryW::createPseudoAccount(view, broker->key(), sfLoanBrokerID);
if (!maybePseudo)
return maybePseudo.error(); // LCOV_EXCL_LINE
auto& pseudo = *maybePseudo;
@@ -4,6 +4,7 @@
#include <xrpl/basics/Number.h> // IWYU pragma: keep
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/protocol/Indexes.h>
@@ -80,7 +81,7 @@ LoanDelete::doApply()
if (!loanSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const borrower = loanSle->at(sfBorrower);
auto const borrowerSle = view.peek(keylet::account(borrower));
auto borrowerSle = AccountRootEntryW(borrower, view);
if (!borrowerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/to_string.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -56,7 +57,7 @@ conservationBalance(ReadView const& view, AccountID const& id, Asset const& asse
{
if (isXRP(asset))
{
auto const sle = view.read(keylet::account(id));
auto const sle = AccountRootEntryR(id, view);
if (!sle)
return STAmount{asset}; // LCOV_EXCL_LINE
return view.balanceHookIOU(id, xrpAccount(), sle->getFieldAmount(sfBalance));
@@ -391,7 +392,7 @@ LoanPay::doApply()
}();
auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount;
auto const brokerPayeeSle = view.peek(keylet::account(brokerPayee));
auto const brokerPayeeSle = AccountRootEntryW(brokerPayee, view);
if (!sendBrokerFeeToOwner)
{
// 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/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -91,8 +92,7 @@ LoanSet::preflight(PreflightContext const& ctx)
// A proposed LoanSet is stored unsigned; its CounterpartySignature is
// collected on-ledger afterward, so its absence here is expected, not an
// error (On-Chain Cosigner spec §5.3.1.2).
if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig &&
(ctx.flags & ApplyFlags::Proposal) == ApplyFlags::None)
if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig && (ctx.flags & TapProposal) == 0)
{
JLOG(ctx.j.warn()) << "LoanSet transaction must have a CounterpartySignature.";
return temBAD_SIGNER;
@@ -306,7 +306,7 @@ LoanSet::preclaim(PreclaimContext const& ctx)
auto const brokerPseudo = brokerSle->at(sfAccount);
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
// signature check with terNO_ACCOUNT.
@@ -444,7 +444,7 @@ LoanSet::doApply()
if (!brokerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const brokerOwner = brokerSle->at(sfOwner);
auto const brokerOwnerSle = view.peek(keylet::account(brokerOwner));
auto const brokerOwnerSle = AccountRootEntryW(brokerOwner, view);
if (!brokerOwnerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -456,14 +456,14 @@ LoanSet::doApply()
auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner);
auto const borrower = counterparty == brokerOwner ? accountID_ : counterparty;
auto const borrowerSle = view.peek(keylet::account(borrower));
auto borrowerSle = AccountRootEntryW(borrower, view, j_);
if (!borrowerSle)
{
return tefBAD_LEDGER; // LCOV_EXCL_LINE
}
auto const brokerPseudo = brokerSle->at(sfAccount);
auto const brokerPseudoSle = view.peek(keylet::account(brokerPseudo));
auto const brokerPseudoSle = AccountRootEntryW(brokerPseudo, view);
if (!brokerPseudoSle)
{
return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -582,12 +582,13 @@ LoanSet::doApply()
}
}
increaseOwnerCount(view, borrowerSle, {}, 1, j_);
std::optional<AccountRootEntryW> noSponsor;
borrowerSle.increaseOwnerCount(noSponsor, 1);
{
auto const balance =
accountID_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp();
if (balance < accountReserve(view, borrowerSle, j_))
if (balance < borrowerSle.reserve())
return tecINSUFFICIENT_RESERVE;
}
@@ -5,7 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.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/TokenHelpers.h>
#include <xrpl/protocol/Asset.h>
@@ -376,7 +376,7 @@ NFTokenAcceptOffer::transferNFToken(
!isTesSuccess(ret))
return ret;
auto const sleBuyer = view().read(keylet::account(buyer));
auto const sleBuyer = AccountRootEntryW(buyer, view(), j_);
if (!sleBuyer)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -397,7 +397,7 @@ NFTokenAcceptOffer::transferNFToken(
auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
if (buyerOwnerCountAfter > buyerOwnerCountBefore)
{
if (buyerBalance < accountReserve(view(), sleBuyer, j_))
if (buyerBalance < sleBuyer.reserve())
return tecINSUFFICIENT_RESERVE;
}
@@ -1,6 +1,8 @@
#include <xrpl/tx/transactors/nft/NFTokenBurn.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/protocol/Indexes.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 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)
return tecNO_PERMISSION;
@@ -68,10 +70,10 @@ NFTokenBurn::doApply()
if (!isTesSuccess(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;
view().update(issuer);
issuer.update();
}
// Delete up to 500 offers in total.
@@ -2,12 +2,12 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.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/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/InnerObjectFormats.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
@@ -186,7 +186,7 @@ NFTokenMint::preclaim(PreclaimContext const& ctx)
// transaction. Check that and verify that this is allowed:
if (auto issuer = ctx.tx[~sfIssuer])
{
auto const sle = ctx.view.read(keylet::account(*issuer));
auto const sle = AccountRootEntryR(*issuer, ctx.view);
if (!sle)
return tecNO_ISSUER;
@@ -225,8 +225,8 @@ NFTokenMint::doApply()
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
auto const tokenSeq = [this, &issuer]() -> std::expected<std::uint32_t, TER> {
auto const root = view().peek(keylet::account(issuer));
if (root == nullptr)
auto root = AccountRootEntryW(issuer, view());
if (!root)
{
// Should not happen. Checked in preclaim.
return std::unexpected(tecNO_ISSUER);
@@ -271,7 +271,7 @@ NFTokenMint::doApply()
if (tokenSeq + 1u == 0u || tokenSeq < offset)
return std::unexpected(tecMAX_SEQUENCE_REACHED);
ctx_.view().update(root);
root.update();
return tokenSeq;
}();
@@ -279,7 +279,7 @@ NFTokenMint::doApply()
return (tokenSeq.error());
std::uint32_t const ownerCountBefore =
view().read(keylet::account(accountID_))->getFieldU32(sfOwnerCount);
AccountRootEntryW(accountID_, view())->getFieldU32(sfOwnerCount);
// Assemble the new NFToken.
SOTemplate const* nfTokenTemplate =
@@ -332,14 +332,14 @@ NFTokenMint::doApply()
// allows NFTs to be added to the page (and burn fees) without
// requiring the reserve to be met each time. The reserve is
// 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)
return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ownerCountAfter = sleAccount->getFieldU32(sfOwnerCount);
ownerCountAfter > ownerCountBefore)
{
if (preFeeBalance_ < accountReserve(view(), sleAccount, j_))
if (preFeeBalance_ < sleAccount.reserve())
return tecINSUFFICIENT_RESERVE;
}
return tesSUCCESS;
@@ -2,9 +2,10 @@
#include <xrpl/basics/base_uint.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/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -47,7 +48,7 @@ NFTokenModify::preclaim(PreclaimContext const& ctx)
// Verify permissions for the issuer
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)
return tecINTERNAL; // LCOV_EXCL_LINE
if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account)
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -67,7 +68,7 @@ OracleDelete::deleteOracle(
// LCOV_EXCL_STOP
}
auto const sleOwner = view.peek(keylet::account(account));
auto sleOwner = AccountRootEntryW(account, view);
if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE
+17 -19
View File
@@ -3,7 +3,7 @@
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/instrumentation.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/OracleHelpers.h>
#include <xrpl/protocol/Feature.h>
@@ -27,6 +27,7 @@
#include <cstdlib>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <utility>
@@ -64,7 +65,7 @@ OracleSet::preflight(PreflightContext const& ctx)
TER
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)
return terNO_ACCOUNT; // LCOV_EXCL_LINE
@@ -172,8 +173,7 @@ OracleSet::preclaim(PreclaimContext const& ctx)
if (pairs.size() > kMaxOracleDataSeries)
return tecARRAY_TOO_LARGE;
auto const reserve =
accountReserve(ctx.view, sleSetter, ctx.j, {.ownerCountDelta = adjustReserve});
auto const reserve = sleSetter.reserve({.ownerCountDelta = adjustReserve});
auto const& balance = sleSetter->getFieldAmount(sfBalance);
if (balance < reserve)
@@ -187,22 +187,20 @@ adjustOracleOwnerCount(ApplyContext& ctx, int count)
{
XRPL_ASSERT(std::abs(count) <= 2, "xrpl::adjustOracleOwnerCount abs(counter) <= 2");
if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount])))
{
if (count > 0)
{
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;
}
auto sleAccount = AccountRootEntryW(ctx.tx[sfAccount], ctx.view(), ctx.journal);
if (!sleAccount)
return false; // LCOV_EXCL_LINE
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
@@ -4,6 +4,7 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -103,11 +104,11 @@ DepositPreauth::preclaim(PreclaimContext const& ctx)
{
// Verify that the Authorize account is present in the ledger.
AccountID const auth{ctx.tx[sfAuthorize]};
auto const sleAuth = ctx.view.read(keylet::account(auth));
auto const sleAuth = AccountRootEntryR(auth, ctx.view);
if (!sleAuth)
return tecNO_TARGET;
if (ctx.view.rules().enabled(fixCleanup3_3_0) && isPseudoAccount(sleAuth))
if (ctx.view.rules().enabled(fixCleanup3_3_0) && sleAuth.isPseudoAccount())
{
JLOG(ctx.j.debug()) << "Authorized account is a pseudo-account.";
return tecPSEUDO_ACCOUNT;
@@ -161,15 +162,15 @@ DepositPreauth::doApply()
auto applyViewContext = ctx_.getApplyViewContext();
if (ctx_.tx.isFieldPresent(sfAuthorize))
{
auto const sleOwner = view().peek(keylet::account(accountID_));
auto sleOwner = AccountRootEntryW(accountID_, view(), j_);
if (!sleOwner)
return {tefINTERNAL};
// A preauth counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the
// reserve to pay fees.
if (auto const ret = checkReserve(
applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_);
if (auto const ret =
sleOwner.checkReserve(applyViewContext, preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
@@ -195,7 +196,7 @@ DepositPreauth::doApply()
slePreauth->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve.
increaseOwnerCount(applyViewContext, sleOwner, 1, j_);
sleOwner.increaseOwnerCount(applyViewContext, 1);
addSponsorToLedgerEntry(applyViewContext, slePreauth);
}
else if (ctx_.tx.isFieldPresent(sfUnauthorize))
@@ -206,15 +207,15 @@ DepositPreauth::doApply()
}
else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
{
auto const sleOwner = view().peek(keylet::account(accountID_));
auto sleOwner = AccountRootEntryW(accountID_, view(), j_);
if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE
// A preauth counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the
// reserve to pay fees.
if (auto const ret = checkReserve(
applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_);
if (auto const ret =
sleOwner.checkReserve(applyViewContext, preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
@@ -254,7 +255,7 @@ DepositPreauth::doApply()
slePreauth->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve.
increaseOwnerCount(applyViewContext, sleOwner, 1, j_);
sleOwner.increaseOwnerCount(applyViewContext, 1);
addSponsorToLedgerEntry(applyViewContext, slePreauth);
}
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.
auto const sleOwner = view.peek(keylet::account(account));
auto sleOwner = AccountRootEntryW(account, view);
if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE
+30 -15
View File
@@ -6,7 +6,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/PermissionedDEXHelpers.h>
@@ -386,7 +386,7 @@ Payment::preclaim(PreclaimContext const& ctx)
STAmount const dstAmount(ctx.tx[sfAmount]);
auto const k = keylet::account(dstAccountID);
auto const sleDst = ctx.view.read(k);
auto const sleDst = AccountRootEntryR(k, ctx.view);
if (!sleDst)
{
@@ -564,19 +564,19 @@ Payment::doApply()
// Open a ledger for editing.
auto const k = keylet::account(dstAccountID);
SLE::pointer sleDst = view().peek(k);
AccountRootEntryW sleDst(k, view());
if (!sleDst)
{
// Create the account.
sleDst = std::make_shared<SLE>(k);
sleDst.newSLE();
sleDst->setAccountID(sfAccount, dstAccountID);
sleDst->setFieldU32(sfSequence, view().seq());
sleDst->setFieldAmount(sfBalance, XRPAmount(beast::kZero));
if (ctx_.tx.isFlag(tfSponsorCreatedAccount))
{
auto const sponsor = view().peek(keylet::account(accountID_));
auto sponsor = AccountRootEntryW(accountID_, view());
if (!sponsor)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const currentSponsoringAccountCount =
@@ -591,18 +591,18 @@ Payment::doApply()
}
sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1);
addSponsorToLedgerEntry(sleDst, sponsor);
view().update(sponsor);
addSponsorToLedgerEntry(sleDst.mutableRawSle(), sponsor.rawSle());
sponsor.update();
}
view().insert(sleDst);
sleDst.insert();
}
else
{
// Tell the engine that we are intending to change the destination
// account. The source account gets always charged a fee so it's always
// marked as modified.
view().update(sleDst);
sleDst.update();
}
bool const mpTokensV2 = view().rules().enabled(featureMPTokensV2);
@@ -621,7 +621,12 @@ Payment::doApply()
// 2. If Account is deposit preauthorized by destination.
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))
return err;
@@ -691,7 +696,12 @@ Payment::doApply()
return ter;
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))
return err;
@@ -757,13 +767,13 @@ Payment::doApply()
// Direct XRP payment.
auto const sleSrc = view().peek(keylet::account(accountID_));
auto sleSrc = AccountRootEntryW(accountID_, view(), j_);
if (!sleSrc)
return tefINTERNAL; // LCOV_EXCL_LINE
// the number of reserves in this ledger for this account that require a
// 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_).
bool const accountIsPayer = ctx_.tx.getFeePayerID() == accountID_;
@@ -790,7 +800,7 @@ Payment::doApply()
// transaction types. Note, this is not amendment-gated because all writes
// to pseudo-account discriminator fields **are** amendment gated, hence the
// behaviour of this check will always match the active amendments.
if (isPseudoAccount(sleDst))
if (sleDst.isPseudoAccount())
return tecNO_PERMISSION;
// The source account does have enough money. Make sure the
@@ -820,7 +830,12 @@ Payment::doApply()
if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve)
{
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))
return err;
}
@@ -3,11 +3,11 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/PayChan.h>
@@ -159,12 +159,12 @@ PaymentChannelClaim::doApply()
return tecUNFUNDED_PAYMENT;
}
auto const sled = ctx_.view().peek(keylet::account(dst));
auto sled = AccountRootEntryW(dst, ctx_.view());
if (!sled)
return tecNO_DST;
if (auto err =
verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal);
if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.view(), txAccount, dst, sled.rawSle(), ctx_.journal);
!isTesSuccess(err))
return err;
@@ -173,7 +173,7 @@ PaymentChannelClaim::doApply()
XRPL_ASSERT(
reqDelta >= beast::kZero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta");
(*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta;
ctx_.view().update(sled);
sled.update();
ctx_.view().update(slep);
}
@@ -5,7 +5,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/Feature.h>
@@ -73,7 +73,7 @@ TER
PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
{
auto const account = ctx.tx[sfAccount];
auto const sle = ctx.view.read(keylet::account(account));
auto const sle = AccountRootEntryR(account, ctx.view);
if (!sle)
return terNO_ACCOUNT;
@@ -94,7 +94,7 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
{
// Check destination account
auto const sled = ctx.view.read(keylet::account(dst));
auto const sled = AccountRootEntryR(dst, ctx.view);
if (!sled)
return tecNO_DST;
@@ -111,7 +111,7 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
// writes to pseudo-account discriminator fields **are** amendment
// gated, hence the behaviour of this check will always match the
// currently active amendments.
if (isPseudoAccount(sled))
if (sled.isPseudoAccount())
return tecNO_PERMISSION;
}
@@ -122,7 +122,7 @@ TER
PaymentChannelCreate::doApply()
{
auto const account = ctx_.tx[sfAccount];
auto const sle = ctx_.view().peek(keylet::account(account));
auto sle = AccountRootEntryW(account, ctx_.view(), j_);
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -140,8 +140,8 @@ PaymentChannelCreate::doApply()
// validates the sponsor's reserve + remaining credit. When
// unsponsored this hits the source branch and validates the
// source's pre-lock balance against base + (currentOC+1)*increment.
if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, j_);
if (auto const ret = sle.checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
!isTesSuccess(ret))
return ret;
@@ -157,8 +157,8 @@ PaymentChannelCreate::doApply()
// - sponsored: 0 — sponsor covers the new owner increment, so
// the source only owes reserve for its current owners.
// - unsponsored: 1 — source owes reserve including the new increment.
auto const sourceReserve = accountReserve(
ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
auto const sourceReserve =
sle.reserve({.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve)
return tecUNFUNDED;
}
@@ -210,9 +210,9 @@ PaymentChannelCreate::doApply()
// Deduct owner's balance, increment owner count
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
ctx_.view().update(sle);
sle.update();
return tesSUCCESS;
}
@@ -4,11 +4,10 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/ApplyView.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/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
@@ -82,14 +81,14 @@ PaymentChannelFund::doApply()
ctx_.view().update(slep);
}
auto const sle = ctx_.view().peek(keylet::account(txAccount));
auto sle = AccountRootEntryW(txAccount, ctx_.view(), j_);
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
{
// Check reserve and funds availability
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))
return ret;
@@ -99,12 +98,12 @@ PaymentChannelFund::doApply()
// when a sponsor is present and would ignore the source's post-lock
// balance entirely. Funding an existing channel adds no owned object,
// 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;
}
// 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;
}
@@ -113,7 +112,7 @@ PaymentChannelFund::doApply()
ctx_.view().update(slep);
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
ctx_.view().update(sle);
sle.update();
return tesSUCCESS;
}
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h>
@@ -63,7 +64,7 @@ PermissionedDomainDelete::doApply()
// LCOV_EXCL_STOP
}
auto const ownerSle = view().peek(keylet::account(accountID_));
auto ownerSle = AccountRootEntryW(accountID_, view());
XRPL_ASSERT(
ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0,
"xrpl::PermissionedDomainDelete::doApply : nonzero owner count");
@@ -2,7 +2,7 @@
#include <xrpl/beast/utility/Zero.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/DirectoryHelpers.h>
#include <xrpl/protocol/Feature.h>
@@ -19,6 +19,7 @@
#include <xrpl/tx/Transactor.h>
#include <memory>
#include <optional>
#include <utility>
namespace xrpl {
@@ -80,7 +81,7 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx)
TER
PermissionedDomainSet::doApply()
{
auto const ownerSle = view().peek(keylet::account(accountID_));
auto ownerSle = AccountRootEntryW(accountID_, view(), ctx_.journal);
if (!ownerSle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -109,8 +110,7 @@ PermissionedDomainSet::doApply()
// Create new permissioned domain.
// Check reserve availability for new object creation
auto const balance = STAmount((*ownerSle)[sfBalance]).xrp();
auto const reserve =
accountReserve(ctx_.view(), ownerSle, ctx_.journal, {.ownerCountDelta = 1});
auto const reserve = ownerSle.reserve({.ownerCountDelta = 1});
if (balance < reserve)
return tecINSUFFICIENT_RESERVE;
@@ -130,7 +130,8 @@ PermissionedDomainSet::doApply()
slePd->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve.
increaseOwnerCount(view(), ownerSle, {}, 1, ctx_.journal);
std::optional<AccountRootEntryW> noSponsor;
ownerSle.increaseOwnerCount(noSponsor, 1);
view().insert(slePd);
}
@@ -4,7 +4,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/ProposalHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -54,8 +54,8 @@ TransactionProposalCreate::preflight(PreflightContext const& ctx)
try
{
STTx const stx{STObject{proposedTx}};
auto const inner = xrpl::preflight(
ctx.registry, ctx.rules, stx, ApplyFlags::DryRun | ApplyFlags::Proposal, ctx.j);
auto const inner =
xrpl::preflight(ctx.registry, ctx.rules, stx, TapDryRun | TapProposal, ctx.j);
if (!isTesSuccess(inner.ter))
{
JLOG(ctx.j.debug()) << "TransactionProposalCreate: proposed txn "
@@ -159,7 +159,7 @@ TransactionProposalCreate::preclaim(PreclaimContext const& ctx)
}
AccountID const target = proposedTx.getAccountID(sfAccount);
auto const sleTarget = ctx.view.read(keylet::account(target));
auto const sleTarget = AccountRootEntryR(target, ctx.view);
if (!sleTarget)
{
JLOG(ctx.j.debug()) << "TransactionProposalCreate: target account "
@@ -168,7 +168,7 @@ TransactionProposalCreate::preclaim(PreclaimContext const& ctx)
}
// A pseudo-account cannot authorize a transaction through a SignerList.
if (isPseudoAccount(sleTarget))
if (sleTarget.isPseudoAccount())
return tecNO_PERMISSION;
// Only the target account itself, an account on its SignerList, or (if
@@ -297,7 +297,7 @@ TransactionProposalCreate::preclaim(PreclaimContext const& ctx)
TER
TransactionProposalCreate::doApply()
{
auto const sle = view().peek(keylet::account(accountID_));
auto sle = AccountRootEntryW(accountID_, view(), ctx_.journal);
if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -307,12 +307,10 @@ TransactionProposalCreate::doApply()
// The proposal holds a full transaction plus its collected signatures, so
// it reserves more than a typical ledger entry (5 increments; 10 for a
// proposed Batch).
if (auto const ret = checkReserve(
if (auto const ret = sle.checkReserve(
ctx_.getApplyViewContext(),
sle,
preFeeBalance_,
{.ownerCountDelta = static_cast<int>(ownerCount)},
ctx_.journal);
{.ownerCountDelta = static_cast<int>(ownerCount)});
!isTesSuccess(ret))
return ret;
@@ -327,7 +325,6 @@ TransactionProposalCreate::doApply()
view().insert(sleProposal);
auto viewJ = ctx_.registry.get().getJournal("View");
{
auto const page = view().dirInsert(
keylet::ownerDir(accountID_), proposalKeylet, describeOwnerDir(accountID_));
@@ -336,7 +333,7 @@ TransactionProposalCreate::doApply()
sleProposal->setFieldU64(sfOwnerNode, *page);
}
increaseOwnerCount(ctx_.getApplyViewContext(), sle, ownerCount, viewJ);
sle.increaseOwnerCount(ctx_.getApplyViewContext(), ownerCount);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleProposal);
return tesSUCCESS;
}
@@ -7,6 +7,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -164,16 +165,16 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx)
if (sponseeID == sponsorID)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const sponsorAccSle = ctx.view.read(keylet::account(sponsorID));
auto const sponsorAccSle = AccountRootEntryR(sponsorID, ctx.view);
if (!sponsorAccSle)
return tecNO_DST;
auto const sponseeSle = ctx.view.read(keylet::account(sponseeID));
auto const sponseeSle = AccountRootEntryR(sponseeID, ctx.view);
if (!sponseeSle)
return tecNO_DST;
// Pseudo-accounts cannot participate in sponsorship.
if (isPseudoAccount(sponsorAccSle) || isPseudoAccount(sponseeSle))
if (sponsorAccSle.isPseudoAccount() || sponseeSle.isPseudoAccount())
return tecPSEUDO_ACCOUNT;
auto const sponsorshipSle = ctx.view.read(keylet::sponsorship(sponsorID, sponseeID));
@@ -212,7 +213,7 @@ deleteSponsorship(ApplyView& view, SLE::Ref sle, beast::Journal j)
// The sponsor owns the Sponsorship object, so deletion releases the
// sponsor's owner reserve.
auto sponsorAccSle = view.peek(keylet::account(sponsorID));
auto sponsorAccSle = AccountRootEntryW(sponsorID, view);
if (!sponsorAccSle)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -237,7 +238,7 @@ deleteSponsorship(ApplyView& view, SLE::Ref sle, beast::Journal j)
if (sle->isFieldPresent(sfFeeAmount))
{
(*sponsorAccSle)[sfBalance] += sle->getFieldAmount(sfFeeAmount);
view.update(sponsorAccSle);
sponsorAccSle.update();
}
view.erase(sle);
@@ -250,8 +251,8 @@ SponsorshipSet::createSponsorship(
Keylet const& sponsorshipKeylet,
AccountID const& sponsorID,
AccountID const& sponseeID,
SLE::Ref sponsorAccSle,
SLE::Ref reserveSponsorAccSle)
AccountRootEntryW& sponsorAccSle,
std::optional<AccountRootEntryW>& reserveSponsorAccSle)
{
auto const feeAmountDelta = ctx_.tx[~sfFeeAmountDelta];
auto const maxFee = ctx_.tx[~sfMaxFee];
@@ -276,13 +277,11 @@ SponsorshipSet::createSponsorship(
if (hasPositiveFeeAmount)
sponsorBalanceAfterFee -= *feeAmountDelta;
if (auto const ret = checkReserve(
if (auto const ret = sponsorAccSle.checkReserve(
ctx_.getApplyViewContext(),
sponsorAccSle,
sponsorBalanceAfterFee.xrp(),
reserveSponsorAccSle,
{.ownerCountDelta = 1},
ctx_.journal,
tecUNFUNDED);
!isTesSuccess(ret))
{
@@ -323,8 +322,9 @@ SponsorshipSet::createSponsorship(
(*newSle)[sfSponseeNode] = *sponseePage;
// NOLINTNEXTLINE(readability-suspicious-call-argument)
increaseOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, ctx_.journal);
addSponsorToLedgerEntry(newSle, reserveSponsorAccSle);
sponsorAccSle.increaseOwnerCount(reserveSponsorAccSle, 1);
if (reserveSponsorAccSle.has_value())
addSponsorToLedgerEntry(newSle, reserveSponsorAccSle->rawSle());
ctx_.view().insert(newSle);
return tesSUCCESS;
@@ -339,7 +339,7 @@ SponsorshipSet::doApply()
if (sponseeID == sponsorID)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorID));
auto sponsorAccSle = AccountRootEntryW(sponsorID, ctx_.view(), ctx_.journal);
if (!sponsorAccSle)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -388,13 +388,11 @@ SponsorshipSet::doApply()
// object.
(*sponsorAccSle)[sfBalance] -= actualDelta;
if (auto const ret = checkReserve(
if (auto const ret = sponsorAccSle.checkReserve(
ctx_.getApplyViewContext(),
sponsorAccSle,
(*sponsorAccSle)[sfBalance]->xrp(),
*reserveSponsorAccSle,
{},
ctx_.journal,
tecUNFUNDED);
!isTesSuccess(ret))
{
@@ -413,7 +411,7 @@ SponsorshipSet::doApply()
{
(*sponsorshipSle)[sfFeeAmount] = newFee;
}
ctx_.view().update(sponsorAccSle);
sponsorAccSle.update();
}
if (maxFee)
@@ -7,7 +7,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.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/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -20,17 +20,13 @@
#include <bit>
#include <cstdint>
#include <limits>
#include <memory>
#include <optional>
namespace xrpl {
// Increment an uint32 sponsor count field and update the SLE.
static TER
incrementSponsorCount(
ApplyView& view,
SLE::Ref sle,
SF_UINT32 const& field,
std::uint32_t const delta)
incrementSponsorCount(AccountRootEntryW& sle, SF_UINT32 const& field, std::uint32_t const delta)
{
auto const currentValue = sle->getFieldU32(field);
if (std::numeric_limits<std::uint32_t>::max() - currentValue < delta)
@@ -42,17 +38,13 @@ incrementSponsorCount(
}
sle->at(field) = currentValue + delta;
view.update(sle);
sle.update();
return tesSUCCESS;
}
// Decrement an uint32 sponsor count field and update the SLE.
static TER
decrementSponsorCount(
ApplyView& view,
SLE::Ref sle,
SF_UINT32 const& field,
std::uint32_t const delta)
decrementSponsorCount(AccountRootEntryW& sle, SF_UINT32 const& field, std::uint32_t const delta)
{
auto const currentValue = sle->getFieldU32(field);
if (currentValue < delta)
@@ -64,7 +56,7 @@ decrementSponsorCount(
}
sle->at(field) = currentValue - delta;
view.update(sle);
sle.update();
return tesSUCCESS;
}
@@ -205,7 +197,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx)
// is collected on-ledger afterward, so its absence here is expected, not
// an error (On-Chain Cosigner spec §5.3.1.2).
if (isAccountReserveSponsorship && !ctx.tx.isFieldPresent(sfSponsorSignature) &&
(ctx.flags & ApplyFlags::Proposal) == ApplyFlags::None)
(ctx.flags & TapProposal) == 0)
{
JLOG(ctx.j.debug()) << "preflight: account sponsorship requires sfSponsorSignature";
return temMALFORMED;
@@ -232,7 +224,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
auto const account = ctx.tx[sfAccount];
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 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
// overridden to the object SLE and its type-specific sponsor field:
// 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;
if (objectID.has_value())
@@ -313,7 +305,7 @@ SponsorshipTransfer::doApply()
auto const objectID = ctx_.tx[~sfObjectID];
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)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -337,7 +329,7 @@ SponsorshipTransfer::doApply()
if (!isLedgerEntryOwner(view(), *objectSle, sponseeID))
return tefINTERNAL; // LCOV_EXCL_LINE
auto const ownerSle = view().peek(keylet::account(sponseeID));
auto ownerSle = AccountRootEntryW(sponseeID, view());
if (!ownerSle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -355,27 +347,25 @@ SponsorshipTransfer::doApply()
if (!newSponsor)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const newSponsorID = *newSponsor;
auto const newSponsorSle = view().peek(keylet::account(newSponsorID));
auto newSponsorSle = AccountRootEntryW(newSponsorID, view());
if (!newSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE
// Check if new sponsor has sufficient balance
// NOLINTNEXTLINE(readability-suspicious-call-argument)
if (auto const ter = checkReserve(
if (auto const ter = sponseeSle.checkReserve(
ctx_.getApplyViewContext(),
sponseeSle,
sponseeSle->getFieldAmount(sfBalance).xrp(),
newSponsorSle,
{.ownerCountDelta = ownerCountDelta},
ctx_.journal);
std::optional<AccountRootEntryR>{newSponsorSle},
{.ownerCountDelta = ownerCountDelta});
!isTesSuccess(ter))
return ter;
if (isCreate)
{
// Update owner's sponsored count
if (auto const ter = incrementSponsorCount(
view(), ownerSle, sfSponsoredOwnerCount, ownerCountDelta);
if (auto const ter =
incrementSponsorCount(ownerSle, sfSponsoredOwnerCount, ownerCountDelta);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
}
@@ -384,20 +374,20 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = objectSle->getAccountID(sponsorField);
if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view());
if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE
// Decrement old sponsor's sponsoring count
if (auto const ter = decrementSponsorCount(
view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
}
// Increment new sponsor's sponsoring count
if (auto const ter = incrementSponsorCount(
view(), newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
if (auto const ter =
incrementSponsorCount(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
@@ -421,7 +411,7 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = objectSle->getAccountID(sponsorField);
if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view());
if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE
@@ -433,26 +423,24 @@ SponsorshipTransfer::doApply()
// steps in (Reassign), or the object/account is deleted.
if (view().rules().enabled(fixCleanup3_4_0))
{
if (auto const ter = checkReserve(
if (auto const ter = sponseeSle.checkReserve(
ctx_.getApplyViewContext(),
sponseeSle,
balanceBeforeFee(sponseeSle),
SLE::pointer(),
{.ownerCountDelta = ownerCountDelta},
ctx_.journal);
balanceBeforeFee(sponseeSle.rawSle()),
std::nullopt,
{.ownerCountDelta = ownerCountDelta});
!isTesSuccess(ter))
return ter;
}
// Decrement sponsored count
if (auto const ter = decrementSponsorCount(
view(), sponseeSle, sfSponsoredOwnerCount, ownerCountDelta);
if (auto const ter =
decrementSponsorCount(sponseeSle, sfSponsoredOwnerCount, ownerCountDelta);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
// Decrement old sponsoring count
if (auto const ter = decrementSponsorCount(
view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
if (auto const ter =
decrementSponsorCount(oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
@@ -475,17 +463,15 @@ SponsorshipTransfer::doApply()
if (!newSponsor)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const newSponsorID = *newSponsor;
auto const newSponsorSle = view().peek(keylet::account(newSponsorID));
auto newSponsorSle = AccountRootEntryW(newSponsorID, view());
if (!newSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ter = checkReserve(
if (auto const ter = sponseeSle.checkReserve(
ctx_.getApplyViewContext(),
sponseeSle,
sponseeSle->getFieldAmount(sfBalance).xrp(),
newSponsorSle,
{.accountCountDelta = 1},
ctx_.journal);
std::optional<AccountRootEntryR>{newSponsorSle},
{.accountCountDelta = 1});
!isTesSuccess(ter))
return ter;
@@ -494,26 +480,25 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view());
if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE
// Decrement old sponsoring count
if (auto const ter =
decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1);
decrementSponsorCount(oldSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
}
// Increment new sponsoring count
if (auto const ter =
incrementSponsorCount(view(), newSponsorSle, sfSponsoringAccountCount, 1);
if (auto const ter = incrementSponsorCount(newSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
// Account is now sponsored by new sponsor
sponseeSle->setAccountID(sfSponsor, newSponsorID);
view().update(sponseeSle);
sponseeSle.update();
}
else if (ctx_.tx.isFlag(tfSponsorshipEnd))
{
@@ -521,28 +506,25 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view());
if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE
// The sponsee must be able to hold its own account reserve after
// the sponsorship is removed.
if (auto const ter = checkReserve(
if (auto const ter = sponseeSle.checkReserve(
ctx_.getApplyViewContext(),
sponseeSle,
balanceBeforeFee(sponseeSle),
SLE::pointer(),
{.accountCountDelta = 1},
ctx_.journal);
balanceBeforeFee(sponseeSle.rawSle()),
std::optional<AccountRootEntryR>{},
{.accountCountDelta = 1});
!isTesSuccess(ter))
return ter;
sponseeSle->makeFieldAbsent(sfSponsor);
view().update(sponseeSle);
sponseeSle.update();
// Decrement account sponsoring count
if (auto const ter =
decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1);
if (auto const ter = decrementSponsorCount(oldSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE
}
Loaded 100 of 183 files, more files were not shown because too many files have changed in this diff. Show more