Compare commits

..
Author SHA1 Message Date
Nik Bougalis f14395427e Modernize CountedObject infrastructure:
- Replace the lazy singleton with a constinit registry of per-type
  static counters. The `getInstance` method is removed.
- A concurrency bug that would corrupt the list of counters during
  insertion has been fixed.
- Debug asserts can detect incorrect accounting that can result in
  counter under- or overflow.
- `CountedObject` can only be used as a CRTP base for its own type
  parameter.
- Track the current and maximum values of counter. This results in
  a change in the output of `get_counts`, with individual counters
  now reported as sub-objects of a `counters` object.
2026-10-05 19:42:50 -07:00
Nik Bougalis b707bac74f Improve AccountID base58 conversion cache:
The previous cache guarded a configurably-sized vector with 64
packed spinlocks. Two aspects of that setup were not worth the
cost:

- Callers were forced to perform atomic RMW operations on a
  single word which was shared by all 64 locks; this caused
  the lock line to bounce between cores on every lookup.

- The configurable size and runtime initialization resulted
  in complexity that did not yield a meaningful improvement
  in performance.

This commit ditches the 64 packed spinlocks, replacing them
with per-entry sequence locks, allowing readers on the fast
path to avoid performing any stores at all.

Cache entries are now carefully sized and aligned to fit into
a typical cache line, which helps avoid false sharing and any
unnecessary coherence traffic.

The cache is now a fixed array of 65,536 entries. The size was
chosen to balance capacity against memory overhead and to keep
the indexing operation fast and simple: since AccountID values
are uniformly distributed, two bytes can serve directly as the
index without requiring additional hashing.

Note: The reader path will perform unsynchronized reads against
      concurrent writes; the sequence lock protocol detects and
      discards any data observed mid-update, so these races are
      benign. A sanitizer like TSAN can still flag them and the
      warnings are expected. A compile-time knob to disable the
      cache is available, if necessary.
2026-10-05 19:42:50 -07:00
Nik Bougalis da2b5d5eaf Remove beast::typeName
Replace Beast's demangling wrapper (originally by @HowardHinnant)
with calls to `boost::core::type_name`, eliminating the low-level
dependency and manual memory management from the codebase.
2026-10-05 19:42:50 -07:00
Nik Bougalis 4de4902200 Rework and clean up the safe_cast framework:
The SafeToCast concept transposed its signedness clause: it was written in
<Dest, Src> order but it declared <Src, Dest>. This introduced what can be
best described as a polarity bug. As a result:

* Signed-to-wider-unsigned casts were wrongly deemed safe.
* Unsigned-to-wider-signed casts were wrongly deemed unsafe.

The root cause was drift caused by the safety condition being repeated in
the concept and again as a static_assert in safeCast. The checking is now
done only in the concept and is expressed in terms of range coverage, and
not via a sizeof/signedness proxy. This change also improves the handling
of bool (which previously compiled despite truncating) and eliminates the
platform-dependent accept/reject behavior for same-size types.

Additional fixes:

* The pointer form of safeDowncast now performs the same static_cast
  in all builds. The dynamic_cast check is now entirely contained in
  the XRPL_ASSERT.
* Extended integer types wider than intmax_t (e.g. __int128) are now
  excluded from SafeToCast instead of silently misevaluating through
  wrapped bounds; they remain expressible via unsafeCast.

Cleanups:

* Remove the single-parameter enum overload of safeCast: it had an
  explicitly-specified template argument binding to Src, where all
  sibling overload bound to Dest, silently yielding the underlying
  type from expressions that read as casts *to* an enum.
* Add checkedCast for conversions guarded by runtime bounds checks
  where safety varies per instantiation.
* Constrain safeDowncast to polymorphic sources and genuine public
  unambiguous downcasts, rejecting upcasts and unrelated types.
* Route enum conversions through std::to_underlying; clarify the
  unsafeCast/checkedCast documentation split.
2026-10-05 19:42:49 -07:00
Nik Bougalis 2ea1f09dcb Modernize beast::Zero for C++20
- Replace the twelve comparison operators with two
  `constexpr`-capable replacements: `==` and `<=>`
  and allow the compiler to synthesize the rest.
- Constrain `==` and `<=>` using concepts and mark
  them as conditionally noexcept).
- Remove the detail::zero_helper indirection which
  did not do what its comment claimed.
2026-10-05 19:30:38 -07:00
Nik Bougalis 68b4b894de Make ApplyFlags a scoped enumeration:
Convert ApplyFlags to a scoped enum, drop the now-redundant `Tap`
prefix from its enumerators and replace the hand-written bitwise
operators by opting in to enum_bitops.

No functional change.
2026-10-05 19:30:38 -07:00
Nik Bougalis defd83e5a6 Make STPathElement::Type a scoped enumeration:
`STPathElement` stored its type as an `unsigned int`, returned it
as a `std::uint32_t`, while the flags themselves were an unscoped
`enum` (i.e. an `int`). The type field has always been serialized
as a single byte.

This commit turns `STPathElement::Type` into a scoped `enum` with
a fixed sized that matches the serialized width, and opts it into
the bitwise operators from `enum_bitops.h`:

- Store the type as `Type`, and use `Type` for the constructor
  parameter and the return type of `getNodeType()`.
- Keep the existing spellings such as `STPathElement::TypeAccount`
  valid through `using enum`.
- Replace raw mask tests in the serializer, JSON output, strand
  construction and pathfinder with the existing predicates.
- Validate the type byte once when deserializing, before any field
  is read, and restructure the loop around that.
- Make `operator==` a hidden friend.
- Mark the trivial accessors `noexcept`.

No functional changes are intended. The one observable difference
is the rejection error generated for a malformed path element: if
the currency and MPT bits are both set and the input is truncated
within the account field, the invalid bit combination is detected
first; previously the short read would be detected. Such elements
are rejected either way.
2026-10-05 19:30:38 -07:00
Nik Bougalis 452e31a05f Introduce opt-in bitwise operators for scoped enumerations:
Scoped enumerations do not support bitwise operators, so using an
`enum class` as a set of flags requires extensive casting at call
sites or a hand-written set of operators for each type.

Add `enum_bitops.h`, which provides `&`, `|`, `^`, `~` along with
the compound assignment forms for any scoped enumeration that has
an unsigned underlying type and is opted in:

    enum class Flags : std::uint8_t { a = 1, b = 2 };

    // Opt into bitwise operations
    template <>
    struct enum_bitops::OptIn<Flags> : std::true_type {};

All synthesized operators are constexpr and noexcept, and preserve
the enumeration type.
2026-10-05 19:30:37 -07:00
Mayukha Vadari 1798262d7a refactor: Use LedgerHashesEntry everywhere (#8369) 2026-10-05 18:15:02 +00:00
Mayukha Vadari cd633b9ac9 refactor: Use EscrowEntry everywhere (#8354) 2026-10-05 17:51:18 +00:00
Ayaz Salikhov 5c5f7d315a chore: Update nix flake file (#8479) 2026-10-05 16:58:41 +00:00
Mayukha Vadari 0f7493ce50 refactor: Use FeeSettingsEntry everywhere (#8370) 2026-10-05 16:56:41 +00:00
Harshit Gupta 40f61f828a fix: Add string type validation for channel_id and signature (#7582) 2026-10-05 16:55:14 +00:00
Mayukha Vadari 108277f4b7 test: Migrate three beast suites from beast::unit_test to gtest (#7993) 2026-10-05 15:28:05 +00:00
183 changed files with 2776 additions and 2577 deletions

No files matched your search

+5
View File
@@ -30,6 +30,11 @@ 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)) - `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 ## 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. 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": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1781173989, "lastModified": 1791130267,
"narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=", "narHash": "sha256-1sjwcQMcgAbmiip/VivBSiK8p2bTkQGOvCuO7vnwfx4=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "8c91a71d13451abc40eb9dae8910f972f979852f", "rev": "9013764fcc0ea99fa16cf7aa7decf8a5c3889dd2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -47,11 +47,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1784611586, "lastModified": 1791189933,
"narHash": "sha256-OfqgY+0hp/zseZB7uyH0U8kIDPS4scZZCyAurEplvG0=", "narHash": "sha256-Y8eN7ki0Urx4Ojf4w1xSpWg8uk/3lJqK+E0Oth77rH0=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "14f58845249f3552a89b07772626b8d3c632fa86", "rev": "e60029353d0c48d216bc4b065168ccd4079c166f",
"type": "github" "type": "github"
}, },
"original": { "original": {
+146 -55
View File
@@ -1,29 +1,31 @@
#pragma once #pragma once
#include <xrpl/beast/type_name.h> #include <xrpl/beast/utility/instrumentation.h>
#include <boost/core/type_name.hpp>
#include <algorithm>
#include <atomic> #include <atomic>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <string> #include <string>
#include <type_traits>
#include <utility> #include <utility>
#include <vector>
namespace xrpl { namespace xrpl {
/** /**
* Manages all counted object types. * 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 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: public:
/** /**
* Implementation for @ref CountedObject. * Implementation for @ref CountedObject.
@@ -33,68 +35,147 @@ public:
class Counter class Counter
{ {
public: public:
Counter(std::string name) noexcept : name_(std::move(name)), count_(0) Counter(std::string name) noexcept;
{
// Insert ourselves at the front of the lock-free linked list
CountedObjects& instance = CountedObjects::getInstance();
Counter* head = nullptr;
do // Counters are intrusive list nodes whose addresses are published
{ // in the registry; they must never be copied or moved. The atomic
head = instance.head_.load(); // members already force this, but we make it explicit.
next_ = head; Counter(Counter const&) = delete;
} while (instance.head_.exchange(this) != head); Counter&
operator=(Counter const&) = delete;
Counter(Counter&&) = delete;
Counter&
operator=(Counter&&) = delete;
++instance.count_; std::uint32_t
}
~Counter() noexcept = default;
int
increment() noexcept increment() noexcept
{ {
return ++count_; 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;
} }
int std::uint32_t
decrement() noexcept decrement() noexcept
{ {
return --count_; auto const prev = count_.fetch_sub(1, std::memory_order::relaxed);
XRPL_ASSERT(prev != 0, "xrpl::CountedObjects::Counter::decrement : no underflow");
return prev - 1;
} }
[[nodiscard]] int [[nodiscard]] std::uint32_t
getCount() const noexcept count() const noexcept
{ {
return count_.load(); return count_.load(std::memory_order::relaxed);
} }
[[nodiscard]] Counter* [[nodiscard]] std::uint32_t
getNext() const noexcept max() const noexcept
{ {
return next_; return std::max(
count_.load(std::memory_order::relaxed),
maxCount_.load(std::memory_order::relaxed));
} }
[[nodiscard]] std::string const& [[nodiscard]] std::string const&
getName() const noexcept name() const noexcept
{ {
return name_; return name_;
} }
private: 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::string const name_;
std::atomic<int> count_;
Counter* next_;
}; };
private: class Iterator
CountedObjects() noexcept; {
~CountedObjects() noexcept = default; 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: private:
std::atomic<int> count_; std::atomic<Counter*> head_ = nullptr;
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))
;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** /**
@@ -103,27 +184,37 @@ private:
* Derived classes have their instances counted automatically. This is used * Derived classes have their instances counted automatically. This is used
* for reporting purposes. * 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 * @ingroup basics
*/ */
template <class Object> template <class Object>
requires std::is_class_v<Object>
class CountedObject class CountedObject
{ {
private: static inline CountedObjects::Counter counter{boost::core::type_name<Object>()};
static auto&
getCounter() noexcept
{
static CountedObjects::Counter kC{beast::typeName<Object>()};
return kC;
}
CountedObject() noexcept CountedObject() noexcept
{ {
getCounter().increment(); counter.increment();
} }
CountedObject(CountedObject const&) noexcept CountedObject(CountedObject const&) noexcept
{ {
getCounter().increment(); counter.increment();
} }
CountedObject& CountedObject&
@@ -132,7 +223,7 @@ private:
public: public:
~CountedObject() noexcept ~CountedObject() noexcept
{ {
getCounter().decrement(); counter.decrement();
} }
friend Object; friend Object;
+3 -2
View File
@@ -3,11 +3,11 @@
#pragma once #pragma once
#include <xrpl/basics/ByteUtilities.h> #include <xrpl/basics/ByteUtilities.h>
#include <xrpl/beast/type_name.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <boost/align.hpp> #include <boost/align.hpp>
#include <boost/container/static_vector.hpp> #include <boost/container/static_vector.hpp>
#include <boost/core/type_name.hpp>
#include <boost/predef.h> #include <boost/predef.h>
#include <algorithm> #include <algorithm>
@@ -16,6 +16,7 @@
#include <cstring> #include <cstring>
#include <mutex> #include <mutex>
#include <stdexcept> #include <stdexcept>
#include <typeinfo>
#include <vector> #include <vector>
#if BOOST_OS_LINUX #if BOOST_OS_LINUX
@@ -335,7 +336,7 @@ public:
}) != cfg.end()) }) != cfg.end())
{ {
throw std::runtime_error( throw std::runtime_error(
"SlabAllocatorSet<" + beast::typeName<Type>() + ">: duplicate slab size"); "SlabAllocatorSet<" + boost::core::type_name<Type>() + ">: duplicate slab size");
} }
for (auto const& c : cfg) for (auto const& c : cfg)
+3 -2
View File
@@ -1,7 +1,8 @@
#pragma once #pragma once
#include <xrpl/basics/sanitizers.h> #include <xrpl/basics/sanitizers.h>
#include <xrpl/beast/type_name.h>
#include <boost/core/type_name.hpp>
#include <exception> #include <exception>
#include <string> #include <string>
@@ -55,7 +56,7 @@ Throw(Args&&... args)
std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception."); std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception.");
E e(std::forward<Args>(args)...); E e(std::forward<Args>(args)...);
logThrow(std::string("Throwing exception of type " + beast::typeName<E>() + ": ") + e.what()); logThrow("Throwing exception of type " + boost::core::type_name<E>() + ": " + e.what());
throw std::move(e); throw std::move(e);
} }
+200
View File
@@ -0,0 +1,200 @@
#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
+152 -47
View File
@@ -2,107 +2,212 @@
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep #include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
#include <concepts>
#include <cstdint>
#include <limits>
#include <memory>
#include <type_traits> #include <type_traits>
#include <utility>
namespace xrpl { namespace xrpl {
// safe_cast adds compile-time checks to a static_cast to ensure that /** Every value of @p Src can be represented by @p Dest.
// the destination can hold all values of the source. This is particularly
// handy when the source or destination is an enumeration type.
template <class Src, class Dest> Given two integral types, the cast is safe when the destination can
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) && hold every possible value of the source.
(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));
template <class Dest, class 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>)
constexpr Dest constexpr Dest
safeCast(Src s) noexcept safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{ {
static_assert( static_assert(
std::is_signed_v<Dest> || std::is_unsigned_v<Src>, "Cannot cast signed to unsigned"); SafeToCast<Src, Dest>, "This cast is not value-preserving. Please use unsafeCast instead.");
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); return static_cast<Dest>(s);
} }
template <class Dest, class Src> template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
constexpr Dest constexpr Dest
safeCast(Src s) noexcept 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)); return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
} }
template <class Dest, class Src> template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
constexpr Dest constexpr Dest
safeCast(Src s) noexcept safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
{ {
return safeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s)); return safeCast<Dest>(std::to_underlying(s));
} }
/** @} */
// unsafe_cast explicitly flags a static_cast as not necessarily able to hold /** Integral-to-integral cast that is known to be narrowing or sign-erasing.
// 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.
template <class Dest, class Src> 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>)
constexpr Dest constexpr Dest
unsafeCast(Src s) noexcept unsafeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{ {
static_assert( static_assert(
!SafeToCast<Src, Dest>, !SafeToCast<Src, Dest>, "This cast is value-preserving. Please use safeCast instead.");
"Only unsafe if casting signed to unsigned or "
"destination is too small");
return static_cast<Dest>(s); return static_cast<Dest>(s);
} }
template <class Dest, class Src> template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
constexpr Dest constexpr Dest
unsafeCast(Src s) noexcept 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)); return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
} }
template <class Dest, class Src> template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
constexpr Dest constexpr Dest
unsafeCast(Src s) noexcept unsafeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
{ {
return unsafeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s)); 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));
}
/** @} */
/** 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> template <class Dest, class Src>
requires std::is_pointer_v<Dest> 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>))
inline Dest inline Dest
safeDowncast(Src* s) noexcept safeDowncast(Src* s) noexcept
{ {
#ifdef NDEBUG XRPL_ASSERT(
s == nullptr || dynamic_cast<Dest>(s) != nullptr, "xrpl::safeDowncast : valid downcast");
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) 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> template <class Dest, class Src>
requires std::is_lvalue_reference_v<Dest> requires(
std::is_lvalue_reference_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_reference_t<Dest>, Src>)
inline Dest inline Dest
safeDowncast(Src& s) noexcept safeDowncast(Src& s) noexcept
{ {
#ifndef NDEBUG return *safeDowncast<std::add_pointer_t<std::remove_reference_t<Dest>>>(std::addressof(s));
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 } // namespace xrpl
-47
View File
@@ -1,47 +0,0 @@
#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
+36 -109
View File
@@ -2,26 +2,24 @@
#pragma once #pragma once
#include <compare>
#include <concepts>
namespace beast { namespace beast {
/** /**
* Zero allows classes to offer efficient comparisons to zero. * 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. * 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 * In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
* like t > 1 or t != 1 do not. * like t > 1 or t != 1 do not. Comparing against kZero expresses exactly that,
* without constructing a T.
* *
* The class Zero allows such comparisons to be easily made. * A type T participates if either `t.signum()` or an unqualified `signum(t)`
* * found by argument-dependent lookup returns an integer that is negative,
* The comparing class T either needs to have a method called signum() which * zero, or positive according to the sign of t. Both `t == kZero` and
* returns a positive number, 0, or a negative; or there needs to be a signum * `kZero == t` work, as do all six relational operators in either order.
* function which resolves in the namespace which takes an instance of T and
* returns a positive, zero or negative number.
*/ */
struct Zero struct Zero
{ {
explicit Zero() = default; explicit Zero() = default;
@@ -30,115 +28,44 @@ struct Zero
inline constexpr Zero kZero{}; inline constexpr Zero kZero{};
/** /**
* Default implementation of signum calls the method on the class. * Default implementation of signum: call the member function.
*/ */
template <typename T> template <class T>
auto requires requires(T const& t) {
signum(T const& t) { t.signum() } -> std::integral;
}
[[nodiscard]] constexpr auto
signum(T const& t) noexcept(noexcept(t.signum()))
{ {
return t.signum(); return t.signum();
} }
namespace detail::zero_helper { namespace detail {
// For argument dependent lookup to function properly, calls to signum must /**
// be made from a namespace that does not include overloads of the function.. * 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.
*/
template <class T> template <class T>
auto concept HasSignum = requires(T const& t) {
callSignum(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)))
{ {
return signum(t); return signum(t) == 0;
} }
} // namespace detail::zero_helper template <detail::HasSignum T>
[[nodiscard]] constexpr std::strong_ordering
// Handle operators where T is on the left side using signum. operator<=>(T const& t, Zero) noexcept(noexcept(signum(t)))
template <typename T>
bool
operator==(T const& t, Zero)
{ {
return detail::zero_helper::callSignum(t) == 0; return signum(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 } // namespace beast
+12 -53
View File
@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/basics/safe_cast.h> #include <xrpl/basics/enum_bitops.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/OwnerCounts.h> #include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
@@ -22,80 +22,39 @@
namespace xrpl { namespace xrpl {
// Bitwise flag enum with existing operator overloads enum class ApplyFlags : std::uint32_t {
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) None = 0x00,
enum ApplyFlags : std::uint32_t {
TapNone = 0x00,
// This is a local transaction with the // This is a local transaction with the
// fail_hard flag set. // fail_hard flag set.
TapFailHard = 0x10, FailHard = 0x10,
// This is not the transaction's last pass // This is not the transaction's last pass
// Transaction can be retried, soft failures allowed // Transaction can be retried, soft failures allowed
TapRetry = 0x20, Retry = 0x20,
// Transaction came from a privileged source // Transaction came from a privileged source
TapUnlimited = 0x400, Unlimited = 0x400,
// Transaction is executing as part of a batch // Transaction is executing as part of a batch
TapBatch = 0x800, Batch = 0x800,
// Transaction shouldn't be applied // Transaction shouldn't be applied
// Signatures shouldn't be checked // Signatures shouldn't be checked
TapDryRun = 0x1000, DryRun = 0x1000,
// Transaction is being preflighted as the payload of a // Transaction is being preflighted as the payload of a
// TransactionProposalCreate. Its signatures are collected on-ledger // TransactionProposalCreate. Its signatures are collected on-ledger
// afterward, so signature-presence checks (e.g. Batch signer matching) // afterward, so signature-presence checks (e.g. Batch signer matching)
// are skipped at proposal-creation time (On-Chain Cosigner spec // are skipped at proposal-creation time (On-Chain Cosigner spec
// §5.3.1.2). // §5.3.1.2).
TapProposal = 0x2000 Proposal = 0x2000
}; };
constexpr ApplyFlags template <>
operator|(ApplyFlags const& lhs, ApplyFlags const& rhs) struct enum_bitops::OptIn<ApplyFlags> : std::true_type
{ {
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;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
+1 -2
View File
@@ -5,7 +5,6 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h> #include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
@@ -212,7 +211,7 @@ canWithdraw(
ReadView const& view, ReadView const& view,
AccountID const& from, AccountID const& from,
AccountID const& to, AccountID const& to,
AccountRootEntryR const& toSle, SLE::ConstRef toSle,
STAmount const& amount, STAmount const& amount,
bool hasDestinationTag, bool hasDestinationTag,
std::optional<std::vector<UInt256>> const& credentialIDs = std::nullopt); std::optional<std::vector<UInt256>> const& credentialIDs = std::nullopt);
@@ -7,20 +7,9 @@
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <optional>
namespace xrpl { namespace xrpl {
struct Adjustment
{
std::int32_t ownerCountDelta = 0;
std::int32_t accountCountDelta = 0;
};
template <typename ViewT> template <typename ViewT>
class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT> class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT>
{ {
@@ -38,123 +27,6 @@ public:
: Base(keylet::account(id), view, j) : Base(keylet::account(id), view, j)
{ {
} }
/**
* Returns the account reserve, in drops.
*
* Actual owner count can be adjusted by delta in ownerCountAdj
* Actual reserve count can be adjusted by delta in accountCountAdj
* The reserve is calculated as:
* (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) *
* increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base"
*
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative
* to subtract.
* @return The account reserve amount in drops
*/
[[nodiscard]] XRPAmount
reserve(Adjustment adj = {}) const;
/**
* Return number of the objects which reserve is covered by the account (so called "owner
* count"). Actual owner count can be adjusted by delta in ownerCountAdj.
*
* @param ownerCountAdj Adjustment to the owner count (default: 0)
* @return The adjusted owner count
*/
[[nodiscard]] std::uint32_t
ownerCount(std::int32_t ownerCountAdj = 0) const;
/**
* Check if this account has sufficient reserve.
*
* @param ctx The apply-view context (view + tx)
* @param accBalance The account's balance
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative
* to subtract.
* @param insufReserveCode The transaction result code to return if the reserve is
* insufficient (default: tecINSUFFICIENT_RESERVE).
* @return Transaction result code
*/
[[nodiscard]] TER
checkReserve(
ApplyViewContext ctx,
XRPAmount accBalance,
std::optional<AccountRootEntry<ReadView>> const& sponsorSle,
Adjustment adj,
TER insufReserveCode = tecINSUFFICIENT_RESERVE) const;
/**
* Check if this account has sufficient reserve, deriving the sponsor internally.
*
* Equivalent to the overload above, but resolves the sponsor via
* getEffectiveTxReserveSponsor(ctx, *this) instead of taking it explicitly. Use this
* in the common case where the sponsor is simply the transaction's reserve
* sponsor for this account. Callers that must force the account's-own-reserve branch
* (passing a null sponsor) or supply a different sponsor should use the
* explicit overload above.
*
* @param ctx The apply-view context (view + tx)
* @param accBalance The account's balance
* @param adj Reserve adjustments (owner/account count deltas)
* @return Transaction result code
*/
[[nodiscard]] TER
checkReserve(ApplyViewContext ctx, XRPAmount accBalance, Adjustment adj) const;
/**
* Increase owner-count fields when the caller supplies the sponsor.
*
* This helper does not create a ledger object. It updates reserve accounting
* after the caller has created/updated an object.
* If sponsorSle is provided, this also adjusts the account's sponsored count
* and the sponsor's sponsoring count.
*
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param count Amount to add to the owner count
*/
void
increaseOwnerCount(std::optional<AccountRootEntry<ApplyView>>& sponsorSle, std::uint32_t count)
requires Base::kIsWritable;
/**
* Increase owner-count fields, deriving the tx reserve sponsor internally.
*
* Equivalent to the overload above, but resolves the sponsor via
* getEffectiveTxReserveSponsor(ctx, *this) instead of taking it explicitly. Use
* this when the sponsor is the transaction's reserve sponsor for this account
* (the common create path). Deletion paths, which derive the sponsor from an
* object's sfSponsor field, should keep using the explicit overload.
*
* @param ctx The apply-view context (view + tx)
* @param count Amount to add to the owner count
*/
void
increaseOwnerCount(ApplyViewContext ctx, std::uint32_t count)
requires Base::kIsWritable;
/**
* Decrease owner-count fields when the caller supplies the sponsor.
*
* This helper does not delete a ledger object. It updates reserve accounting
* after the caller has removed an owner-counted reserve, or for special
* owner-count changes whose sponsor cannot be derived from an object's
* sfSponsor field.
*
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param count Amount to remove from the owner count
*/
void
decreaseOwnerCount(std::optional<AccountRootEntry<ApplyView>>& sponsorSle, std::uint32_t count)
requires Base::kIsWritable;
private:
void
adjustOwnerCountSigned(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::int32_t adjustment)
requires Base::kIsWritable;
}; };
using AccountRootEntryR = AccountRootEntry<ReadView>; using AccountRootEntryR = AccountRootEntry<ReadView>;
@@ -1,11 +1,17 @@
#pragma once #pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/SLEBase.h> #include <xrpl/ledger/entries/SLEBase.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STVector256.h>
#include <cstddef>
#include <optional>
namespace xrpl { namespace xrpl {
@@ -25,6 +31,31 @@ public:
: Base(keylet::skip(), view, j) : 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>; using LedgerHashesEntryR = LedgerHashesEntry<ReadView>;
+164 -60
View File
@@ -4,8 +4,8 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Rate.h> #include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
@@ -46,55 +46,30 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer);
[[nodiscard]] XRPAmount [[nodiscard]] XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j); xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
namespace detail { struct Adjustment
{
std::int32_t ownerCountDelta = 0;
std::int32_t accountCountDelta = 0;
};
/** /**
* An owner count cannot be negative. If adjustment would cause a negative * Returns the account reserve, in drops.
* 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.
* *
* Shared by AccountRootEntry::ownerCount() and the owner-count helpers. * 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 currentOwnerCount The owner count before adjustment * @param view The ledger view to read from
* @param ownerCountAdj Positive to add to count, negative to reduce count * @param sle The ledger entry for the account
* @param id The account ID to use for error reporting, or std::nullopt to
* skip error reporting
* @param j Journal for logging * @param j Journal for logging
* @return The adjusted owner count * @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to
* subtract.
* @return The account reserve amount in drops
*/ */
std::uint32_t [[nodiscard]] XRPAmount
confineOwnerCount( accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjustment adj = {});
std::uint32_t currentOwnerCount,
std::int32_t ownerCountAdj,
std::optional<AccountID> const& id = std::nullopt,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
/**
* Clamps and writes an owner-count field of sle, then updates sle in view.
*
* Shared by AccountRootEntry and adjustLoanBrokerOwnerCount().
*
* @return The adjusted owner count
*/
std::uint32_t
adjustOwnerCountImpl(
ApplyView& view,
SLE::Ref sle,
SF_UINT32 const& sfield,
AccountID const& accID,
std::int32_t ownerCountAdj,
beast::Journal j);
/**
* Returns the number of account reserves funded by this account: 1 for itself
* (0 if sponsored by another account) plus the count of accounts it sponsors.
* Shared by AccountRootEntry::reserve() and xrpLiquid().
*/
std::uint32_t
accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, beast::Journal j);
} // namespace detail
/** /**
* Convenience overload that accepts AccountID instead of SLE. * Convenience overload that accepts AccountID instead of SLE.
@@ -109,9 +84,114 @@ accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, bea
[[nodiscard]] inline XRPAmount [[nodiscard]] inline XRPAmount
accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {}) accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {})
{ {
return AccountRootEntryR(id, view, j).reserve(adj); return accountReserve(view, view.read(keylet::account(id)), j, adj);
} }
/**
* Check if an account has sufficient reserve.
*
* @param view The ledger view to read from
* @param tx The transaction being processed
* @param accSle The account's ledger entry
* @param accBalance The account's balance
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to
* subtract.
* @param j Journal for logging (default: null sink)
* @param insufReserveCode The transaction result code to return if the reserve is insufficient
* (default: tecINSUFFICIENT_RESERVE).
* @return Transaction result code
*/
[[nodiscard]] TER
checkReserve(
ApplyViewContext ctx,
SLE::ConstRef accSle,
XRPAmount accBalance,
SLE::ConstRef sponsorSle,
Adjustment adj,
beast::Journal j,
TER insufReserveCode = tecINSUFFICIENT_RESERVE);
/**
* Check if an account has sufficient reserve, deriving the sponsor internally.
*
* Equivalent to the overload above, but resolves the sponsor via
* getEffectiveTxReserveSponsor(ctx, accSle) instead of taking it explicitly. Use this
* in the common case where the sponsor is simply the transaction's reserve
* sponsor for accSle. Callers that must force the account's-own-reserve branch
* (passing a null sponsor) or supply a different sponsor should use the
* explicit overload above.
*
* @param ctx The apply-view context (view + tx)
* @param accSle The account's ledger entry
* @param accBalance The account's balance
* @param adj Reserve adjustments (owner/account count deltas)
* @param j Journal for logging (default: null sink)
* @return Transaction result code
*/
[[nodiscard]] TER
checkReserve(
ApplyViewContext ctx,
SLE::ConstRef accSle,
XRPAmount accBalance,
Adjustment adj,
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
/**
* Return number of the objects which reserve is covered by the account(sle) (so called "owner
* count"). Actual owner count can be adjusted by delta in ownerCountAdj.
*
* @param sle The account's ledger entry
* @param j Journal for logging
* @param ownerCountAdj Adjustment to the owner count (default: 0)
* @return The adjusted owner count
*/
std::uint32_t
ownerCount(SLE::ConstRef sle, beast::Journal j, std::int32_t ownerCountAdj = 0);
/**
* Increase owner-count fields when the caller supplies the sponsor.
*
* This helper does not create a ledger object. It updates reserve accounting
* after the caller has created/updated an object.
* If sponsorSle is provided, this also adjusts the account's sponsored count
* and the sponsor's sponsoring count.
*
* @param view The apply view for making changes
* @param accountSle The account's ledger entry
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param count Amount to add to the owner count
* @param j Journal for logging
*/
void
increaseOwnerCount(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::uint32_t count,
beast::Journal j);
/**
* Increase owner-count fields, deriving the tx reserve sponsor internally.
*
* Equivalent to the overload above, but resolves the sponsor via
* getEffectiveTxReserveSponsor(ctx, accountSle) instead of taking it explicitly. Use
* this when the sponsor is the transaction's reserve sponsor for accountSle
* (the common create path). Deletion paths, which derive the sponsor from an
* object's sfSponsor field, should keep using the explicit overload.
*
* @param ctx The apply-view context (view + tx)
* @param accountSle The account's ledger entry
* @param count Amount to add to the owner count
* @param j Journal for logging
*/
void
increaseOwnerCount(
ApplyViewContext ctx,
SLE::Ref accountSle,
std::uint32_t count,
beast::Journal j);
/** /**
* Convenience overload that accepts AccountID instead of SLE references. * Convenience overload that accepts AccountID instead of SLE references.
* *
@@ -129,13 +209,36 @@ increaseOwnerCount(
std::uint32_t count, std::uint32_t count,
beast::Journal j) beast::Journal j)
{ {
AccountRootEntryW accountSle(account, view, j); increaseOwnerCount(
std::optional<AccountRootEntryW> sponsorSle; view,
if (sponsor) view.peek(keylet::account(account)),
sponsorSle.emplace(*sponsor, view); sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(),
accountSle.increaseOwnerCount(sponsorSle, count); count,
j);
} }
/**
* Decrease owner-count fields when the caller supplies the sponsor.
*
* This helper does not delete a ledger object. It updates reserve accounting
* after the caller has removed an owner-counted reserve, or for special
* owner-count changes whose sponsor cannot be derived from an object's
* sfSponsor field.
*
* @param view The apply view for making changes
* @param accountSle The account's ledger entry
* @param sponsorSle The sponsor's ledger entry (if applicable)
* @param count Amount to remove from the owner count
* @param j Journal for logging
*/
void
decreaseOwnerCount(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::uint32_t count,
beast::Journal j);
/** /**
* Convenience overload that accepts AccountID instead of SLE references. * Convenience overload that accepts AccountID instead of SLE references.
* *
@@ -153,11 +256,12 @@ decreaseOwnerCount(
std::uint32_t count, std::uint32_t count,
beast::Journal j) beast::Journal j)
{ {
AccountRootEntryW accountSle(account, view, j); decreaseOwnerCount(
std::optional<AccountRootEntryW> sponsorSle; view,
if (sponsor) view.peek(keylet::account(account)),
sponsorSle.emplace(*sponsor, view); sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(),
accountSle.decreaseOwnerCount(sponsorSle, count); count,
j);
} }
/** /**
@@ -177,7 +281,7 @@ decreaseOwnerCount(
void void
decreaseOwnerCountForObject( decreaseOwnerCountForObject(
ApplyView& view, ApplyView& view,
AccountRootEntryW& accountSle, SLE::Ref accountSle,
SLE::Ref objectSle, SLE::Ref objectSle,
std::uint32_t count, std::uint32_t count,
beast::Journal j); beast::Journal j);
@@ -199,7 +303,7 @@ decreaseOwnerCountForObject(
std::uint32_t count, std::uint32_t count,
beast::Journal j) beast::Journal j)
{ {
AccountRootEntryW accountSle(account, view); SLE::Ref accountSle = view.peek(keylet::account(account));
decreaseOwnerCountForObject(view, accountSle, objectSle, count, j); decreaseOwnerCountForObject(view, accountSle, objectSle, count, j);
} }
@@ -263,7 +367,7 @@ getPseudoAccountFields();
* - null pointer * - null pointer
*/ */
[[nodiscard]] bool [[nodiscard]] bool
isPseudoAccount(AccountRootEntryR const& sleAcct); isPseudoAccount(SLE::const_pointer sleAcct);
/** /**
* Convenience overload that reads the account from the view. * Convenience overload that reads the account from the view.
@@ -271,7 +375,7 @@ isPseudoAccount(AccountRootEntryR const& sleAcct);
[[nodiscard]] inline bool [[nodiscard]] inline bool
isPseudoAccount(ReadView const& view, AccountID const& accountId) isPseudoAccount(ReadView const& view, AccountID const& accountId)
{ {
return isPseudoAccount(AccountRootEntryR(accountId, view)); return isPseudoAccount(view.read(keylet::account(accountId)));
} }
/** /**
@@ -292,6 +396,6 @@ createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const
* - If the SLE requires a destination tag, checks that there is a tag. * - If the SLE requires a destination tag, checks that there is a tag.
*/ */
[[nodiscard]] TER [[nodiscard]] TER
checkDestinationAndTag(AccountRootEntryR const& toSle, bool hasDestinationTag); checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag);
} // namespace xrpl } // namespace xrpl
+31 -62
View File
@@ -4,7 +4,7 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -27,32 +27,6 @@
namespace xrpl { namespace xrpl {
namespace detail {
/**
* Before fixCleanup3_2_0, EscrowCancel passes the escrow SLE as sleDest.
* Return what the old reserve check returned for it, without adopting a
* non-AccountRoot SLE into an AccountRootEntry: the tx reserve sponsor's
* error if the escrow owner submitted the tx, else tefINTERNAL.
*
* @param ctx the apply view context for the escrow transaction.
* @param sleDest the non-AccountRoot SLE that was passed as the destination.
* @return the tx reserve sponsor's error code if the escrow owner submitted
* the tx, else tefINTERNAL.
*/
inline TER
escrowDestNotAccountResult(ApplyViewContext ctx, SLE::ConstRef sleDest)
{
if (sleDest->getAccountID(sfAccount) == ctx.tx[sfAccount])
{
if (auto const sponsor = getTxReserveSponsor(ctx); !sponsor)
return sponsor.error(); // LCOV_EXCL_LINE
}
return tefINTERNAL;
}
} // namespace detail
template <ValidIssueType T> template <ValidIssueType T>
TER TER
escrowUnlockApplyHelper( escrowUnlockApplyHelper(
@@ -96,17 +70,18 @@ escrowUnlockApplyHelper<Issue>(
if (!ctx.view.exists(trustLineKey) && createAsset) if (!ctx.view.exists(trustLineKey) && createAsset)
{ {
// Can the account cover the trust line's reserve? // Can the account cover the trust line's reserve?
if (sleDest->getType() != ltACCOUNT_ROOT) auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
return detail::escrowDestNotAccountResult(ctx, sleDest);
auto const destSle = AccountRootEntryR(sleDest, ctx.view, journal);
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, destSle);
if (!sponsorSle) if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE return sponsorSle.error(); // LCOV_EXCL_LINE
auto& sponsor = *sponsorSle; if (auto const ret = checkReserve(
ctx,
if (auto const ret = destSle.checkReserve( sleDest,
ctx, xrpBalance, sponsor, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE); xrpBalance,
*sponsorSle,
{.ownerCountDelta = 1},
journal,
tecNO_LINE_INSUF_RESERVE);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
JLOG(journal.trace()) << "Trust line does not exist. " JLOG(journal.trace()) << "Trust line does not exist. "
@@ -119,22 +94,22 @@ escrowUnlockApplyHelper<Issue>(
initialBalance.get<Issue>().account = noAccount(); initialBalance.get<Issue>().account = noAccount();
if (TER const ter = trustCreate( if (TER const ter = trustCreate(
ctx.view, // payment sandbox ctx.view, // payment sandbox
recvLow, // is dest low? recvLow, // is dest low?
issuer, // source issuer, // source
receiver, // destination receiver, // destination
trustLineKey.key, // ledger index trustLineKey.key, // ledger index
sleDest, // Account to add to sleDest, // Account to add to
false, // authorize account false, // authorize account
!sleDest->isFlag(lsfDefaultRipple), // !sleDest->isFlag(lsfDefaultRipple), //
false, // freeze trust line false, // freeze trust line
false, // deep freeze trust line false, // deep freeze trust line
initialBalance, // zero initial balance initialBalance, // zero initial balance
Issue(currency, receiver), // limit of zero Issue(currency, receiver), // limit of zero
0, // quality in 0, // quality in
0, // quality out 0, // quality out
sponsor ? sponsor->mutableRawSle() : SLE::pointer(), // sponsor *sponsorSle, // sponsor
journal); // journal journal); // journal
!isTesSuccess(ter)) !isTesSuccess(ter))
{ {
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
@@ -229,29 +204,23 @@ escrowUnlockApplyHelper<MPTIssue>(
auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver); auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver);
if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer) if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer)
{ {
if (sleDest->getType() != ltACCOUNT_ROOT) auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
return detail::escrowDestNotAccountResult(ctx, sleDest);
auto const destSle = AccountRootEntryR(sleDest, ctx.view, journal);
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, destSle);
if (!sponsorSle) if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE return sponsorSle.error(); // LCOV_EXCL_LINE
auto& sponsor = *sponsorSle; if (auto const ret = checkReserve(
ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal);
if (auto const ret = destSle.checkReserve(ctx, xrpBalance, sponsor, {.ownerCountDelta = 1});
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
if (auto const ter = createMPToken( if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0);
ctx.view, mptID, receiver, sponsor ? sponsor->mutableRawSle() : SLE::pointer(), 0);
!isTesSuccess(ter)) !isTesSuccess(ter))
{ {
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
// update owner count. // update owner count.
AccountRootEntryW receiverSle(receiver, ctx.view, journal); increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal);
receiverSle.increaseOwnerCount(sponsor, 1);
} }
if (!ctx.view.exists(mptKeylet) && !receiverIssuer) if (!ctx.view.exists(mptKeylet) && !receiverIssuer)
+11 -12
View File
@@ -2,7 +2,6 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
@@ -55,11 +54,11 @@ getTxReserveSponsorID(STTx const& tx);
* Return a mutable SLE for the transaction's reserve sponsor account. * Return a mutable SLE for the transaction's reserve sponsor account.
* *
* @param ctx The apply-view context (view + tx) * @param ctx The apply-view context (view + tx)
* @return The sponsor account entry, nullopt if the tx is not * @return The sponsor account SLE, a null pointer if the tx is not
* reserve-sponsored, or tecINTERNAL if the sponsor account cannot * reserve-sponsored, or tecINTERNAL if the sponsor account cannot
* be loaded (an already-checked invariant). * be loaded (an already-checked invariant).
*/ */
std::expected<std::optional<AccountRootEntryW>, TER> std::expected<SLE::pointer, TER>
getTxReserveSponsor(ApplyViewContext ctx); getTxReserveSponsor(ApplyViewContext ctx);
/** /**
@@ -67,29 +66,29 @@ getTxReserveSponsor(ApplyViewContext ctx);
* *
* @param view The ledger read view * @param view The ledger read view
* @param tx The transaction to inspect * @param tx The transaction to inspect
* @return The sponsor account entry, nullopt if the tx is not * @return The sponsor account SLE, a null pointer if the tx is not
* reserve-sponsored, or tecINTERNAL if the sponsor account cannot * reserve-sponsored, or tecINTERNAL if the sponsor account cannot
* be loaded (an already-checked invariant). * be loaded (an already-checked invariant).
*/ */
std::expected<std::optional<AccountRootEntryR>, TER> std::expected<SLE::const_pointer, TER>
getTxReserveSponsor(ReadView const& view, STTx const& tx); getTxReserveSponsor(ReadView const& view, STTx const& tx);
/** /**
* The transaction's reserve sponsor for the given account, if applicable. * The transaction's reserve sponsor for the given account, if applicable.
* *
* A reserve sponsor only covers the transaction submitter's own objects, so * A reserve sponsor only covers the transaction submitter's own objects, so
* this returns the tx reserve sponsor entry only when accountSle is the tx's * this returns the tx reserve sponsor SLE only when accountSle is the tx's own
* own (non-pseudo) account; otherwise it returns nullopt. This is * (non-pseudo) account; otherwise it returns a null sponsor pointer. This is
* the single source of truth for the "sponsor applies to tx.Account only" rule * the single source of truth for the "sponsor applies to tx.Account only" rule
* that the sponsor-deriving helper overloads in AccountRootHelpers rely on. * that the sponsor-deriving helper overloads in AccountRootHelpers rely on.
* *
* @param ctx The apply-view context (view + tx) * @param ctx The apply-view context (view + tx)
* @param accountSle The account whose sponsor is being resolved * @param accountSle The account whose sponsor is being resolved
* @return The sponsor entry (nullopt if unsponsored), or tecINTERNAL if the * @return The sponsor SLE (nullptr if unsponsored), or tecINTERNAL if the
* sponsor account cannot be loaded (an already-checked invariant) * sponsor account cannot be loaded (an already-checked invariant)
*/ */
[[nodiscard]] std::expected<std::optional<AccountRootEntryW>, TER> [[nodiscard]] std::expected<SLE::pointer, TER>
getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle); getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle);
/** /**
* Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent. * Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent.
@@ -106,9 +105,9 @@ getLedgerEntryReserveSponsorID(SLE::ConstRef sle, SF_ACCOUNT const& field = sfSp
* @param view The mutable apply view * @param view The mutable apply view
* @param sle The ledger entry whose sponsor field is inspected * @param sle The ledger entry whose sponsor field is inspected
* @param field The field that holds the sponsor AccountID (defaults to sfSponsor) * @param field The field that holds the sponsor AccountID (defaults to sfSponsor)
* @return The sponsor account entry, or nullopt if the entry is unsponsored. * @return The sponsor account SLE, or a null pointer if the entry is unsponsored.
*/ */
std::optional<AccountRootEntryW> SLE::pointer
getLedgerEntryReserveSponsor( getLedgerEntryReserveSponsor(
ApplyView& view, ApplyView& view,
SLE::ConstRef sle, SLE::ConstRef sle,
-16
View File
@@ -101,22 +101,6 @@ operator<<(std::ostream& os, AccountID const& x)
return os; 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 } // namespace xrpl
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
+2 -2
View File
@@ -1,10 +1,10 @@
#pragma once #pragma once
#include <xrpl/basics/contract.h> #include <xrpl/basics/contract.h>
#include <xrpl/beast/type_name.h>
#include <xrpl/protocol/SOTemplate.h> #include <xrpl/protocol/SOTemplate.h>
#include <boost/container/flat_map.hpp> #include <boost/container/flat_map.hpp>
#include <boost/core/type_name.hpp>
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
@@ -84,7 +84,7 @@ public:
* Derived classes will load the object with all the known formats. * Derived classes will load the object with all the known formats.
*/ */
private: private:
KnownFormats() : name_(beast::typeName<Derived>()) KnownFormats() : name_(boost::core::type_name<Derived>())
{ {
} }
+56 -51
View File
@@ -2,6 +2,7 @@
#include <xrpl/basics/CountedObject.h> #include <xrpl/basics/CountedObject.h>
#include <xrpl/basics/UnorderedContainers.h> #include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/enum_bitops.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h> #include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -15,6 +16,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -22,18 +24,8 @@ namespace xrpl {
class STPathElement final : public CountedObject<STPathElement> class STPathElement final : public CountedObject<STPathElement>
{ {
unsigned int type_;
AccountID accountID_;
PathAsset assetID_;
AccountID issuerID_;
bool isOffer_;
std::size_t hashValue_;
public: public:
// Bitwise values (typeCurrency | typeMPT) enum class Type : std::uint8_t {
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum Type {
TypeNone = 0x00, TypeNone = 0x00,
TypeAccount = 0x01, // Rippling through an account (vs taking an offer). TypeAccount = 0x01, // Rippling through an account (vs taking an offer).
TypeCurrency = 0x10, // Currency follows. TypeCurrency = 0x10, // Currency follows.
@@ -45,6 +37,8 @@ public:
// Combination of all types. // Combination of all types.
}; };
using enum Type;
STPathElement(); STPathElement();
STPathElement(STPathElement const&) = default; STPathElement(STPathElement const&) = default;
STPathElement& STPathElement&
@@ -62,42 +56,42 @@ public:
bool forceAsset = false); bool forceAsset = false);
STPathElement( STPathElement(
unsigned int uType, Type type,
AccountID const& account, AccountID const& account,
PathAsset const& asset, PathAsset const& asset,
AccountID const& issuer); AccountID const& issuer);
[[nodiscard]] std::uint32_t [[nodiscard]] Type
getNodeType() const; getNodeType() const noexcept;
[[nodiscard]] bool [[nodiscard]] bool
isOffer() const; isOffer() const noexcept;
[[nodiscard]] bool [[nodiscard]] bool
isAccount() const; isAccount() const noexcept;
[[nodiscard]] bool [[nodiscard]] bool
hasIssuer() const; hasIssuer() const noexcept;
[[nodiscard]] bool [[nodiscard]] bool
hasCurrency() const; hasCurrency() const noexcept;
[[nodiscard]] bool [[nodiscard]] bool
hasMPT() const; hasMPT() const noexcept;
[[nodiscard]] bool [[nodiscard]] bool
hasAsset() const; hasAsset() const noexcept;
[[nodiscard]] bool [[nodiscard]] bool
isNone() const; isNone() const noexcept;
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a // Nodes are either an account ID or a offer prefix. Offer prefixs denote a
// class of offers. // class of offers.
[[nodiscard]] AccountID const& [[nodiscard]] AccountID const&
getAccountID() const; getAccountID() const noexcept;
[[nodiscard]] PathAsset const& [[nodiscard]] PathAsset const&
getPathAsset() const; getPathAsset() const noexcept;
[[nodiscard]] Currency const& [[nodiscard]] Currency const&
getCurrency() const; getCurrency() const;
@@ -106,17 +100,35 @@ public:
getMPTID() const; getMPTID() const;
[[nodiscard]] AccountID const& [[nodiscard]] AccountID const&
getIssuerID() const; getIssuerID() const noexcept;
[[nodiscard]] bool [[nodiscard]] bool
isType(Type const& pe) const; isType(Type pe) const noexcept;
bool friend bool
operator==(STPathElement const& t) const; 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_;
}
private: private:
static std::size_t static std::size_t
getHash(STPathElement const& element); 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> template <class Hasher>
@@ -124,7 +136,7 @@ void
hash_append(Hasher& h, STPathElement const& e) noexcept hash_append(Hasher& h, STPathElement const& e) noexcept
{ {
using beast::hash_append; using beast::hash_append;
hash_append(h, (e.getNodeType() & STPathElement::TypeAccount) != 0u); hash_append(h, e.isType(STPathElement::TypeAccount));
hash_append(h, e.getAccountID()); hash_append(h, e.getAccountID());
hash_append(h, e.getPathAsset()); hash_append(h, e.getPathAsset());
hash_append(h, e.getIssuerID()); hash_append(h, e.getIssuerID());
@@ -404,11 +416,11 @@ inline STPathElement::STPathElement(
} }
inline STPathElement::STPathElement( inline STPathElement::STPathElement(
unsigned int uType, Type type,
AccountID const& account, AccountID const& account,
PathAsset const& asset, PathAsset const& asset,
AccountID const& issuer) AccountID const& issuer)
: type_(uType) : type_(type)
, accountID_(account) , accountID_(account)
, assetID_(asset) , assetID_(asset)
, issuerID_(issuer) , issuerID_(issuer)
@@ -423,56 +435,56 @@ inline STPathElement::STPathElement(
hashValue_ = getHash(*this); hashValue_ = getHash(*this);
} }
inline std::uint32_t inline STPathElement::Type
STPathElement::getNodeType() const STPathElement::getNodeType() const noexcept
{ {
return type_; return type_;
} }
inline bool inline bool
STPathElement::isOffer() const STPathElement::isOffer() const noexcept
{ {
return isOffer_; return isOffer_;
} }
inline bool inline bool
STPathElement::isAccount() const STPathElement::isAccount() const noexcept
{ {
return !isOffer(); return !isOffer();
} }
inline bool inline bool
STPathElement::isType(Type const& pe) const STPathElement::isType(Type pe) const noexcept
{ {
return (type_ & pe) != 0u; return (type_ & pe) != STPathElement::TypeNone;
} }
inline bool inline bool
STPathElement::hasIssuer() const STPathElement::hasIssuer() const noexcept
{ {
return isType(STPathElement::TypeIssuer); return isType(STPathElement::TypeIssuer);
} }
inline bool inline bool
STPathElement::hasCurrency() const STPathElement::hasCurrency() const noexcept
{ {
return isType(STPathElement::TypeCurrency); return isType(STPathElement::TypeCurrency);
} }
inline bool inline bool
STPathElement::hasMPT() const STPathElement::hasMPT() const noexcept
{ {
return isType(STPathElement::TypeMpt); return isType(STPathElement::TypeMpt);
} }
inline bool inline bool
STPathElement::hasAsset() const STPathElement::hasAsset() const noexcept
{ {
return isType(STPathElement::TypeAsset); return isType(STPathElement::TypeAsset);
} }
inline bool inline bool
STPathElement::isNone() const STPathElement::isNone() const noexcept
{ {
return getNodeType() == STPathElement::TypeNone; return getNodeType() == STPathElement::TypeNone;
} }
@@ -480,13 +492,13 @@ STPathElement::isNone() const
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a // Nodes are either an account ID or a offer prefix. Offer prefixs denote a
// class of offers. // class of offers.
inline AccountID const& inline AccountID const&
STPathElement::getAccountID() const STPathElement::getAccountID() const noexcept
{ {
return accountID_; return accountID_;
} }
inline PathAsset const& inline PathAsset const&
STPathElement::getPathAsset() const STPathElement::getPathAsset() const noexcept
{ {
return assetID_; return assetID_;
} }
@@ -504,18 +516,11 @@ STPathElement::getMPTID() const
} }
inline AccountID const& inline AccountID const&
STPathElement::getIssuerID() const STPathElement::getIssuerID() const noexcept
{ {
return issuerID_; 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 ------------ // ------------ STPath ------------
inline STPath::STPath(std::vector<STPathElement> p) : path_(std::move(p)) inline STPath::STPath(std::vector<STPathElement> p) : path_(std::move(p))
+1
View File
@@ -392,6 +392,7 @@ JSS(max_ledger); // in/out: LedgerCleaner
JSS(max_queue_size); // out: TxQ JSS(max_queue_size); // out: TxQ
JSS(max_spend_drops); // out: AccountInfo JSS(max_spend_drops); // out: AccountInfo
JSS(max_spend_drops_total); // out: AccountInfo JSS(max_spend_drops_total); // out: AccountInfo
JSS(maximum);
JSS(mean); // out: get_aggregate_price JSS(mean); // out: get_aggregate_price
JSS(median); // out: get_aggregate_price JSS(median); // out: get_aggregate_price
JSS(median_fee); // out: TxQ JSS(median_fee); // out: TxQ
+2 -1
View File
@@ -51,7 +51,8 @@ public:
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()}) beast::Journal journal = beast::Journal{beast::Journal::getNullSink()})
: ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal) : ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal)
{ {
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set"); XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
} }
std::reference_wrapper<ServiceRegistry> registry; std::reference_wrapper<ServiceRegistry> registry;
+11 -8
View File
@@ -59,10 +59,11 @@ public:
, parentBatchId(parentBatchId) , parentBatchId(parentBatchId)
, j(j) , j(j)
{ {
XRPL_ASSERT((flags & TapBatch) == TapBatch, "Batch apply flag should be set"); XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::Batch, "Batch apply flag should be set");
XRPL_ASSERT_IF( XRPL_ASSERT_IF(
(flags & TapProposal) != TapNone, (flags & ApplyFlags::Proposal) != ApplyFlags::None,
(flags & TapDryRun) != TapNone, (flags & ApplyFlags::DryRun) != ApplyFlags::None,
"xrpl::PreflightContext : proposal preflight implies dry run"); "xrpl::PreflightContext : proposal preflight implies dry run");
} }
@@ -74,10 +75,11 @@ public:
beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: registry(registry), tx(tx), rules(std::move(rules)), flags(flags), j(j) : registry(registry), tx(tx), rules(std::move(rules)), flags(flags), j(j)
{ {
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set"); XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
XRPL_ASSERT_IF( XRPL_ASSERT_IF(
(flags & TapProposal) != TapNone, (flags & ApplyFlags::Proposal) != ApplyFlags::None,
(flags & TapDryRun) != TapNone, (flags & ApplyFlags::DryRun) != ApplyFlags::None,
"xrpl::PreflightContext : proposal preflight implies dry run"); "xrpl::PreflightContext : proposal preflight implies dry run");
} }
@@ -116,7 +118,7 @@ public:
, j(j) , j(j)
{ {
XRPL_ASSERT( XRPL_ASSERT(
parentBatchId.has_value() == ((flags & TapBatch) == TapBatch), parentBatchId.has_value() == ((flags & ApplyFlags::Batch) == ApplyFlags::Batch),
"Parent Batch ID should be set if batch apply flag is set"); "Parent Batch ID should be set if batch apply flag is set");
} }
@@ -129,7 +131,8 @@ public:
beast::Journal j = beast::Journal{beast::Journal::getNullSink()}) beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: PreclaimContext(registry, view, preflightResult, tx, flags, std::nullopt, j) : PreclaimContext(registry, view, preflightResult, tx, flags, std::nullopt, j)
{ {
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set"); XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
} }
PreclaimContext& PreclaimContext&
+1 -1
View File
@@ -41,7 +41,7 @@ struct ApplyResult
inline bool inline bool
isTecClaimHardFail(TER ter, ApplyFlags flags) isTecClaimHardFail(TER ter, ApplyFlags flags)
{ {
return isTecClaim(ter) && ((flags & TapRetry) == 0u); return isTecClaim(ter) && ((flags & ApplyFlags::Retry) == ApplyFlags::None);
} }
/** /**
@@ -27,18 +27,6 @@
namespace xrpl { namespace xrpl {
/**
* Returns true if sleAcct is an AccountRoot carrying a pseudo-account field.
*
* isPseudoAccount() takes a view-bound AccountRootEntry, which is not
* available while visiting entries, so invariant checks use this instead.
*
* @param sleAcct the SLE to check.
* @return true if sleAcct is an AccountRoot carrying a pseudo-account field.
*/
[[nodiscard]] bool
isPseudoAccountSle(SLE::ConstRef sleAcct);
#if GENERATING_DOCS #if GENERATING_DOCS
/** /**
* @brief Prototype for invariant check implementations. * @brief Prototype for invariant check implementations.
+2 -3
View File
@@ -5,7 +5,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -26,7 +25,7 @@ checkFreeze(
XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts"); XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts");
// check freeze // check freeze
if (auto sle = AccountRootEntryR(dst, view)) if (auto sle = view.read(keylet::account(dst)))
{ {
if (sle->isFlag(lsfGlobalFreeze)) if (sle->isFlag(lsfGlobalFreeze))
{ {
@@ -50,7 +49,7 @@ checkFreeze(
if (view.rules().enabled(fixFrozenLPTokenTransfer)) if (view.rules().enabled(fixFrozenLPTokenTransfer))
{ {
if (auto const sleDst = AccountRootEntryR(dst, view); if (auto const sleDst = view.read(keylet::account(dst));
sleDst && sleDst->isFieldPresent(sfAMMID)) sleDst && sleDst->isFieldPresent(sfAMMID))
{ {
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID])); auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
@@ -2,7 +2,6 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
@@ -13,7 +12,6 @@
#include <xrpl/tx/Transactor.h> #include <xrpl/tx/Transactor.h>
#include <cstdint> #include <cstdint>
#include <optional>
namespace xrpl { namespace xrpl {
@@ -58,8 +56,8 @@ private:
Keylet const& sponsorshipKeylet, Keylet const& sponsorshipKeylet,
AccountID const& sponsorID, AccountID const& sponsorID,
AccountID const& sponseeID, AccountID const& sponseeID,
AccountRootEntryW& sponsorAccSle, SLE::Ref sponsorAccSle,
std::optional<AccountRootEntryW>& reserveSponsorAccSle); SLE::Ref reserveSponsorAccSle);
}; };
} // namespace xrpl } // namespace xrpl
+73 -73
View File
@@ -2,144 +2,144 @@ Detected OS: macos (Darwin arm64)
Core build tools: Core build tools:
✅ cmake ✅ cmake
cmake version 4.1.2 cmake version 4.4.3
/nix/store/gvabsb4yqb5xsqzqph54rijnn4zpihnp-cmake-4.1.2/bin/cmake /nix/store/q85csxf4s4shx89zif097h1ql4ax1rrz-cmake-4.4.3/bin/cmake
✅ conan ✅ conan
Conan version 2.28.1 Conan version 2.32.0
/nix/store/9jiyxmkpwmn6dcqs0765s83riw3l5ail-conan-2.28.1/bin/conan /nix/store/921jqsgbilixmr3xchj95si0jl8pi13z-conan-2.32.0/bin/conan
✅ git ✅ git
git version 2.54.0 git version 2.55.0
/nix/store/a14yxcqvv9x2l9mllgpirzhvz93pgprg-git-2.54.0/bin/git /nix/store/gw7c7m0dwca5lg4152x9lpmxpj171bzw-git-2.55.0/bin/git
✅ python3 ✅ python3
Python 3.13.13 Python 3.14.7
/nix/store/ygxqin6ydzjfawywqpp5pal8wv6sf5bh-python3-3.13.13/bin/python3.13 /nix/store/0kcws5blwbdyxqxjd1w8hjipz61xny3k-python3-3.14.7/bin/python3.14
Development tooling: Development tooling:
✅ ccache ✅ ccache
ccache version 4.13.6 ccache version 4.13.6
/nix/store/57davyvs6p6dkrl3svzwg1ph18wsy4cz-ccache-4.13.6/bin/ccache /nix/store/ydr5nlzxp1djb5256y0zzqa87vz55gsk-ccache-4.13.6/bin/ccache
✅ clang ✅ clang
clang version 22.1.7 clang version 22.1.8
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang /nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang
✅ clang-22 ✅ clang-22
clang version 22.1.7 clang version 22.1.8
/nix/store/rbap7zqq7mw00fyqa02p5rj7gqjp4w5i-clang-22/bin/clang-22 /nix/store/w826sqb98nnym6kzackg829664iqdlxj-clang-22/bin/clang-22
✅ clang++ ✅ clang++
clang version 22.1.7 clang version 22.1.8
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang++ /nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang++
✅ clang++-22 ✅ clang++-22
clang version 22.1.7 clang version 22.1.8
/nix/store/v9haf787f7bcz0mq1sad4bpyx21pj6li-clang++-22/bin/clang++-22 /nix/store/wnwyw3yfqrhpxij65bl3pa1x65q2zg3m-clang++-22/bin/clang++-22
✅ ClangBuildAnalyzer ✅ ClangBuildAnalyzer
ClangBuildAnalyzer 1.6.0 ClangBuildAnalyzer 1.6.0
/nix/store/4l50ds9fa2mkvh7wg8qzrlbmjs12sb8l-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer /nix/store/c7jjnw29ra78jdlzjww2m374izhy05n8-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
✅ curl ✅ 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 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/kclq0czaxvsgh4ym9ld7b6iwy50l1snk-curl-8.20.0-bin/bin/curl /nix/store/pq1h17abqy6q5y6k5dn9qj0fwb89flba-curl-8.22.0-bin/bin/curl
✅ file ✅ file
file-5.47 file-5.48
/nix/store/dax63li7wwcbqxxkkgzc4g2rx7d4w86x-file-5.47/bin/file /nix/store/y44vpziwbzfm8020dzlqqbybyyy7lb6g-file-5.48/bin/file
✅ less ✅ less
less 692 (PCRE2 regular expressions) less 710 (PCRE2 regular expressions)
/nix/store/lvr16y75r1pdxpdv0aph5ak2yd0hkvqm-less-692/bin/less /nix/store/3yn3dx5fwg5i0inqqzwnxq7f6lwhpjqa-less-710/bin/less
✅ make ✅ make
GNU Make 4.4.1 GNU Make 4.4.1
/nix/store/8wwiw8pwyhrkzyq28hqzxfl4z84lks81-gnumake-4.4.1/bin/make /nix/store/8l4wgxmhdxfvc9g18y76bnxl1ghgcp7n-gnumake-4.4.1/bin/make
✅ netstat ✅ netstat
present present
/nix/store/qsd1kzqb0ahrk433vmyl245gp623j19s-network_cmds-730.80.3/bin/netstat /nix/store/57fqvgdav8lrmzndkwm7qrgksl01yj23-network_cmds-741.100.2/bin/netstat
✅ ninja ✅ ninja
1.13.2 1.13.2
/nix/store/bqykhrblarkj4fl0hz2mf8ngwfv6x6bz-ninja-1.13.2/bin/ninja /nix/store/infm4vlx71q83bqz0k559w1gn4b0rq4i-ninja-1.13.2/bin/ninja
✅ perl ✅ perl
v5.42.0 v5.42.3
/nix/store/js13ri9fvm0ajk1fpd3acigys2a9whdv-perl-5.42.0/bin/perl /nix/store/7rvr6qjfzk2ybzn1imjw6c6qq1n1a9cr-perl-5.42.3/bin/perl
✅ pkg-config ✅ pkg-config
0.29.2 0.29.2
/nix/store/lzrwr375jqhhbca116kja96xf1md83l8-pkg-config-wrapper-0.29.2/bin/pkg-config /nix/store/nhgylvj98klqggy54h7gbc3b2ia4a00y-pkg-config-wrapper-0.29.2/bin/pkg-config
✅ vim ✅ vim
VIM - Vi IMproved 9.2 (2026 Feb 14, compiled Jan 01 1980 00:00:00) VIM - Vi IMproved 9.2 (2026 Feb 14, compiled Jan 01 1980 00:00:00)
/nix/store/6vbkykg92w603c0sw3mkk7p7mfaawbns-vim-9.2.0389/bin/vim /nix/store/akaa9kqm3b8rlkv61kvsqr2l2j70rvxb-vim-9.2.1001/bin/vim
✅ zip ✅ zip
Zip 3.0 Zip 3.0
/nix/store/z6ph729vcakbvz3wh8ln1wk6mi06w487-zip-3.0/bin/zip /nix/store/dvawr36npnad4xa6dxi2ss0fw44aa82r-zip-3.0/bin/zip
✅ clang-apply-replacements ✅ clang-apply-replacements
clang-apply-replacements version 22.1.7 clang-apply-replacements version 22.1.8
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-apply-replacements /nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-apply-replacements
✅ clang-apply-replacements-22 ✅ clang-apply-replacements-22
clang-apply-replacements version 22.1.7 clang-apply-replacements version 22.1.8
/nix/store/m3ii69rca4077lf4wlk7m3jcag1fs577-clang-apply-replacements-22/bin/clang-apply-replacements-22 /nix/store/w4bz034k4l0w719rrmbwnqcgyjbswy81-clang-apply-replacements-22/bin/clang-apply-replacements-22
✅ clang-format ✅ clang-format
clang-format version 22.1.7 clang-format version 22.1.8
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-format /nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-format
✅ clang-format-22 ✅ clang-format-22
clang-format version 22.1.7 clang-format version 22.1.8
/nix/store/4fawqy6ngqcsqd2ygyyzm93q0xy3f5gs-clang-format-22/bin/clang-format-22 /nix/store/w7z346l0r8y36b6lc5i7jzj3w0581l2y-clang-format-22/bin/clang-format-22
✅ clang-tidy ✅ clang-tidy
LLVM version 22.1.7 LLVM version 22.1.8
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-tidy /nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-tidy
✅ clang-tidy-22 ✅ clang-tidy-22
LLVM version 22.1.7 LLVM version 22.1.8
/nix/store/jqw4280saixaxxihwdba9ldm2fsm6dr3-clang-tidy-22/bin/clang-tidy-22 /nix/store/0lv25qd3ddgjm3ndn70q61jfr6krlkl7-clang-tidy-22/bin/clang-tidy-22
✅ dot ✅ dot
dot - graphviz version 12.2.1 (0) dot - graphviz version 15.1.1 (0)
/nix/store/ijb4fbnqa6wzlpqnhb6q9knqpf7qqn5z-graphviz-12.2.1/bin/dot /nix/store/mc99a6bpbk2waym2cnw6ifmxli4ndb4x-graphviz-15.1.1/bin/dot
✅ doxygen ✅ doxygen
1.16.1 1.17.0
/nix/store/kbryjdpq9jizjb0ws0nzbf2h2ymbdiwm-doxygen-1.16.1/bin/doxygen /nix/store/kz43yaj0wg6fx6cbrcmi2442z1ramy0c-doxygen-1.17.0/bin/doxygen
✅ gcovr ✅ gcovr
gcovr 8.4 gcovr 8.4
/nix/store/wn8jiyh9p0bybs96s4163qp3k8vfmczx-python3.13-gcovr-8.4/bin/gcovr /nix/store/czj1vxsizrbba280gg22cys40qxkqjgr-python3.14-gcovr-8.4/bin/gcovr
✅ gh ✅ gh
gh version 2.94.0 (nixpkgs) gh version 2.102.0 (2026-09-30)
/nix/store/fhnpw0hs0gjms1ha6ap02jq7rx13gkbp-gh-2.94.0/bin/gh /nix/store/s8bahl6zssvry93ihivfmk1l6c58byrr-gh-2.102.0/bin/gh
✅ git-cliff ✅ git-cliff
git-cliff 2.13.1 git-cliff 2.14.2
/nix/store/cy0wwhgxa7yvrz97zydbq6sqmixc90fq-git-cliff-2.13.1/bin/git-cliff /nix/store/k4m5yk0qb7hfslwsq1zjajz1d94j12bj-git-cliff-2.14.2/bin/git-cliff
✅ git-lfs ✅ git-lfs
git-lfs/3.7.1 (3.7.1; darwin arm64; go 1.26.3) git-lfs/3.8.0 (3.8.0; darwin arm64; go 1.26.8)
/nix/store/k9r7zjfjplqa4d5s71cqvf2iv73jd9mc-git-lfs-3.7.1/bin/git-lfs /nix/store/wzixjr3yb40a13m2q654hm3bdjn6sal3-git-lfs-3.8.0/bin/git-lfs
✅ gpg ✅ gpg
gpg (GnuPG) 2.4.9 gpg (GnuPG) 2.4.9
/nix/store/cgh6iwzz5jgx9z5whka4vgj210i6npc6-gnupg-2.4.9/bin/gpg /nix/store/0ldfwnwx385i5kh7cyvxx4mkvgjhs5rh-gnupg-2.4.9/bin/gpg
✅ pre-commit ✅ pre-commit
pre-commit 4.5.1 pre-commit 4.6.2
/nix/store/z3cca68620w0w10f090szgzdnmh1waf2-pre-commit-4.5.1/bin/pre-commit /nix/store/ivh54xypg9qvd3ha9lis0i8p4nkf8n7a-pre-commit-4.6.2/bin/pre-commit
✅ run-clang-tidy ✅ run-clang-tidy
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers] usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
/nix/store/4x28x911z2f9y7adqlh3qspp4a16dig7-run-clang-tidy/bin/run-clang-tidy /nix/store/3xss1mm3mh1hzcmg182na6ki1p5rnxf1-run-clang-tidy/bin/run-clang-tidy
✅ run-clang-tidy-22 ✅ run-clang-tidy-22
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers] usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
/nix/store/x8iymrh76sk5q91ryg5pa7i32s6gfh34-run-clang-tidy-22/bin/run-clang-tidy-22 /nix/store/w68r9z07hcq4fwbfyq3yvc7qxm8aqfbl-run-clang-tidy-22/bin/run-clang-tidy-22
Rust toolchain: Rust toolchain:
✅ cargo ✅ cargo
cargo 1.97.1 (c980f4866 2026-06-30) cargo 1.97.1 (c980f4866 2026-06-30)
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/cargo /nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/cargo
✅ cargo-audit ✅ cargo-audit
cargo-audit-audit 0.22.1 cargo-audit-audit 0.22.2
/nix/store/snwkga2f5gyf404h7mmp9wriwxb8v65f-cargo-audit-0.22.1/bin/cargo-audit /nix/store/7jgrwj557l91940aj0ag4i924ima1cqn-cargo-audit-0.22.2/bin/cargo-audit
✅ cargo-llvm-cov ✅ cargo-llvm-cov
cargo-llvm-cov 0.8.5 cargo-llvm-cov 0.9.0
/nix/store/fpiqdh91gwyxalqp409ynm0s0g086w7w-cargo-llvm-cov-0.8.5/bin/cargo-llvm-cov /nix/store/5q1h285z8zzkw2qxig9a0b1avcsk1cf0-cargo-llvm-cov-0.9.0/bin/cargo-llvm-cov
✅ cargo-nextest ✅ cargo-nextest
cargo-nextest 0.9.137 cargo-nextest 0.9.146
/nix/store/ylz7m947mhkgsp6i7611id3s3gcd58nq-cargo-nextest-0.9.137/bin/cargo-nextest /nix/store/69v3hx3k597hcxd2zy7yad7fg0aw5yxd-cargo-nextest-0.9.146/bin/cargo-nextest
✅ clippy-driver ✅ clippy-driver
clippy 0.1.97 (8bab26f4f6 2026-07-14) clippy 0.1.97 (8bab26f4f6 2026-07-14)
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/clippy-driver /nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/clippy-driver
✅ rust-analyzer ✅ rust-analyzer
rust-analyzer 1.97.1 (8bab26f4 2026-07-14) rust-analyzer 1.97.1 (8bab26f4 2026-07-14)
/nix/store/j6apc5pmd0giy15da9p650r8zklslmvi-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer /nix/store/30rbyk8gx7v53fxqhs4pp0fvxfjycspr-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
✅ rust-nightly ✅ rust-nightly
rustc 1.99.0-nightly (87e5904f5 2026-07-20) rustc 1.101.0-nightly (282215592 2026-10-04)
/nix/store/fqpjz4l0nsnji8b2pz57mnj0akbp6hcl-rust-nightly/bin/rust-nightly /nix/store/a9552rf3pylcnkijc1z0hf1lcnv50djq-rust-nightly/bin/rust-nightly
✅ rustc ✅ rustc
rustc 1.97.1 (8bab26f4f 2026-07-14) rustc 1.97.1 (8bab26f4f 2026-07-14)
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/rustc /nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/rustc
✅ rustfmt ✅ rustfmt
rustfmt 1.9.0-stable (8bab26f4f6 2026-07-14) rustfmt 1.9.0-stable (8bab26f4f6 2026-07-14)
/nix/store/5ymwgr9jqjz7zzbmj0j5vqbwcd3kp0vm-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt /nix/store/0jmdipyn8266j9kqx2xxm2qkxlrlrjfc-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set). Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
+2 -1
View File
@@ -21,7 +21,8 @@ let
) )
); );
toolchain = if pkgs.stdenv.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]); toolchain =
if pkgs.stdenv.hostPlatform.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
in in
{ {
default = pkgs.buildEnv { default = pkgs.buildEnv {
+7 -6
View File
@@ -20,15 +20,16 @@ let
# Custom-glibc stdenvs, matching the CI environment. darwin has no custom # Custom-glibc stdenvs, matching the CI environment. darwin has no custom
# glibc, so there they fall back to the plain nixpkgs stdenvs. # glibc, so there they fall back to the plain nixpkgs stdenvs.
customGccStdenv = if pkgs.stdenv.isLinux then linux.gccStdenv else plainGccStdenv; customGccStdenv = if pkgs.stdenv.hostPlatform.isLinux then linux.gccStdenv else plainGccStdenv;
customClangStdenv = if pkgs.stdenv.isLinux then linux.clangStdenv else plainClangStdenv; customClangStdenv =
if pkgs.stdenv.hostPlatform.isLinux then linux.clangStdenv else plainClangStdenv;
# gcov matching each gcc shell, so `-Dcoverage=ON` builds work in the shell. # gcov matching each gcc shell, so `-Dcoverage=ON` builds work in the shell.
plainGcov = mkGcov { plainGcov = mkGcov {
name = "plain"; name = "plain";
cc = gccPackage.cc; cc = gccPackage.cc;
}; };
customGccGcov = if pkgs.stdenv.isLinux then linux.gcov else plainGcov; customGccGcov = if pkgs.stdenv.hostPlatform.isLinux then linux.gcov else plainGcov;
# Whole directory: init.sh locates the profiles relative to itself. # Whole directory: init.sh locates the profiles relative to itself.
conanDir = ../conan; conanDir = ../conan;
@@ -51,7 +52,7 @@ let
# Not sdkEnv: a shell's stdenv already sets that up. Prepended so the stub # Not sdkEnv: a shell's stdenv already sets that up. Prepended so the stub
# beats the nixpkgs libresolv this shell's tooling drags in. # beats the nixpkgs libresolv this shell's tooling drags in.
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.isDarwin ( darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin (
pkgs.lib.concatLines ( pkgs.lib.concatLines (
pkgs.lib.mapAttrsToList ( pkgs.lib.mapAttrsToList (
name: value: ''export ${name}="${value} ''${${name}:-}"'' name: value: ''export ${name}="${value} ''${${name}:-}"''
@@ -126,7 +127,7 @@ let
in in
rec { rec {
# macOS: Nix Clang. Linux: Nix GCC. # macOS: Nix Clang. Linux: Nix GCC.
default = if pkgs.stdenv.isDarwin then clang else gcc; default = if pkgs.stdenv.hostPlatform.isDarwin then clang else gcc;
# gcc/clang use the custom-glibc toolchain, matching CI. On darwin there is no # 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. # custom glibc, so they fall back to the plain nixpkgs toolchain.
@@ -171,7 +172,7 @@ rec {
# The *-plain shells (stock nixpkgs toolchain) exist only on Linux: on darwin # 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 # 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. # makes `nix develop .#gcc-plain` fail there rather than silently aliasing gcc.
// pkgs.lib.optionalAttrs pkgs.stdenv.isLinux { // pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
gcc-plain = makeShell { gcc-plain = makeShell {
shellName = "gcc-plain"; shellName = "gcc-plain";
stdenv = plainGccStdenv; stdenv = plainGccStdenv;
-39
View File
@@ -1,39 +0,0 @@
#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
+2 -1
View File
@@ -11,6 +11,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/LedgerTiming.h> #include <xrpl/ledger/LedgerTiming.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
#include <xrpl/nodestore/NodeObject.h> #include <xrpl/nodestore/NodeObject.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Fees.h> #include <xrpl/protocol/Fees.h>
@@ -560,7 +561,7 @@ Ledger::setup()
try try
{ {
if (auto const sle = read(keylet::feeSettings())) if (auto const sle = FeeSettingsEntryR(*this))
{ {
bool oldFees = false; bool oldFees = false;
bool newFees = false; bool newFees = false;
+17 -28
View File
@@ -9,7 +9,7 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/entries/LedgerHashesEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -116,8 +116,8 @@ isVaultPseudoAccountFrozenForIssuance(
view, {issuer, account}, assetOfHolding(issuanceSle, *sleHolding), depth + 1); view, {issuer, account}, assetOfHolding(issuanceSle, *sleHolding), depth + 1);
} }
auto const mptIssuer = AccountRootEntryR(issuer, view); auto const mptIssuer = view.read(keylet::account(issuer));
if (!mptIssuer.exists()) if (mptIssuer == nullptr)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer"); UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
@@ -190,7 +190,7 @@ canTransferLPToken(
{ {
// Only AMM-issued LPTokens are subject to this check. The LPToken's issuer // Only AMM-issued LPTokens are subject to this check. The LPToken's issuer
// is the AMM account; if it is not an AMM, this is not an LPToken. // is the AMM account; if it is not an AMM, this is not an LPToken.
auto const sleIssuer = AccountRootEntryR(lpTokenIssuer, view); auto const sleIssuer = view.read(keylet::account(lpTokenIssuer));
if (!sleIssuer || !sleIssuer->isFieldPresent(sfAMMID)) if (!sleIssuer || !sleIssuer->isFieldPresent(sfAMMID))
return tesSUCCESS; return tesSUCCESS;
@@ -362,17 +362,16 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
if (int const diff = ledger.seq() - seq; diff <= 256) if (int const diff = ledger.seq() - seq; diff <= 256)
{ {
// Within 256... // Within 256...
auto const hashIndex = ledger.read(keylet::skip()); LedgerHashesEntryR const hashIndex(ledger, journal);
if (hashIndex) if (hashIndex)
{ {
XRPL_ASSERT( XRPL_ASSERT(
hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.seq() - 1), hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.seq() - 1),
"xrpl::hashOfSeq : matching ledger sequence"); "xrpl::hashOfSeq : matching ledger sequence");
STVector256 vec = hashIndex->getFieldV256(sfHashes); if (auto const hash = hashIndex.hashAt(diff - 1))
if (vec.size() >= diff) return hash;
return vec[vec.size() - diff];
JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " (" JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " ("
<< vec.size() << "," << diff << ")"; << hashIndex->getFieldV256(sfHashes).size() << "," << diff << ")";
} }
else else
{ {
@@ -388,16 +387,14 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
} }
// in skiplist // in skiplist
auto const hashIndex = ledger.read(keylet::skip(seq)); LedgerHashesEntryR const hashIndex(keylet::skip(seq), ledger, journal);
if (hashIndex) if (hashIndex)
{ {
auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence); auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
XRPL_ASSERT(lastSeq >= seq, "xrpl::hashOfSeq : minimum last ledger"); XRPL_ASSERT(lastSeq >= seq, "xrpl::hashOfSeq : minimum last ledger");
XRPL_ASSERT((lastSeq & 0xff) == 0, "xrpl::hashOfSeq : valid last ledger"); XRPL_ASSERT((lastSeq & 0xff) == 0, "xrpl::hashOfSeq : valid last ledger");
auto const diff = (lastSeq - seq) >> 8; if (auto const hash = hashIndex.hashAt((lastSeq - seq) >> 8))
STVector256 vec = hashIndex->getFieldV256(sfHashes); return hash;
if (vec.size() > diff)
return vec[vec.size() - diff - 1];
} }
JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " error"; JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " error";
return std::nullopt; return std::nullopt;
@@ -467,7 +464,7 @@ canWithdraw(
ReadView const& view, ReadView const& view,
AccountID const& from, AccountID const& from,
AccountID const& to, AccountID const& to,
AccountRootEntryR const& toSle, SLE::ConstRef toSle,
STAmount const& amount, STAmount const& amount,
bool hasDestinationTag, bool hasDestinationTag,
std::optional<std::vector<UInt256>> const& credentialIDs) std::optional<std::vector<UInt256>> const& credentialIDs)
@@ -517,7 +514,7 @@ canWithdraw(
bool hasDestinationTag, bool hasDestinationTag,
std::optional<std::vector<UInt256>> const& credentialIDs) std::optional<std::vector<UInt256>> const& credentialIDs)
{ {
auto const toSle = AccountRootEntryR(to, view); auto const toSle = view.read(keylet::account(to));
return canWithdraw(view, from, to, toSle, amount, hasDestinationTag, credentialIDs); return canWithdraw(view, from, to, toSle, amount, hasDestinationTag, credentialIDs);
} }
@@ -542,7 +539,7 @@ doWithdraw(
STAmount const& amount, STAmount const& amount,
beast::Journal j) beast::Journal j)
{ {
auto dstSle = AccountRootEntryW(dstAcct, ctx.view); auto const dstSle = ctx.view.read(keylet::account(dstAcct));
// Create a trust line or MPToken for a self-destination only when there // Create a trust line or MPToken for a self-destination only when there
// is a payout to credit. Post-fixCleanup3_4_0, a zero-value withdraw // is a payout to credit. Post-fixCleanup3_4_0, a zero-value withdraw
@@ -560,8 +557,7 @@ doWithdraw(
} }
else else
{ {
if (auto err = if (auto err = verifyDepositPreauth(ctx.tx, ctx.view, senderAcct, dstAcct, dstSle, j))
verifyDepositPreauth(ctx.tx, ctx.view, senderAcct, dstAcct, dstSle.rawSle(), j))
return err; return err;
} }
@@ -584,21 +580,14 @@ doWithdraw(
// sponsor against the destination. accountSend can auto-create a holding // sponsor against the destination. accountSend can auto-create a holding
// for dstAcct; keying on the destination ensures a third-party destination's // for dstAcct; keying on the destination ensures a third-party destination's
// holding is never stamped with the tx's reserve sponsor. // holding is never stamped with the tx's reserve sponsor.
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle); auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle);
if (!sponsorSle) if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE return sponsorSle.error(); // LCOV_EXCL_LINE
// Move the funds directly from the broker's pseudo-account to the // Move the funds directly from the broker's pseudo-account to the
// dstAcct // dstAcct
auto& sponsor = *sponsorSle;
return accountSend( return accountSend(
ctx.view, ctx.view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes);
sourceAcct,
dstAcct,
amount,
j,
sponsor ? sponsor->mutableRawSle() : SLE::pointer{},
WaiveTransferFee::Yes);
} }
TER TER
@@ -1,322 +0,0 @@
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <cstdint>
#include <limits>
#include <optional>
#include <utility>
namespace xrpl {
template <typename ViewT>
XRPAmount
AccountRootEntry<ViewT>::reserve(Adjustment adj) const
{
auto const& view = this->readView();
auto const j = this->journal();
XRPL_ASSERT(
this->exists() && (*this)->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle");
if (!view.rules().enabled(featureSponsor))
{
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::accountReserve : no account count delta");
return view.fees().accountReserve(
(*this)->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
}
std::uint32_t const currentOwnerCount = ownerCount(adj.ownerCountDelta);
std::uint32_t const currentAccountCount =
detail::accountCountImpl(*this, adj.accountCountDelta, j);
return view.fees().accountReserve(currentOwnerCount, currentAccountCount);
}
template <typename ViewT>
std::uint32_t
AccountRootEntry<ViewT>::ownerCount(std::int32_t ownerCountAdj) const
{
auto const j = this->journal();
XRPL_ASSERT(
this->exists() && (*this)->getType() == ltACCOUNT_ROOT,
"xrpl::ownerCount : sle is account root");
AccountID const id = (*this)->getAccountID(sfAccount);
std::uint32_t const currentOwnerCount = (*this)->at(sfOwnerCount);
std::uint32_t const sponsoredOwnerCount = (*this)->at(sfSponsoredOwnerCount);
std::uint32_t const sponsoringOwnerCount = (*this)->at(sfSponsoringOwnerCount);
XRPL_ASSERT(
currentOwnerCount >= sponsoredOwnerCount,
"xrpl::ownerCount : OwnerCount must be greater than or equal to SponsoredOwnerCount");
std::int64_t deltaCount =
static_cast<std::int64_t>(ownerCountAdj) - sponsoredOwnerCount + sponsoringOwnerCount;
if (deltaCount > std::numeric_limits<std::int32_t>::max())
{
// LCOV_EXCL_START
deltaCount = std::numeric_limits<std::int32_t>::max();
JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, "
<< "adjustment: " << ownerCountAdj
<< ", sponsoredCount: " << sponsoredOwnerCount
<< ", sponsoringOwnerCount: " << sponsoringOwnerCount;
// LCOV_EXCL_STOP
}
else if (deltaCount < std::numeric_limits<std::int32_t>::min())
{
// LCOV_EXCL_START
deltaCount = std::numeric_limits<std::int32_t>::min();
JLOG(j.fatal()) << "Account " << id << " delta count is below min, "
<< "adjustment: " << ownerCountAdj
<< ", sponsoredCount: " << sponsoredOwnerCount
<< ", sponsoringCount: " << sponsoringOwnerCount;
// LCOV_EXCL_STOP
}
return detail::confineOwnerCount(currentOwnerCount, deltaCount);
}
template <typename ViewT>
TER
AccountRootEntry<ViewT>::checkReserve(
ApplyViewContext ctx,
XRPAmount accBalance,
std::optional<AccountRootEntry<ReadView>> const& sponsorSle,
Adjustment adj,
TER insufReserveCode) const
{
// TODO: swap to assert after fixCleanup3_2_0 is retired
if (!this->exists() || (*this)->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
XRPL_ASSERT(
!isTesSuccess(insufReserveCode), "xrpl::checkReserve : insufReserveCode is not tesSUCCESS");
if (ctx.view.rules().enabled(featureSponsor))
{
if (sponsorSle)
{
if ((*sponsorSle)->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sle = ctx.view.read(
keylet::sponsorship(
(*sponsorSle)->getAccountID(sfAccount), (*this)->getAccountID(sfAccount)));
// A reserve-sponsored tx must carry a sponsor signature
// (cosigning path) and/or have a pre-existing sponsorship SLE
// (prefunded path). Absence of both is an internal invariant break.
if (isReserveSponsored(ctx.tx) && !sle && !ctx.tx.isFieldPresent(sfSponsorSignature))
return tecINTERNAL; // LCOV_EXCL_LINE
if (sle)
{
auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount);
if (adj.ownerCountDelta > 0 &&
ownerCountAllowed < static_cast<std::uint32_t>(adj.ownerCountDelta))
return insufReserveCode;
}
auto const sponsorBalance = (*sponsorSle)->getFieldAmount(sfBalance).xrp();
XRPAmount const sponsorReserve = sponsorSle->reserve(adj);
if (sponsorBalance < sponsorReserve)
return insufReserveCode;
}
else
{
XRPAmount const reserve = this->reserve(adj);
if (accBalance < reserve)
return insufReserveCode;
}
}
else
{
XRPL_ASSERT(
!sponsorSle,
"xrpl::checkReserve : featureSponsor disabled and sponsorSle not provided");
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::checkReserve : accountCountDelta is 0");
auto const reserve = ctx.view.fees().accountReserve(
(*this)->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
if (accBalance < reserve)
return insufReserveCode;
}
return tesSUCCESS;
}
template <typename ViewT>
TER
AccountRootEntry<ViewT>::checkReserve(ApplyViewContext ctx, XRPAmount accBalance, Adjustment adj)
const
{
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, *this);
if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE
std::optional<AccountRootEntry<ReadView>> sponsorSle;
if (*sponsorExp)
sponsorSle.emplace(**sponsorExp);
return checkReserve(ctx, accBalance, sponsorSle, adj);
}
template <typename ViewT>
void
AccountRootEntry<ViewT>::adjustOwnerCountSigned(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::int32_t adjustment)
requires Base::kIsWritable
{
auto& view = this->applyView();
auto const j = this->journal();
if (view.rules().enabled(featureSponsor))
{
XRPL_ASSERT(this->exists(), "xrpl::adjustOwnerCountSigned : valid account sle");
if (!this->exists())
return; // LCOV_EXCL_LINE
auto const accountID = (*this)->getAccountID(sfAccount);
bool const validType = (*this)->getType() == ltACCOUNT_ROOT;
XRPL_ASSERT(validType, "xrpl::adjustOwnerCountSigned : valid account sle type");
if (!validType)
return; // LCOV_EXCL_LINE
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCountSigned : nonzero adjustment input");
OwnerCounts const currentOwnerCount(this->rawSle());
OwnerCounts totalOwnerCount(currentOwnerCount);
if (sponsorSle)
{
bool const validSponsorType = (*sponsorSle)->getType() == ltACCOUNT_ROOT;
XRPL_ASSERT(validSponsorType, "xrpl::adjustOwnerCountSigned : valid sponsor sle type");
if (!validSponsorType)
return; // LCOV_EXCL_LINE
auto const sponsorID = (*sponsorSle)->getAccountID(sfAccount);
totalOwnerCount.sponsored = detail::adjustOwnerCountImpl(
view, this->mutableRawSle(), sfSponsoredOwnerCount, accountID, adjustment, j);
{
OwnerCounts const sponsorCurrent(sponsorSle->rawSle());
OwnerCounts sponsorAdjustment(sponsorCurrent);
sponsorAdjustment.sponsoring = detail::adjustOwnerCountImpl(
view,
sponsorSle->mutableRawSle(),
sfSponsoringOwnerCount,
sponsorID,
adjustment,
j);
view.adjustOwnerCountHook(sponsorID, sponsorCurrent, sponsorAdjustment);
}
auto sponsorshipSle = view.peek(keylet::sponsorship(sponsorID, accountID));
if (sponsorshipSle && adjustment > 0)
{
// Only decrease the pre-funded ReserveCount on Sponsorship if we assign new
// objects. Removing/reassigning ownership of the object doesn't increase
// RemainingOwnerCount back. Don't call hook because this counter is not something
// that requires reserve (like other sf...OwnerCounts do).
detail::adjustOwnerCountImpl(
view, sponsorshipSle, sfRemainingOwnerCount, sponsorID, -adjustment, j);
}
}
totalOwnerCount.owner = detail::adjustOwnerCountImpl(
view, this->mutableRawSle(), sfOwnerCount, accountID, adjustment, j);
view.adjustOwnerCountHook(accountID, currentOwnerCount, totalOwnerCount);
}
else
{
XRPL_ASSERT(this->exists(), "xrpl::adjustOwnerCountSigned : valid account sle");
if (!this->exists())
return;
// the remaining are only asserts to preserve existing behavior
XRPL_ASSERT(!sponsorSle, "xrpl::adjustOwnerCountSigned : sponsor not enabled");
XRPL_ASSERT(
(*this)->getType() == ltACCOUNT_ROOT,
"xrpl::adjustOwnerCountSigned : valid account sle type");
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input");
std::uint32_t const current{(*this)->getFieldU32(sfOwnerCount)};
AccountID const id = (**this)[sfAccount];
std::uint32_t const adjusted = detail::confineOwnerCount(current, adjustment, id, j);
OwnerCounts const currentOwnerCount(this->rawSle());
OwnerCounts finalOwnerCount(currentOwnerCount);
finalOwnerCount.owner = adjusted;
view.adjustOwnerCountHook(id, currentOwnerCount, finalOwnerCount);
(*this)->at(sfOwnerCount) = adjusted;
this->update();
}
}
template <typename ViewT>
void
AccountRootEntry<ViewT>::increaseOwnerCount(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::uint32_t count)
requires Base::kIsWritable
{
XRPL_ASSERT(
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
"xrpl::increaseOwnerCount : count in signed delta range");
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
return; // LCOV_EXCL_LINE
adjustOwnerCountSigned(sponsorSle, static_cast<std::int32_t>(count));
}
template <typename ViewT>
void
AccountRootEntry<ViewT>::increaseOwnerCount(ApplyViewContext ctx, std::uint32_t count)
requires Base::kIsWritable
{
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, *this);
// The sponsor's existence is validated by checkReserve/checkSponsor before
// any owner-count mutation, so loading it here cannot fail.
XRPL_ASSERT(
sponsorExp.has_value(), "xrpl::increaseOwnerCount : sponsor validated before mutation");
std::optional<AccountRootEntry<ApplyView>> sponsorSle;
if (sponsorExp && *sponsorExp)
sponsorSle.emplace(std::move(**sponsorExp));
increaseOwnerCount(sponsorSle, count);
}
template <typename ViewT>
void
AccountRootEntry<ViewT>::decreaseOwnerCount(
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
std::uint32_t count)
requires Base::kIsWritable
{
XRPL_ASSERT(
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
"xrpl::decreaseOwnerCount : count in signed delta range");
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
return; // LCOV_EXCL_LINE
adjustOwnerCountSigned(sponsorSle, -static_cast<std::int32_t>(count));
}
template class AccountRootEntry<ReadView>;
template class AccountRootEntry<ApplyView>;
} // namespace xrpl
+3 -4
View File
@@ -11,7 +11,6 @@
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -642,7 +641,7 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
[&](Issue const& issue) { [&](Issue const& issue) {
if (isXRP(issue)) if (isXRP(issue))
{ {
if (auto const sle = AccountRootEntryR(ammAccountID, view)) if (auto const sle = view.read(keylet::account(ammAccountID)))
return (*sle)[sfBalance]; return (*sle)[sfBalance];
} }
else if ( else if (
@@ -758,7 +757,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
} }
auto const ammAccountID = (*ammSle)[sfAccount]; auto const ammAccountID = (*ammSle)[sfAccount];
auto sleAMMRoot = AccountRootEntryW(ammAccountID, sb); auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
if (!sleAMMRoot) if (!sleAMMRoot)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
@@ -796,7 +795,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
} }
sb.erase(ammSle); sb.erase(ammSle);
sleAMMRoot.erase(); sb.erase(sleAMMRoot);
return tesSUCCESS; return tesSUCCESS;
} }
+300 -22
View File
@@ -9,7 +9,6 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/OwnerCounts.h> #include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -18,6 +17,7 @@
#include <xrpl/protocol/Rate.h> #include <xrpl/protocol/Rate.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h> #include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h> #include <xrpl/protocol/XRPAmount.h>
#include <xrpl/protocol/digest.h> #include <xrpl/protocol/digest.h>
@@ -38,19 +38,25 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer)
{ {
if (isXRP(issuer)) if (isXRP(issuer))
return false; return false;
if (auto const sle = AccountRootEntryR(issuer, view)) if (auto const sle = view.read(keylet::account(issuer)))
return sle->isFlag(lsfGlobalFreeze); return sle->isFlag(lsfGlobalFreeze);
return false; return false;
} }
namespace detail { namespace {
// An owner count cannot be negative. If adjustment would cause a negative
// owner count, clamp the owner count at 0. Similarly for overflow. This
// adjustment allows the ownerCount to be adjusted up or down in multiple steps.
// If id != std::nullopt, then do error reporting.
//
// Returns adjusted owner count.
std::uint32_t std::uint32_t
confineOwnerCount( confineOwnerCount(
std::uint32_t currentOwnerCount, std::uint32_t currentOwnerCount,
std::int32_t ownerCountAdj, std::int32_t ownerCountAdj,
std::optional<AccountID> const& id, std::optional<AccountID> const& id = std::nullopt,
beast::Journal j) beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
{ {
std::uint32_t totalOwnerCount{currentOwnerCount + ownerCountAdj}; std::uint32_t totalOwnerCount{currentOwnerCount + ownerCountAdj};
if (ownerCountAdj > 0) if (ownerCountAdj > 0)
@@ -85,8 +91,10 @@ confineOwnerCount(
return totalOwnerCount; return totalOwnerCount;
} }
// Returns the number of account reserves funded by this account: 1 for itself (0 if sponsored by
// another account) plus the count of accounts it sponsors.
std::uint32_t std::uint32_t
accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, beast::Journal j) accountCountImpl(SLE::ConstRef sle, std::int32_t accountCountAdj, beast::Journal j)
{ {
bool const isSponsored = sle->isFieldPresent(sfSponsor); bool const isSponsored = sle->isFieldPresent(sfSponsor);
std::int64_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount); std::int64_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount);
@@ -123,25 +131,151 @@ adjustOwnerCountImpl(
{ {
std::uint32_t const currentOwnerCount = sle->at(sfield); std::uint32_t const currentOwnerCount = sle->at(sfield);
std::uint32_t const totalOwnerCount = std::uint32_t const totalOwnerCount =
detail::confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j); confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j);
sle->at(sfield) = totalOwnerCount; sle->at(sfield) = totalOwnerCount;
view.update(sle); view.update(sle);
return totalOwnerCount; return totalOwnerCount;
} }
} // namespace detail 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 XRPAmount
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j) xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
{ {
auto const sle = AccountRootEntryR(id, view); auto const sle = view.read(keylet::account(id));
if (!sle.exists()) if (sle == nullptr)
return beast::kZero; return beast::kZero;
// Return balance minus reserve // Return balance minus reserve
std::uint32_t const currentOwnerCount = detail::confineOwnerCount( std::uint32_t const currentOwnerCount =
view.ownerCountHook(id, OwnerCounts(sle.rawSle())).count(), ownerCountAdj); confineOwnerCount(view.ownerCountHook(id, OwnerCounts(sle)).count(), ownerCountAdj);
std::uint32_t const currentAccountCount = detail::accountCountImpl(sle, 0, j); std::uint32_t const currentAccountCount = accountCountImpl(sle, 0, j);
// Pseudo-accounts have no reserve requirement // Pseudo-accounts have no reserve requirement
auto const reserve = isPseudoAccount(sle) auto const reserve = isPseudoAccount(sle)
@@ -166,7 +300,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
Rate Rate
transferRate(ReadView const& view, AccountID const& issuer) transferRate(ReadView const& view, AccountID const& issuer)
{ {
auto const sle = AccountRootEntryR(issuer, view); auto const sle = view.read(keylet::account(issuer));
if (sle && sle->isFieldPresent(sfTransferRate)) if (sle && sle->isFieldPresent(sfTransferRate))
return Rate{sle->getFieldU32(sfTransferRate)}; return Rate{sle->getFieldU32(sfTransferRate)};
@@ -174,10 +308,57 @@ transferRate(ReadView const& view, AccountID const& issuer)
return kParityRate; return kParityRate;
} }
void
increaseOwnerCount(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::uint32_t count,
beast::Journal j)
{
XRPL_ASSERT(
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
"xrpl::increaseOwnerCount : count in signed delta range");
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
return; // LCOV_EXCL_LINE
adjustOwnerCountSigned(view, accountSle, sponsorSle, static_cast<std::int32_t>(count), j);
}
void
increaseOwnerCount(ApplyViewContext ctx, SLE::Ref accountSle, std::uint32_t count, beast::Journal j)
{
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, accountSle);
// The sponsor's existence is validated by checkReserve/checkSponsor before
// any owner-count mutation, so loading it here cannot fail.
XRPL_ASSERT(
sponsorExp.has_value(), "xrpl::increaseOwnerCount : sponsor validated before mutation");
increaseOwnerCount(ctx.view, accountSle, sponsorExp ? *sponsorExp : SLE::pointer(), count, j);
}
void
decreaseOwnerCount(
ApplyView& view,
SLE::Ref accountSle,
SLE::Ref sponsorSle,
std::uint32_t count,
beast::Journal j)
{
XRPL_ASSERT(
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
"xrpl::decreaseOwnerCount : count in signed delta range");
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
return; // LCOV_EXCL_LINE
adjustOwnerCountSigned(view, accountSle, sponsorSle, -static_cast<std::int32_t>(count), j);
}
void void
decreaseOwnerCountForObject( decreaseOwnerCountForObject(
ApplyView& view, ApplyView& view,
AccountRootEntryW& accountSle, SLE::Ref accountSle,
SLE::Ref objectSle, SLE::Ref objectSle,
std::uint32_t count, std::uint32_t count,
beast::Journal j) beast::Journal j)
@@ -191,8 +372,8 @@ decreaseOwnerCountForObject(
if (!validObjectType) if (!validObjectType)
return; // LCOV_EXCL_LINE return; // LCOV_EXCL_LINE
auto sponsorSle = getLedgerEntryReserveSponsor(view, objectSle); SLE::Ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle);
accountSle.decreaseOwnerCount(sponsorSle, count); decreaseOwnerCount(view, accountSle, sponsorSle, count, j);
} }
void void
@@ -212,10 +393,107 @@ adjustLoanBrokerOwnerCount(
if (delta == 0) if (delta == 0)
return; // LCOV_EXCL_LINE return; // LCOV_EXCL_LINE
detail::adjustOwnerCountImpl( adjustOwnerCountImpl(
view, brokerSle, sfOwnerCount, brokerSle->getAccountID(sfAccount), delta, j); view, brokerSle, sfOwnerCount, brokerSle->getAccountID(sfAccount), delta, j);
} }
XRPAmount
accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjustment adj)
{
XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle");
if (!view.rules().enabled(featureSponsor))
{
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::accountReserve : no account count delta");
return view.fees().accountReserve(sle->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
}
std::uint32_t const currentOwnerCount = ownerCount(sle, j, adj.ownerCountDelta);
std::uint32_t const currentAccountCount = accountCountImpl(sle, adj.accountCountDelta, j);
return view.fees().accountReserve(currentOwnerCount, currentAccountCount);
}
TER
checkReserve(
ApplyViewContext ctx,
SLE::ConstRef accSle,
XRPAmount accBalance,
SLE::ConstRef sponsorSle,
Adjustment adj,
beast::Journal j,
TER insufReserveCode)
{
// TODO: swap to assert after fixCleanup3_2_0 is retired
if (!accSle || accSle->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
XRPL_ASSERT(
!isTesSuccess(insufReserveCode), "xrpl::checkReserve : insufReserveCode is not tesSUCCESS");
if (ctx.view.rules().enabled(featureSponsor))
{
if (sponsorSle)
{
if (sponsorSle->getType() != ltACCOUNT_ROOT)
return tefINTERNAL; // LCOV_EXCL_LINE
auto const sle = ctx.view.read(
keylet::sponsorship(
sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount)));
// A reserve-sponsored tx must carry a sponsor signature
// (cosigning path) and/or have a pre-existing sponsorship SLE
// (prefunded path). Absence of both is an internal invariant break.
if (isReserveSponsored(ctx.tx) && !sle && !ctx.tx.isFieldPresent(sfSponsorSignature))
return tecINTERNAL; // LCOV_EXCL_LINE
if (sle)
{
auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount);
if (adj.ownerCountDelta > 0 &&
ownerCountAllowed < static_cast<std::uint32_t>(adj.ownerCountDelta))
return insufReserveCode;
}
auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance).xrp();
XRPAmount const sponsorReserve = accountReserve(ctx.view, sponsorSle, j, adj);
if (sponsorBalance < sponsorReserve)
return insufReserveCode;
}
else
{
XRPAmount const reserve = accountReserve(ctx.view, accSle, j, adj);
if (accBalance < reserve)
return insufReserveCode;
}
}
else
{
XRPL_ASSERT(
!sponsorSle,
"xrpl::checkReserve : featureSponsor disabled and sponsorSle not provided");
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::checkReserve : accountCountDelta is 0");
auto const reserve = ctx.view.fees().accountReserve(
accSle->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
if (accBalance < reserve)
return insufReserveCode;
}
return tesSUCCESS;
}
TER
checkReserve(
ApplyViewContext ctx,
SLE::ConstRef accSle,
XRPAmount accBalance,
Adjustment adj,
beast::Journal j)
{
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, accSle);
if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE
return checkReserve(ctx, accSle, accBalance, *sponsorExp, adj, j);
}
// ---------------------------------------------------- // ----------------------------------------------------
AccountID AccountID
@@ -229,7 +507,7 @@ pseudoAccountAddress(ReadView const& view, UInt256 const& pseudoOwnerKey)
auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey); auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey);
rsh(hash.data(), hash.size()); rsh(hash.data(), hash.size());
AccountID const ret = AccountID::fromRaw(static_cast<RipeshaHasher::result_type>(rsh)); AccountID const ret = AccountID::fromRaw(static_cast<RipeshaHasher::result_type>(rsh));
if (!AccountRootEntryR(ret, view).exists()) if (!view.read(keylet::account(ret)))
return ret; return ret;
} }
return beast::kZero; return beast::kZero;
@@ -268,7 +546,7 @@ getPseudoAccountFields()
} }
[[nodiscard]] bool [[nodiscard]] bool
isPseudoAccount(AccountRootEntryR const& sleAcct) isPseudoAccount(SLE::const_pointer sleAcct)
{ {
// Intentionally use defensive coding here because it's cheap and makes the // Intentionally use defensive coding here because it's cheap and makes the
// semantics of true return value clean. // semantics of true return value clean.
@@ -321,9 +599,9 @@ createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const
} }
[[nodiscard]] TER [[nodiscard]] TER
checkDestinationAndTag(AccountRootEntryR const& toSle, bool hasDestinationTag) checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag)
{ {
if (!toSle.exists()) if (toSle == nullptr)
return tecNO_DST; return tecNO_DST;
// The tag is basically account-specific information we don't // The tag is basically account-specific information we don't
@@ -7,7 +7,6 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -82,7 +81,7 @@ deleteSLE(ApplyView& view, SLE::Ref sleCredential, beast::Journal j)
auto delSLE = [&view, &sleCredential, j]( auto delSLE = [&view, &sleCredential, j](
AccountID const& account, SField const& node, bool isOwner) -> TER { AccountID const& account, SField const& node, bool isOwner) -> TER {
auto sleAccount = AccountRootEntryW(account, view); auto const sleAccount = view.peek(keylet::account(account));
if (!sleAccount) if (!sleAccount)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
+11 -15
View File
@@ -7,7 +7,6 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -207,7 +206,7 @@ authorizeMPToken(
std::uint32_t flags, std::uint32_t flags,
std::optional<AccountID> holderID) std::optional<AccountID> holderID)
{ {
auto sleAcct = AccountRootEntryW(account, ctx.view, journal); auto const sleAcct = ctx.view.peek(keylet::account(account));
if (!sleAcct) if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -243,10 +242,10 @@ authorizeMPToken(
// - create the MPToken object for the holder // - create the MPToken object for the holder
// A reserve sponsor only covers tx.Account's own objects. // A reserve sponsor only covers tx.Account's own objects.
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct); auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct);
if (!sponsorExp) if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE return sponsorExp.error(); // LCOV_EXCL_LINE
auto& sponsorSle = *sponsorExp; auto const sponsorSle = *sponsorExp;
// The reserve that is required to create the MPToken. Note // The reserve that is required to create the MPToken. Note
// that although the reserve increases with every item // that although the reserve increases with every item
@@ -256,10 +255,10 @@ authorizeMPToken(
// The "free-tier" shortcut (ownerCount < 2) does not apply once a sponsor is on // The "free-tier" shortcut (ownerCount < 2) does not apply once a sponsor is on
// the tx — the sponsor must always cover the reserve (via balance or prefunded // the tx — the sponsor must always cover the reserve (via balance or prefunded
// budget), so this check always runs for sponsored transactions. // budget), so this check always runs for sponsored transactions.
if (sponsorSle || sleAcct.ownerCount() >= 2) if (sponsorSle || ownerCount(sleAcct, journal) >= 2)
{ {
if (auto const ret = if (auto const ret = checkReserve(
sleAcct.checkReserve(ctx, priorBalance, sponsorSle, {.ownerCountDelta = 1}); ctx, sleAcct, priorBalance, sponsorSle, {.ownerCountDelta = 1}, journal);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
} }
@@ -286,8 +285,8 @@ authorizeMPToken(
ctx.view.insert(mptoken); ctx.view.insert(mptoken);
// Update owner count. // Update owner count.
sleAcct.increaseOwnerCount(sponsorSle, 1); increaseOwnerCount(ctx.view, sleAcct, sponsorSle, 1, journal);
addSponsorToLedgerEntry(mptoken, sponsorSle ? sponsorSle->rawSle() : SLE::const_pointer{}); addSponsorToLedgerEntry(mptoken, sponsorSle);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -416,7 +415,7 @@ requireAuth(
} }
// requireAuth is recursive if the issuer is a vault pseudo-account // requireAuth is recursive if the issuer is a vault pseudo-account
auto const sleIssuer = AccountRootEntryR(mptIssuer, view); auto const sleIssuer = view.read(keylet::account(mptIssuer));
if (!sleIssuer) if (!sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -1021,16 +1020,13 @@ checkCreateMPT(
{ {
return err; return err;
} }
auto sleAcct = AccountRootEntryW(holder, view, j); auto const sleAcct = view.peek(keylet::account(holder));
if (!sleAcct) if (!sleAcct)
{ {
return tecINTERNAL; return tecINTERNAL;
} }
std::optional<AccountRootEntryW> sponsorAccSle; increaseOwnerCount(view, sleAcct, sponsorSle, 1, j);
if (sponsorSle)
sponsorAccSle.emplace(sponsorSle->getAccountID(sfAccount), view);
sleAcct.increaseOwnerCount(sponsorAccSle, 1);
} }
return tesSUCCESS; return tesSUCCESS;
} }
@@ -7,7 +7,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -856,7 +855,7 @@ tokenOfferCreatePreclaim(
if (nftIssuer != acctID && ((nftFlags & nft::kFlagTransferable) == 0)) if (nftIssuer != acctID && ((nftFlags & nft::kFlagTransferable) == 0))
{ {
auto const root = AccountRootEntryR(nftIssuer, view); auto const root = view.read(keylet::account(nftIssuer));
XRPL_ASSERT(root, "xrpl::nft::tokenOfferCreatePreclaim : non-null account"); XRPL_ASSERT(root, "xrpl::nft::tokenOfferCreatePreclaim : non-null account");
if (auto minter = (*root)[~sfNFTokenMinter]; minter != acctID) if (auto minter = (*root)[~sfNFTokenMinter]; minter != acctID)
@@ -887,7 +886,7 @@ tokenOfferCreatePreclaim(
{ {
// If a destination is specified, the destination must already be in // If a destination is specified, the destination must already be in
// the ledger. // the ledger.
auto const sleDst = AccountRootEntryR(*dest, view); auto const sleDst = view.read(keylet::account(*dest));
if (!sleDst) if (!sleDst)
return tecNO_DST; return tecNO_DST;
@@ -899,7 +898,7 @@ tokenOfferCreatePreclaim(
if (owner) if (owner)
{ {
auto const sleOwner = AccountRootEntryR(*owner, view); auto const sleOwner = view.read(keylet::account(*owner));
// defensively check // defensively check
// it should not be possible to specify owner that doesn't exist // it should not be possible to specify owner that doesn't exist
@@ -938,8 +937,9 @@ tokenOfferCreateApply(
beast::Journal j, beast::Journal j,
std::uint32_t txFlags) std::uint32_t txFlags)
{ {
if (auto const acct = AccountRootEntryW(acctID, view, j); Keylet const acctKeylet = keylet::account(acctID);
priorBalance < acct.reserve({.ownerCountDelta = 1})) if (auto const acct = view.read(acctKeylet);
priorBalance < accountReserve(view, acct, j, {.ownerCountDelta = 1}))
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
auto const offerID = keylet::nftokenOffer(acctID, seqProxy); auto const offerID = keylet::nftokenOffer(acctID, seqProxy);
@@ -1008,7 +1008,7 @@ checkTrustlineAuthorized(
if (view.rules().enabled(fixEnforceNFTokenTrustlineV2)) if (view.rules().enabled(fixEnforceNFTokenTrustlineV2))
{ {
auto const issuerAccount = AccountRootEntryR(issue.account, view); auto const issuerAccount = view.read(keylet::account(issue.account));
if (!issuerAccount) if (!issuerAccount)
{ {
JLOG(j.debug()) << "xrpl::nft::checkTrustlineAuthorized: can't " JLOG(j.debug()) << "xrpl::nft::checkTrustlineAuthorized: can't "
@@ -1060,7 +1060,7 @@ checkTrustlineDeepFrozen(
if (view.rules().enabled(featureDeepFreeze)) if (view.rules().enabled(featureDeepFreeze))
{ {
auto const issuerAccount = AccountRootEntryR(issue.account, view); auto const issuerAccount = view.read(keylet::account(issue.account));
if (!issuerAccount) if (!issuerAccount)
{ {
JLOG(j.debug()) << "xrpl::nft::checkTrustlineDeepFrozen: can't " JLOG(j.debug()) << "xrpl::nft::checkTrustlineDeepFrozen: can't "
@@ -6,7 +6,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -52,7 +51,7 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
} }
// Transfer amount back to owner, decrement owner count // Transfer amount back to owner, decrement owner count
auto sle = AccountRootEntryW(src, view); auto const sle = view.peek(keylet::account(src));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -60,7 +59,7 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
(*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount"); (*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount");
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance]; (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
decreaseOwnerCountForObject(view, sle, slep, 1, j); decreaseOwnerCountForObject(view, sle, slep, 1, j);
sle.update(); view.update(sle);
// Remove PayChan from ledger // Remove PayChan from ledger
view.erase(slep); view.erase(slep);
@@ -7,7 +7,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -134,14 +133,14 @@ isFrozen(
{ {
if (isXRP(currency)) if (isXRP(currency))
return false; return false;
auto const sleIssuer = AccountRootEntryR(issuer, view); auto sle = view.read(keylet::account(issuer));
if (sleIssuer && sleIssuer->isFlag(lsfGlobalFreeze)) if (sle && sle->isFlag(lsfGlobalFreeze))
return true; return true;
if (issuer != account) if (issuer != account)
{ {
// Check if the issuer froze the line // Check if the issuer froze the line
auto const sleLine = view.read(keylet::trustLine(account, issuer, currency)); sle = view.read(keylet::trustLine(account, issuer, currency));
if (sleLine && sleLine->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze)) if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
return true; return true;
} }
return false; return false;
@@ -239,7 +238,7 @@ trustCreate(
XRPL_ASSERT( XRPL_ASSERT(
sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID), sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID),
"xrpl::trustCreate : matching account ID"); "xrpl::trustCreate : matching account ID");
auto const slePeer = AccountRootEntryW(bSetHigh ? uLowAccountID : uHighAccountID, view); auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
if (!slePeer) if (!slePeer)
return tecNO_TARGET; return tecNO_TARGET;
@@ -284,11 +283,7 @@ trustCreate(
} }
sleRippleState->setFieldU32(sfFlags, uFlags); sleRippleState->setFieldU32(sfFlags, uFlags);
AccountRootEntryW accountSle(bSetHigh ? uHighAccountID : uLowAccountID, view, j); increaseOwnerCount(view, sleAccount, sponsorSle, 1, j);
std::optional<AccountRootEntryW> sponsorAccSle;
if (sponsorSle)
sponsorAccSle.emplace(sponsorSle->getAccountID(sfAccount), view);
accountSle.increaseOwnerCount(sponsorAccSle, 1);
addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsor : sfLowSponsor); addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsor : sfLowSponsor);
@@ -354,7 +349,7 @@ updateTrustLine(
if (!state) if (!state)
return false; return false;
auto sle = AccountRootEntryW(sender, view, j); auto sle = view.peek(keylet::account(sender));
if (!sle) if (!sle)
return false; return false;
@@ -381,9 +376,9 @@ updateTrustLine(
{ {
// VFALCO Where is the line being deleted? // VFALCO Where is the line being deleted?
// Clear the reserve of the sender, possibly delete the line! // Clear the reserve of the sender, possibly delete the line!
auto currentSponsor = auto const currentSponsor =
getLedgerEntryReserveSponsor(view, state, bSenderHigh ? sfHighSponsor : sfLowSponsor); getLedgerEntryReserveSponsor(view, state, bSenderHigh ? sfHighSponsor : sfLowSponsor);
sle.decreaseOwnerCount(currentSponsor, 1); decreaseOwnerCount(view, sle, currentSponsor, 1, j);
// Clear reserve flag. // Clear reserve flag.
state->clearFlag(senderReserveFlag); state->clearFlag(senderReserveFlag);
@@ -469,7 +464,7 @@ issueIOU(
finalBalance.get<Issue>().account = noAccount(); finalBalance.get<Issue>().account = noAccount();
auto receiverAccount = AccountRootEntryW(account, view); auto const receiverAccount = view.peek(keylet::account(account));
if (!receiverAccount) if (!receiverAccount)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -481,7 +476,7 @@ issueIOU(
issue.account, issue.account,
account, account,
index.key, index.key,
receiverAccount.mutableRawSle(), receiverAccount,
false, false,
noRipple, noRipple,
false, false,
@@ -584,7 +579,7 @@ requireAuth(ReadView const& view, Issue const& issue, AccountID const& account,
// If this is a weak or legacy check, or if the account has a line, fail if // If this is a weak or legacy check, or if the account has a line, fail if
// auth is required and not set on the line // auth is required and not set on the line
if (auto const issuerAccount = AccountRootEntryR(issue.account, view); if (auto const issuerAccount = view.read(keylet::account(issue.account));
issuerAccount && issuerAccount->isFlag(lsfRequireAuth)) issuerAccount && issuerAccount->isFlag(lsfRequireAuth))
{ {
if (trustLine) if (trustLine)
@@ -614,8 +609,8 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
auto const& issuerId = issue.getIssuer(); auto const& issuerId = issue.getIssuer();
if (issuerId == from || issuerId == to) if (issuerId == from || issuerId == to)
return tesSUCCESS; return tesSUCCESS;
auto const sleIssuer = AccountRootEntryR(issuerId, view); auto const sleIssuer = view.read(keylet::account(issuerId));
if (!sleIssuer.exists()) if (sleIssuer == nullptr)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const isRippleDisabled = [&](AccountID account) -> bool { auto const isRippleDisabled = [&](AccountID account) -> bool {
@@ -670,8 +665,8 @@ addEmptyHolding(
if (isGlobalFrozen(ctx.view, issuerId)) if (isGlobalFrozen(ctx.view, issuerId))
return tecFROZEN; // LCOV_EXCL_LINE return tecFROZEN; // LCOV_EXCL_LINE
auto const sleSrc = AccountRootEntryW(srcId, ctx.view); auto const sleSrc = ctx.view.peek(keylet::account(srcId));
auto sleDst = AccountRootEntryW(dstId, ctx.view, journal); auto const sleDst = ctx.view.peek(keylet::account(dstId));
if (!sleDst || !sleSrc) if (!sleDst || !sleSrc)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Create path: DefaultRipple is still required. terNO_RIPPLE is // Create path: DefaultRipple is still required. terNO_RIPPLE is
@@ -686,14 +681,20 @@ addEmptyHolding(
return tecDUPLICATE; return tecDUPLICATE;
// A reserve sponsor only covers tx.Account's own objects. // A reserve sponsor only covers tx.Account's own objects.
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst); auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst);
if (!sponsorExp) if (!sponsorExp)
return sponsorExp.error(); // LCOV_EXCL_LINE return sponsorExp.error(); // LCOV_EXCL_LINE
auto& sponsorSle = *sponsorExp; auto const sponsorSle = *sponsorExp;
// Can the account cover the trust line reserve ? // Can the account cover the trust line reserve ?
if (auto const ret = sleDst.checkReserve( if (auto const ret = checkReserve(
ctx, priorBalance, sponsorSle, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE); ctx,
sleDst,
priorBalance,
sponsorSle,
{.ownerCountDelta = 1},
journal,
tecNO_LINE_INSUF_RESERVE);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
return ret; return ret;
@@ -705,7 +706,7 @@ addEmptyHolding(
srcId, srcId,
dstId, dstId,
index.key, index.key,
sleDst.mutableRawSle(), sleDst,
/*bAuth=*/false, /*bAuth=*/false,
/*bNoRipple=*/true, /*bNoRipple=*/true,
/*bFreeze=*/false, /*bFreeze=*/false,
@@ -714,7 +715,7 @@ addEmptyHolding(
/*saLimit=*/STAmount{Issue{currency, dstId}}, /*saLimit=*/STAmount{Issue{currency, dstId}},
/*uQualityIn=*/0, /*uQualityIn=*/0,
/*uQualityOut=*/0, /*uQualityOut=*/0,
sponsorSle ? sponsorSle->mutableRawSle() : SLE::pointer{}, sponsorSle,
journal); journal);
} }
@@ -727,7 +728,7 @@ removeEmptyHolding(
{ {
if (issue.native()) if (issue.native())
{ {
auto const sle = AccountRootEntryW(accountID, ctx.view); auto const sle = ctx.view.read(keylet::account(accountID));
if (!sle) if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -752,14 +753,13 @@ removeEmptyHolding(
if (line->isFlag(lsfLowReserve)) if (line->isFlag(lsfLowReserve))
{ {
// Clear reserve for low account. // Clear reserve for low account.
auto sleLowAccount = auto sleLowAccount = ctx.view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
AccountRootEntryW(line->at(sfLowLimit)->getIssuer(), ctx.view, journal);
if (!sleLowAccount) if (!sleLowAccount)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor); auto const currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor);
sleLowAccount.decreaseOwnerCount(currentLowSponsor, 1); decreaseOwnerCount(ctx.view, sleLowAccount, currentLowSponsor, 1, journal);
// It's not really necessary to clear the reserve flag, since the line // It's not really necessary to clear the reserve flag, since the line
// is about to be deleted, but this will make the metadata reflect an // is about to be deleted, but this will make the metadata reflect an
// accurate state at the time of deletion. // accurate state at the time of deletion.
@@ -770,14 +770,13 @@ removeEmptyHolding(
if (line->isFlag(lsfHighReserve)) if (line->isFlag(lsfHighReserve))
{ {
// Clear reserve for high account. // Clear reserve for high account.
auto sleHighAccount = auto sleHighAccount = ctx.view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
AccountRootEntryW(line->at(sfHighLimit)->getIssuer(), ctx.view, journal);
if (!sleHighAccount) if (!sleHighAccount)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor); auto const currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor);
sleHighAccount.decreaseOwnerCount(currentHighSponsor, 1); decreaseOwnerCount(ctx.view, sleHighAccount, currentHighSponsor, 1, journal);
// It's not really necessary to clear the reserve flag, since the line // It's not really necessary to clear the reserve flag, since the line
// is about to be deleted, but this will make the metadata reflect an // is about to be deleted, but this will make the metadata reflect an
// accurate state at the time of deletion. // accurate state at the time of deletion.
@@ -806,8 +805,8 @@ deleteAMMTrustLine(
auto const& [low, high] = std::minmax( auto const& [low, high] = std::minmax(
sleState->getFieldAmount(sfLowLimit).getIssuer(), sleState->getFieldAmount(sfLowLimit).getIssuer(),
sleState->getFieldAmount(sfHighLimit).getIssuer()); sleState->getFieldAmount(sfHighLimit).getIssuer());
auto sleLow = AccountRootEntryW(low, view, j); auto sleLow = view.peek(keylet::account(low));
auto sleHigh = AccountRootEntryW(high, view, j); auto sleHigh = view.peek(keylet::account(high));
if (!sleLow || !sleHigh) if (!sleLow || !sleHigh)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -826,7 +825,7 @@ deleteAMMTrustLine(
if (ammAccountID && (low != *ammAccountID && high != *ammAccountID)) if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
return terNO_AMM; return terNO_AMM;
auto sponsorSle = auto const sponsorSle =
getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor); getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor);
if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter)) if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter))
@@ -839,7 +838,7 @@ deleteAMMTrustLine(
if (!sleState->isFlag(uFlags)) if (!sleState->isFlag(uFlags))
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
(!ammLow ? sleLow : sleHigh).decreaseOwnerCount(sponsorSle, 1); decreaseOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, 1, j);
return tesSUCCESS; return tesSUCCESS;
} }
+18 -19
View File
@@ -3,7 +3,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h> #include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/ledger/helpers/ProposalHelpers.h> #include <xrpl/ledger/helpers/ProposalHelpers.h>
@@ -77,7 +76,7 @@ getTxReserveSponsorID(STTx const& tx)
return {}; return {};
} }
std::expected<std::optional<AccountRootEntryW>, TER> std::expected<SLE::pointer, TER>
getTxReserveSponsor(ApplyViewContext ctx) getTxReserveSponsor(ApplyViewContext ctx)
{ {
auto const sponsorID = getTxReserveSponsorID(ctx.tx); auto const sponsorID = getTxReserveSponsorID(ctx.tx);
@@ -86,17 +85,17 @@ getTxReserveSponsor(ApplyViewContext ctx)
XRPL_ASSERT( XRPL_ASSERT(
ctx.view.rules().enabled(featureSponsor), ctx.view.rules().enabled(featureSponsor),
"xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled"); "xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
auto sle = AccountRootEntryW(*sponsorID, ctx.view); auto sle = ctx.view.peek(keylet::account(*sponsorID));
// already checked in Transactor::checkSponsor // already checked in Transactor::checkSponsor
if (!sle.exists()) if (!sle)
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
return sle; return sle;
} }
return std::nullopt; return SLE::pointer();
} }
std::expected<std::optional<AccountRootEntryR>, TER> std::expected<SLE::const_pointer, TER>
getTxReserveSponsor(ReadView const& view, STTx const& tx) getTxReserveSponsor(ReadView const& view, STTx const& tx)
{ {
auto const sponsorID = getTxReserveSponsorID(tx); auto const sponsorID = getTxReserveSponsorID(tx);
@@ -105,18 +104,18 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx)
XRPL_ASSERT( XRPL_ASSERT(
view.rules().enabled(featureSponsor), view.rules().enabled(featureSponsor),
"xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled"); "xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
auto sle = AccountRootEntryR(*sponsorID, view); auto sle = view.read(keylet::account(*sponsorID));
// already checked in Transactor::checkSponsor // already checked in Transactor::checkSponsor
if (!sle.exists()) if (!sle)
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
return sle; return sle;
} }
return std::nullopt; return SLE::pointer();
} }
std::expected<std::optional<AccountRootEntryW>, TER> std::expected<SLE::pointer, TER>
getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle) getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle)
{ {
// A reserve sponsor only covers tx.Account's own objects. // A reserve sponsor only covers tx.Account's own objects.
if (ctx.view.rules().enabled(fixCleanup3_2_0)) if (ctx.view.rules().enabled(fixCleanup3_2_0))
@@ -134,7 +133,7 @@ getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& acco
} }
if (isPseudoAccount(accountSle) || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount]) if (isPseudoAccount(accountSle) || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount])
return std::nullopt; return SLE::pointer();
return getTxReserveSponsor(ctx); return getTxReserveSponsor(ctx);
} }
@@ -152,7 +151,7 @@ getLedgerEntryReserveSponsorID(SLE::ConstRef sle, SF_ACCOUNT const& field)
return {}; return {};
} }
std::optional<AccountRootEntryW> SLE::pointer
getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT const& field) getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT const& field)
{ {
auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field); auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field);
@@ -161,9 +160,9 @@ getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT cons
XRPL_ASSERT( XRPL_ASSERT(
view.rules().enabled(featureSponsor), view.rules().enabled(featureSponsor),
"xrpl::getLedgerEntryReserveSponsor : sponsor exists + Sponsor enabled"); "xrpl::getLedgerEntryReserveSponsor : sponsor exists + Sponsor enabled");
return AccountRootEntryW(*sponsorID, view); return view.peek(keylet::account(*sponsorID));
} }
return std::nullopt; return {};
} }
void void
@@ -186,16 +185,16 @@ addSponsorToLedgerEntry(SLE::Ref sle, SLE::ConstRef sponsorSle, SF_ACCOUNT const
void void
addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::Ref sle, SF_ACCOUNT const& field) addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::Ref sle, SF_ACCOUNT const& field)
{ {
// getTxReserveSponsor yields nullopt when the tx is not reserve-sponsored, // getTxReserveSponsor yields a null pointer when the tx is not
// so addSponsorToLedgerEntry becomes a no-op then. The error case // reserve-sponsored, so addSponsorToLedgerEntry becomes a no-op then. The
// (tecINTERNAL) is an already-checked invariant; skip stamping. // error case (tecINTERNAL) is an already-checked invariant; skip stamping.
auto const sponsorSle = getTxReserveSponsor(ctx); auto const sponsorSle = getTxReserveSponsor(ctx);
if (sponsorSle && *sponsorSle) if (sponsorSle && *sponsorSle)
{ {
XRPL_ASSERT( XRPL_ASSERT(
ctx.view.rules().enabled(featureSponsor), ctx.view.rules().enabled(featureSponsor),
"xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled"); "xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled");
addSponsorToLedgerEntry(sle, (**sponsorSle).rawSle(), field); addSponsorToLedgerEntry(sle, *sponsorSle, field);
} }
} }
+18 -24
View File
@@ -7,7 +7,6 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -294,7 +293,7 @@ getLineIfUsable(
// we need to check if the associated assets have been frozen // we need to check if the associated assets have been frozen
if (view.rules().enabled(fixFrozenLPTokenTransfer)) if (view.rules().enabled(fixFrozenLPTokenTransfer))
{ {
auto const sleIssuer = AccountRootEntryR(issuer, view); auto const sleIssuer = view.read(keylet::account(issuer));
if (!sleIssuer) if (!sleIssuer)
{ {
return nullptr; // LCOV_EXCL_LINE return nullptr; // LCOV_EXCL_LINE
@@ -563,7 +562,7 @@ canAddHolding(ReadView const& view, Issue const& issue)
return tesSUCCESS; // No special checks for XRP return tesSUCCESS; // No special checks for XRP
} }
auto const issuer = AccountRootEntryR(issue.getIssuer(), view); auto const issuer = view.read(keylet::account(issue.getIssuer()));
if (!issuer) if (!issuer)
{ {
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -739,8 +738,6 @@ directSendNoFeeIOU(
auto const senderFreezeFlag = bSenderHigh ? lsfHighFreeze : lsfLowFreeze; auto const senderFreezeFlag = bSenderHigh ? lsfHighFreeze : lsfLowFreeze;
auto const receiverReserveFlag = bSenderHigh ? lsfLowReserve : lsfHighReserve; auto const receiverReserveFlag = bSenderHigh ? lsfLowReserve : lsfHighReserve;
AccountRootEntryW senderSle(uSenderID, view, j);
// FIXME This NEEDS to be cleaned up and simplified. It's impossible // FIXME This NEEDS to be cleaned up and simplified. It's impossible
// for anyone to understand. // for anyone to understand.
if (saBefore > beast::kZero if (saBefore > beast::kZero
@@ -749,7 +746,8 @@ directSendNoFeeIOU(
// Sender is zero or negative. // Sender is zero or negative.
&& sleRippleState->isFlag(senderReserveFlag) && sleRippleState->isFlag(senderReserveFlag)
// Sender reserve is set. // Sender reserve is set.
&& sleRippleState->isFlag(senderNoRippleFlag) != senderSle->isFlag(lsfDefaultRipple) && && sleRippleState->isFlag(senderNoRippleFlag) !=
view.read(keylet::account(uSenderID))->isFlag(lsfDefaultRipple) &&
!sleRippleState->isFlag(senderFreezeFlag) && !sleRippleState->isFlag(senderFreezeFlag) &&
!sleRippleState->getFieldAmount(bSenderHigh ? sfHighLimit : sfLowLimit) !sleRippleState->getFieldAmount(bSenderHigh ? sfHighLimit : sfLowLimit)
// Sender trust limit is 0. // Sender trust limit is 0.
@@ -760,9 +758,9 @@ directSendNoFeeIOU(
// Sender quality out is 0. // Sender quality out is 0.
{ {
// Clear the reserve of the sender, possibly delete the line! // Clear the reserve of the sender, possibly delete the line!
auto currentSponsor = getLedgerEntryReserveSponsor( auto const currentSponsor = getLedgerEntryReserveSponsor(
view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor);
senderSle.decreaseOwnerCount(currentSponsor, 1); decreaseOwnerCount(view, view.peek(keylet::account(uSenderID)), currentSponsor, 1, j);
removeSponsorFromLedgerEntry( removeSponsorFromLedgerEntry(
sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor); sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor);
@@ -807,7 +805,7 @@ directSendNoFeeIOU(
<< to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : " << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : "
<< saAmount.getFullText(); << saAmount.getFullText();
auto sleAccount = AccountRootEntryW(uReceiverID, view); auto const sleAccount = view.peek(keylet::account(uReceiverID));
if (!sleAccount) if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -819,7 +817,7 @@ directSendNoFeeIOU(
uSenderID, uSenderID,
uReceiverID, uReceiverID,
index.key, index.key,
sleAccount.mutableRawSle(), sleAccount,
false, false,
noRipple, noRipple,
false, false,
@@ -1012,12 +1010,10 @@ accountSendIOU(
*/ */
TER terResult(tesSUCCESS); TER terResult(tesSUCCESS);
SLE::pointer const sender = uSenderID != beast::kZero SLE::pointer const sender =
? AccountRootEntryW(uSenderID, view).mutableRawSle() uSenderID != beast::kZero ? view.peek(keylet::account(uSenderID)) : SLE::pointer();
: SLE::pointer(); SLE::pointer const receiver =
SLE::pointer const receiver = uReceiverID != beast::kZero uReceiverID != beast::kZero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer();
? AccountRootEntryW(uReceiverID, view).mutableRawSle()
: SLE::pointer();
if (auto stream = j.trace()) if (auto stream = j.trace())
{ {
@@ -1110,9 +1106,8 @@ accountSendMultiIOU(
* ensure that transfers are balanced. * ensure that transfers are balanced.
*/ */
SLE::pointer const sender = senderID != beast::kZero SLE::pointer const sender =
? AccountRootEntryW(senderID, view).mutableRawSle() senderID != beast::kZero ? view.peek(keylet::account(senderID)) : SLE::pointer();
: SLE::pointer();
if (auto stream = j.trace()) if (auto stream = j.trace())
{ {
@@ -1143,9 +1138,8 @@ accountSendMultiIOU(
if (!amount || (senderID == receiverID)) if (!amount || (senderID == receiverID))
continue; continue;
SLE::pointer const receiver = receiverID != beast::kZero SLE::pointer const receiver =
? AccountRootEntryW(receiverID, view).mutableRawSle() receiverID != beast::kZero ? view.peek(keylet::account(receiverID)) : SLE::pointer();
: SLE::pointer();
if (auto stream = j.trace()) if (auto stream = j.trace())
{ {
@@ -1597,8 +1591,8 @@ transferXRP(
XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver"); XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver");
XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP"); XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP");
SLE::pointer const sender = AccountRootEntryW(from, view).mutableRawSle(); SLE::pointer const sender = view.peek(keylet::account(from));
SLE::pointer const receiver = AccountRootEntryW(to, view).mutableRawSle(); SLE::pointer const receiver = view.peek(keylet::account(to));
if (!sender || !receiver) if (!sender || !receiver)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -317,8 +317,8 @@ public:
} }
else else
{ {
status = static_cast<Status>( status = checkedCast<Status>(
static_cast<int>(Status::CustomCode) + unsafeCast<int>(getStatus.code())); safeCast<int>(Status::CustomCode) + safeCast<int>(getStatus.code()));
JLOG(journal.error()) << getStatus.ToString(); JLOG(journal.error()) << getStatus.ToString();
} }
+114 -61
View File
@@ -8,6 +8,8 @@
#include <xrpl/protocol/digest.h> #include <xrpl/protocol/digest.h>
#include <xrpl/protocol/tokens.h> #include <xrpl/protocol/tokens.h>
#include <algorithm>
#include <array>
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
@@ -19,85 +21,136 @@
namespace xrpl { namespace xrpl {
namespace detail { namespace {
/** /** If true, we implement a cache for account ID lookups.
* Caches the base58 representations of AccountIDs
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.
*/ */
class AccountIdCache 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
{ {
private: /** The sequence lock for the entry.
struct CachedAccountID
{
AccountID id;
char encoding[40] = {0};
};
// The actual cache An even value means that the data is stable, whereas an odd
std::vector<CachedAccountID> cache_; value indicates the data is being actively modified.
// We use a hash function designed to resist algorithmic complexity attacks We use a 32-bit value to ensure that the counter will never
HardenedHash<> hasher_; return to an observed value during a read window.
*/
std::atomic<std::uint32_t> seq{0};
// 64 spinlocks, packed into a single 64-bit value /** The account id. */
std::atomic<std::uint64_t> locks_ = 0; AccountID id{};
public: /** The length of the cached encoding.
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();
}
std::string This field disambiguates collisions between ACCOUNT_ZERO and
toBase58(AccountID const& id) never-written-to slots, since they would otherwise share the
{ same encoded bits.
auto const index = hasher_(id) % cache_.size(); */
std::uint8_t len = 0;
PackedSpinlock sl(locks_, index % 64); /** The actual string encoding. */
char encoding[39] = {0};
{
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;
}
}; };
} // namespace detail static_assert(sizeof(CachedAccountID) == 64 && alignof(CachedAccountID) == 64);
static std::unique_ptr<detail::AccountIdCache> gAccountIdCache; // 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{};
void } // namespace
initAccountIdCache(std::size_t count)
{
if (!gAccountIdCache && count != 0)
gAccountIdCache = std::make_unique<detail::AccountIdCache>(count);
}
std::string std::string
toBase58(AccountID const& v) toBase58(AccountID const& v)
{ {
if (gAccountIdCache) // Since the input bits are generated by a cryptographically secure
return gAccountIdCache->toBase58(v); // 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();
return encodeBase58Token(TokenType::AccountID, v.data(), v.size()); 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;
} }
template <> template <>
+75 -47
View File
@@ -5,6 +5,7 @@
#include <xrpl/basics/UnorderedContainers.h> #include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h> #include <xrpl/basics/contract.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/hash/uhash.h> #include <xrpl/beast/hash/uhash.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h> #include <xrpl/json/json_value.h>
@@ -17,9 +18,13 @@
#include <xrpl/protocol/jss.h> #include <xrpl/protocol/jss.h>
#include <algorithm> #include <algorithm>
#include <concepts>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <memory> #include <memory>
#include <optional>
#include <stdexcept> #include <stdexcept>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -36,10 +41,10 @@ STPathElement::getHash(STPathElement const& element)
// important. We don't even really need to fully hash the whole // important. We don't even really need to fully hash the whole
// base_uint here, as a few bytes would do for our use. // base_uint here, as a few bytes would do for our use.
// //
// The note above is only true because the result of this function reaches // The note above is only true because the result of this function is only
// nothing but STPathElement::operator==, where it is a fast-reject // used by the STPathElement equality operator as a fast-reject prefilter,
// prefilter ahead of the field comparisons that decide the answer. Do not // ahead of the field comparisons that then decide the answer. Do not use
// use it to key a container. // it to key a container.
for (auto const x : element.getAccountID()) for (auto const x : element.getAccountID())
hashAccount += (hashAccount * 257) ^ x; hashAccount += (hashAccount * 257) ^ x;
@@ -106,12 +111,38 @@ STPathSet::operator=(STPathSet const& other)
STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name) STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
{ {
std::vector<STPathElement> path; using enum STPathElement::Type;
for (;;)
{
int const iType = sit.get8();
if (iType == STPathElement::TypeNone || iType == STPathElement::TypeBoundary) 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
{
type = parsePathElementType(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 (path.empty()) if (path.empty())
{ {
@@ -122,28 +153,11 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
// Move rather than converting the vector to an STPath by copy. // Move rather than converting the vector to an STPath by copy.
value_.emplace_back(std::move(path)); value_.emplace_back(std::move(path));
path.clear(); 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 else
{ {
auto const hasAccount = (iType & STPathElement::TypeAccount) != 0u; bool const hasCurrency = ((*type & TypeCurrency) == TypeCurrency);
auto const hasCurrency = (iType & STPathElement::TypeCurrency) != 0u; bool const hasMPT = ((*type & TypeMpt) == TypeMpt);
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) if (hasCurrency && hasMPT)
{ {
@@ -151,18 +165,26 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
Throw<std::runtime_error>("bad path element: MPT and Currency"); 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) if (hasCurrency)
asset = Currency::fromRaw(sit.get160()); asset = Currency::fromRaw(sit.get160());
if (hasMPT) if (hasMPT)
asset = sit.get192(); asset = sit.get192();
if (hasIssuer) if ((*type & TypeIssuer) == TypeIssuer)
issuer = sit.get160(); issuer = sit.get160();
path.emplace_back(account, asset, issuer, hasCurrency || hasMPT); path.emplace_back(account, asset, issuer, hasCurrency || hasMPT);
} }
} } while (type != TypeNone);
} }
STBase* STBase*
@@ -216,24 +238,22 @@ STPath::getJson(JsonOptions) const
for (auto const& it : path_) for (auto const& it : path_)
{ {
json::Value elem(json::ValueType::Object); json::Value elem(json::ValueType::Object);
auto const iType = it.getNodeType();
elem[jss::type] = iType; elem[jss::type] = safeCast<std::uint32_t>(it.getNodeType());
if ((iType & STPathElement::TypeAccount) != 0u) if (it.isType(STPathElement::TypeAccount))
elem[jss::account] = to_string(it.getAccountID()); elem[jss::account] = to_string(it.getAccountID());
XRPL_ASSERT( XRPL_ASSERT(
((iType & STPathElement::TypeCurrency) == 0u) || !(it.hasCurrency() && it.hasMPT()),
((iType & STPathElement::TypeMpt) == 0u),
"xrpl::STPath::getJson : not type Currency and MPT"); "xrpl::STPath::getJson : not type Currency and MPT");
if ((iType & STPathElement::TypeCurrency) != 0u) if (it.hasCurrency())
elem[jss::currency] = to_string(it.getCurrency()); elem[jss::currency] = to_string(it.getCurrency());
if ((iType & STPathElement::TypeMpt) != 0u) if (it.hasMPT())
elem[jss::mpt_issuance_id] = to_string(it.getMPTID()); elem[jss::mpt_issuance_id] = to_string(it.getMPTID());
if ((iType & STPathElement::TypeIssuer) != 0u) if (it.hasIssuer())
elem[jss::issuer] = to_string(it.getIssuerID()); elem[jss::issuer] = to_string(it.getIssuerID());
ret.append(elem); ret.append(elem);
@@ -265,34 +285,42 @@ STPathSet::add(Serializer& s) const
XRPL_ASSERT(getFName().fieldType == STI_PATHSET, "xrpl::STPathSet::add : valid field type"); XRPL_ASSERT(getFName().fieldType == STI_PATHSET, "xrpl::STPathSet::add : valid field type");
bool first = true; 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_) for (auto const& spPath : value_)
{ {
if (!first) if (!first)
s.add8(STPathElement::TypeBoundary); s.add8(toByte(STPathElement::TypeBoundary));
for (auto const& speElement : spPath) for (auto const& speElement : spPath)
{ {
int const iType = speElement.getNodeType(); s.add8(toByte(speElement.getNodeType()));
s.add8(iType); if (speElement.isType(STPathElement::TypeAccount))
if ((iType & STPathElement::TypeAccount) != 0u)
s.addBitString(speElement.getAccountID()); s.addBitString(speElement.getAccountID());
if ((iType & STPathElement::TypeMpt) != 0u) if (speElement.hasMPT())
s.addBitString(speElement.getMPTID()); s.addBitString(speElement.getMPTID());
if ((iType & STPathElement::TypeCurrency) != 0u) if (speElement.hasCurrency())
s.addBitString(speElement.getCurrency()); s.addBitString(speElement.getCurrency());
if ((iType & STPathElement::TypeIssuer) != 0u) if (speElement.hasIssuer())
s.addBitString(speElement.getIssuerID()); s.addBitString(speElement.getIssuerID());
} }
first = false; first = false;
} }
s.add8(STPathElement::TypeNone); s.add8(toByte(STPathElement::TypeNone));
} }
} // namespace xrpl } // namespace xrpl
+4 -3
View File
@@ -38,7 +38,7 @@ ApplyContext::ApplyContext(
, parentBatchId_(parentBatchId) , parentBatchId_(parentBatchId)
{ {
XRPL_ASSERT( XRPL_ASSERT(
parentBatchId.has_value() == ((flags_ & TapBatch) == TapBatch), parentBatchId.has_value() == ((flags_ & ApplyFlags::Batch) == ApplyFlags::Batch),
"Parent Batch ID should be set if batch apply flag is set"); "Parent Batch ID should be set if batch apply flag is set");
view_.emplace(&base_, flags_); view_.emplace(&base_, flags_);
} }
@@ -53,13 +53,14 @@ std::optional<TxMeta>
ApplyContext::apply(TER ter) ApplyContext::apply(TER ter)
{ {
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor // NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & TapDryRun) != 0u, journal); return view_->apply(
base_, tx, ter, parentBatchId_, (flags_ & ApplyFlags::DryRun) != ApplyFlags::None, journal);
} }
void void
ApplyContext::addOrderBook(Book const& book) ApplyContext::addOrderBook(Book const& book)
{ {
if ((flags_ & TapDryRun) == TapNone) if ((flags_ & ApplyFlags::DryRun) == ApplyFlags::None)
registry.get().getOrderBookDB().addOrderBook(book); registry.get().getOrderBookDB().addOrderBook(book);
} }
+27 -26
View File
@@ -11,7 +11,6 @@
#include <xrpl/json/to_string.h> // IWYU pragma: keep #include <xrpl/json/to_string.h> // IWYU pragma: keep
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DelegateHelpers.h> #include <xrpl/ledger/helpers/DelegateHelpers.h>
@@ -135,7 +134,7 @@ preflightCheckSigningKey(STObject const& sigObject, beast::Journal j)
std::optional<NotTEC> std::optional<NotTEC>
preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j) preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j)
{ {
if ((flags & TapDryRun) != 0u) // simulation if ((flags & ApplyFlags::DryRun) != ApplyFlags::None) // simulation
{ {
std::optional<Slice> const signature = sigObject[~sfTxnSignature]; std::optional<Slice> const signature = sigObject[~sfTxnSignature];
if (signature && !signature->empty()) if (signature && !signature->empty())
@@ -511,7 +510,8 @@ Transactor::minimumFee(
Fees const& fees, Fees const& fees,
ApplyFlags flags) ApplyFlags flags)
{ {
return scaleFeeLoad(baseFee, registry.getFeeTrack(), fees, (flags & TapUnlimited) != 0u); return scaleFeeLoad(
baseFee, registry.getFeeTrack(), fees, (flags & ApplyFlags::Unlimited) != ApplyFlags::None);
} }
TER TER
@@ -522,7 +522,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
auto const feePaid = ctx.tx[sfFee].xrp(); auto const feePaid = ctx.tx[sfFee].xrp();
if ((ctx.flags & TapBatch) != 0u) if ((ctx.flags & ApplyFlags::Batch) != ApplyFlags::None)
{ {
if (feePaid == beast::kZero) if (feePaid == beast::kZero)
return tesSUCCESS; return tesSUCCESS;
@@ -587,7 +587,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
if (feePayer.type == FeePayerType::SponsorCoSigned) if (feePayer.type == FeePayerType::SponsorCoSigned)
{ {
auto const sponsorReserve = accountReserve(ctx.view, feePayer.id, ctx.j); auto const sponsorReserve = accountReserve(ctx.view, payerSle, ctx.j);
maxSpendable = payerSle->getFieldAmount(sfBalance).xrp() - sponsorReserve; maxSpendable = payerSle->getFieldAmount(sfBalance).xrp() - sponsorReserve;
} }
else else
@@ -653,7 +653,7 @@ Transactor::payFee()
XRPAmount spendable = balance; XRPAmount spendable = balance;
if (feePayer.type == FeePayerType::SponsorCoSigned) if (feePayer.type == FeePayerType::SponsorCoSigned)
{ {
auto const sponsorReserve = accountReserve(view(), feePayer.id, j_); auto const sponsorReserve = accountReserve(view(), sle, j_);
// max(balance - reserve, 0) with overflow handling // max(balance - reserve, 0) with overflow handling
spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero; spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero;
} }
@@ -701,7 +701,7 @@ Transactor::checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j
{ {
auto const id = tx.getAccountID(sfAccount); auto const id = tx.getAccountID(sfAccount);
auto const sle = AccountRootEntryR(id, view); auto const sle = view.read(keylet::account(id));
if (!sle) if (!sle)
{ {
@@ -766,7 +766,7 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
{ {
auto const id = ctx.tx.getAccountID(sfAccount); auto const id = ctx.tx.getAccountID(sfAccount);
auto const sle = AccountRootEntryR(id, ctx.view); auto const sle = ctx.view.read(keylet::account(id));
if (!sle) if (!sle)
{ {
@@ -836,7 +836,7 @@ Transactor::ticketDelete(
// Update the account root's TicketCount. If the ticket count drops to // Update the account root's TicketCount. If the ticket count drops to
// zero remove the (optional) field. // zero remove the (optional) field.
auto sleAccount = AccountRootEntryW(account, view); auto sleAccount = view.peek(keylet::account(account));
if (!sleAccount) if (!sleAccount)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
@@ -886,19 +886,19 @@ Transactor::apply()
// If the transactor requires a valid account and the transaction doesn't // If the transactor requires a valid account and the transaction doesn't
// list one, preflight will have already a flagged a failure. // list one, preflight will have already a flagged a failure.
auto sle = AccountRootEntryW(accountID_, view()); auto const sle = view().peek(keylet::account(accountID_));
// sle must exist except for transactions // sle must exist except for transactions
// that allow zero account. // that allow zero account.
XRPL_ASSERT( XRPL_ASSERT(
sle.exists() || accountID_ == beast::kZero, sle != nullptr || accountID_ == beast::kZero,
"xrpl::Transactor::apply : non-null SLE or zero account"); "xrpl::Transactor::apply : non-null SLE or zero account");
if (sle) if (sle)
{ {
preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp(); preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp();
TER result = consumeSeqProxy(sle.mutableRawSle()); TER result = consumeSeqProxy(sle);
if (!isTesSuccess(result)) if (!isTesSuccess(result))
return result; return result;
@@ -909,7 +909,7 @@ Transactor::apply()
if (sle->isFieldPresent(sfAccountTxnID)) if (sle->isFieldPresent(sfAccountTxnID))
sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID()); sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID());
sle.update(); view().update(sle);
} }
return doApply(); return doApply();
@@ -926,7 +926,7 @@ Transactor::checkSign(
bool permitUncreatedAccount) bool permitUncreatedAccount)
{ {
{ {
auto const sle = AccountRootEntryR(idAccount, view); auto const sle = view.read(keylet::account(idAccount));
if ((view.rules().enabled(featureLendingProtocol) || if ((view.rules().enabled(featureLendingProtocol) ||
view.rules().enabled(featureBatchV1_1) || view.rules().enabled(fixCleanup3_3_0)) && view.rules().enabled(featureBatchV1_1) || view.rules().enabled(fixCleanup3_3_0)) &&
@@ -952,7 +952,8 @@ Transactor::checkSign(
return tesSUCCESS; return tesSUCCESS;
} }
if (((flags & TapDryRun) != 0u) && pkSigner.empty() && !sigObject.isFieldPresent(sfSigners)) if (((flags & ApplyFlags::DryRun) != ApplyFlags::None) && pkSigner.empty() &&
!sigObject.isFieldPresent(sfSigners))
{ {
// simulate: skip signature validation when neither SigningPubKey nor // simulate: skip signature validation when neither SigningPubKey nor
// Signers are provided // Signers are provided
@@ -992,7 +993,7 @@ Transactor::checkSign(
// Look up the account. // Look up the account.
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner))); auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
auto const sleAccount = AccountRootEntryR(idAccount, view); auto const sleAccount = view.read(keylet::account(idAccount));
if (!sleAccount) if (!sleAccount)
{ {
// An account that does not exist yet can only be authorized by its own // An account that does not exist yet can only be authorized by its own
@@ -1005,7 +1006,7 @@ Transactor::checkSign(
return tesSUCCESS; return tesSUCCESS;
} }
return checkSingleSign(view, idSigner, idAccount, sleAccount.rawSle(), j); return checkSingleSign(view, idSigner, idAccount, sleAccount, j);
} }
NotTEC NotTEC
@@ -1123,7 +1124,7 @@ Transactor::checkMultiSign(
} }
XRPL_ASSERT( XRPL_ASSERT(
(flags & TapDryRun) || !spk.empty(), (flags & ApplyFlags::DryRun) != ApplyFlags::None || !spk.empty(),
"xrpl::Transactor::checkMultiSign : non-empty signer or " "xrpl::Transactor::checkMultiSign : non-empty signer or "
"simulation"); "simulation");
AccountID const signingAcctIDFromPubKey = AccountID const signingAcctIDFromPubKey =
@@ -1154,7 +1155,7 @@ Transactor::checkMultiSign(
// In any of these cases we need to know whether the account is in // In any of these cases we need to know whether the account is in
// the ledger. Determine that now. // the ledger. Determine that now.
auto const sleTxSignerRoot = AccountRootEntryR(txSignerAcctID, view); auto const sleTxSignerRoot = view.read(keylet::account(txSignerAcctID));
if (signingAcctIDFromPubKey == txSignerAcctID) if (signingAcctIDFromPubKey == txSignerAcctID)
{ {
@@ -1297,7 +1298,7 @@ Transactor::reset(XRPAmount fee)
{ {
ctx_.discard(); ctx_.discard();
auto txnAcct = AccountRootEntryW(ctx_.tx.getAccountID(sfAccount), view()); auto const txnAcct = view().peek(keylet::account(ctx_.tx.getAccountID(sfAccount)));
// The account should never be missing from the ledger. But if it // The account should never be missing from the ledger. But if it
// is missing then we can't very well charge it a fee, can we? // is missing then we can't very well charge it a fee, can we?
@@ -1332,7 +1333,7 @@ Transactor::reset(XRPAmount fee)
XRPAmount spendable = balance; XRPAmount spendable = balance;
if (feePayer.type == FeePayerType::SponsorCoSigned) if (feePayer.type == FeePayerType::SponsorCoSigned)
{ {
auto const sponsorReserve = accountReserve(view(), feePayer.id, j_); auto const sponsorReserve = accountReserve(view(), payerSle, j_);
// max(balance - reserve, 0) with overflow handling // max(balance - reserve, 0) with overflow handling
spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero; spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero;
} }
@@ -1365,13 +1366,13 @@ Transactor::reset(XRPAmount fee)
payerSle->setFieldAmount(feePayer.balanceField, feeAmountAfter); payerSle->setFieldAmount(feePayer.balanceField, feeAmountAfter);
} }
TER const ter{consumeSeqProxy(txnAcct.mutableRawSle())}; TER const ter{consumeSeqProxy(txnAcct)};
XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS"); XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS");
if (isTesSuccess(ter)) if (isTesSuccess(ter))
{ {
txnAcct.update(); view().update(txnAcct);
if (payerSle != txnAcct.mutableRawSle()) if (payerSle != txnAcct)
view().update(payerSle); view().update(payerSle);
} }
@@ -1605,7 +1606,7 @@ Transactor::operator()()
if (ctx_.size() > kOversizeMetaDataCap) if (ctx_.size() > kOversizeMetaDataCap)
result = tecOVERSIZE; result = tecOVERSIZE;
if (isTecClaim(result) && ((view().flags() & TapFailHard) != 0u)) if (isTecClaim(result) && ((view().flags() & ApplyFlags::FailHard) != ApplyFlags::None))
{ {
// If the TapFailHard flag is set, a tec result // If the TapFailHard flag is set, a tec result
// must not do anything // must not do anything
@@ -1685,7 +1686,7 @@ Transactor::operator()()
// Once we call apply, we will no longer be able to look at view() // Once we call apply, we will no longer be able to look at view()
metadata = ctx_.apply(result); metadata = ctx_.apply(result);
if ((ctx_.flags() & TapDryRun) != 0u) if ((ctx_.flags() & ApplyFlags::DryRun) != ApplyFlags::None)
return logger(result, false, std::move(metadata)); return logger(result, false, std::move(metadata));
return logger(result, canApply, 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) { auto applyOneTransaction = [&registry, &j, &parentBatchId, &batchView](STTx const& tx) {
OpenView perTxBatchView(kBatchView, batchView); OpenView perTxBatchView(kBatchView, batchView);
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, TapBatch, j); auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, ApplyFlags::Batch, j);
XRPL_ASSERT( XRPL_ASSERT(
ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)), ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)),
"Inner transaction should not be applied"); "Inner transaction should not be applied");
@@ -274,7 +274,7 @@ applyTransaction(
{ {
// Returns false if the transaction has need not be retried. // Returns false if the transaction has need not be retried.
if (retryAssured) if (retryAssured)
flags = flags | TapRetry; flags = flags | ApplyFlags::Retry;
JLOG(j.debug()) << "TXN " << txn.getTransactionID() << (retryAssured ? "/retry" : "/final"); JLOG(j.debug()) << "TXN " << txn.getTransactionID() << (retryAssured ? "/retry" : "/final");
+4 -2
View File
@@ -346,7 +346,8 @@ preflight(
// builds; re-check the same invariant here so a release build can't // builds; re-check the same invariant here so a release build can't
// silently skip a proposed transaction's signature-presence checks // silently skip a proposed transaction's signature-presence checks
// outside of a dry run. // outside of a dry run.
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone) if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun."; JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
@@ -377,7 +378,8 @@ preflight(
PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j); PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
// See the comment in the other preflight() overload above. // See the comment in the other preflight() overload above.
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone) if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun."; JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
+4 -13
View File
@@ -735,15 +735,6 @@ NoDeepFreezeTrustLinesWithoutFreeze::finalize(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool
isPseudoAccountSle(SLE::ConstRef sleAcct)
{
auto const& fields = getPseudoAccountFields();
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
std::ranges::any_of(
fields, [&sleAcct](SField const* sf) { return sleAcct->isFieldPresent(*sf); });
}
void void
ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after) ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after)
{ {
@@ -751,7 +742,7 @@ ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after)
{ {
accountsCreated_++; accountsCreated_++;
accountSeq_ = (*after)[sfSequence]; accountSeq_ = (*after)[sfSequence];
pseudoAccount_ = isPseudoAccountSle(after); pseudoAccount_ = isPseudoAccount(after);
flags_ = after->getFlags(); flags_ = after->getFlags();
} }
} }
@@ -1029,9 +1020,9 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstR
if (after && after->getType() == ltACCOUNT_ROOT) if (after && after->getType() == ltACCOUNT_ROOT)
{ {
bool const isPseudo = [&]() { bool const isPseudo = [&]() {
// isPseudoAccountSle checks that any of the pseudo-account fields // isPseudoAccount checks that any of the pseudo-account fields are
// are set. // set.
if (isPseudoAccountSle(after)) if (isPseudoAccount(after))
return true; return true;
// Not all pseudo-accounts have a zero sequence, but all accounts // Not all pseudo-accounts have a zero sequence, but all accounts
// with a zero sequence had better be pseudo-accounts. // with a zero sequence had better be pseudo-accounts.
+1 -2
View File
@@ -23,7 +23,6 @@
#include <xrpl/protocol/TxFormats.h> #include <xrpl/protocol/TxFormats.h>
#include <xrpl/protocol/UintTypes.h> #include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h> #include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/invariants/InvariantCheck.h>
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h> #include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
#include <algorithm> #include <algorithm>
@@ -894,7 +893,7 @@ ValidMPTTransfer::visitEntry(
// no trace of its pseudo-account status in the post-transaction view // no trace of its pseudo-account status in the post-transaction view
// isAuthorized() sees at finalize() time. // isAuthorized() sees at finalize() time.
if (before && before->getType() == ltACCOUNT_ROOT) if (before && before->getType() == ltACCOUNT_ROOT)
pseudoAccountsBefore_[before->at(sfAccount)] = isPseudoAccountSle(before); pseudoAccountsBefore_[before->at(sfAccount)] = isPseudoAccount(before);
} }
bool bool
+1 -1
View File
@@ -740,7 +740,7 @@ ValidVault::finalize(
return false; return false;
} }
if (!isPseudoAccount(AccountRootEntryR(sleSharesIssuer, view))) if (!isPseudoAccount(sleSharesIssuer))
{ {
JLOG(j.fatal()) // JLOG(j.fatal()) //
<< "Invariant failed: shares issuer must be a " << "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 BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
{ {
// This can be simplified (and sped up) if directories are never empty. // This can be simplified (and sped up) if directories are never empty.
Sandbox sb(&view, TapNone); Sandbox sb(&view, ApplyFlags::None);
BookTip bt(sb, book_); BookTip bt(sb, book_);
auto const lobQuality = bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt; auto const lobQuality = bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
// Multi-path offer generates an offer with the quality // Multi-path offer generates an offer with the quality
+2 -3
View File
@@ -5,7 +5,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -837,7 +836,7 @@ DirectStepI<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH; return temBAD_PATH;
} }
auto const sleSrc = AccountRootEntryR(src_, ctx.view); auto const sleSrc = ctx.view.read(keylet::account(src_));
if (!sleSrc) if (!sleSrc)
{ {
JLOG(j_.warn()) << "DirectStepI: can't receive IOUs from non-existent issuer: " << src_; JLOG(j_.warn()) << "DirectStepI: can't receive IOUs from non-existent issuer: " << src_;
@@ -903,7 +902,7 @@ DirectStepI<TDerived>::check(StrandContext const& ctx) const
} }
} }
return static_cast<TDerived const*>(this)->check(ctx, sleSrc.rawSle()); return static_cast<TDerived const*>(this)->check(ctx, sleSrc);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
+2 -3
View File
@@ -6,7 +6,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -866,7 +865,7 @@ MPTEndpointStep<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH; return temBAD_PATH;
} }
auto const sleSrc = AccountRootEntryR(src_, ctx.view); auto const sleSrc = ctx.view.read(keylet::account(src_));
if (!sleSrc) if (!sleSrc)
{ {
JLOG(j_.warn()) << "MPTEndpointStep: can't receive MPT from non-existent issuer: " << src_; JLOG(j_.warn()) << "MPTEndpointStep: can't receive MPT from non-existent issuer: " << src_;
@@ -929,7 +928,7 @@ MPTEndpointStep<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH; return temBAD_PATH;
} }
return static_cast<TDerived const*>(this)->check(ctx, sleSrc.rawSle()); return static_cast<TDerived const*>(this)->check(ctx, sleSrc);
} }
template <class TDerived> template <class TDerived>
+13 -27
View File
@@ -57,10 +57,8 @@ checkNear(IOUAmount const& expected, IOUAmount const& actual)
static bool static bool
isXRPAccount(STPathElement const& pe) isXRPAccount(STPathElement const& pe)
{ {
if (pe.getNodeType() != STPathElement::TypeAccount) return pe.getNodeType() == STPathElement::TypeAccount && isXRP(pe.getAccountID());
return false; }
return isXRP(pe.getAccountID());
};
static std::pair<TER, std::unique_ptr<Step>> static std::pair<TER, std::unique_ptr<Step>>
toStep( toStep(
@@ -71,11 +69,8 @@ toStep(
{ {
auto& j = ctx.j; auto& j = ctx.j;
if (ctx.isFirst && e1->isAccount() && if (ctx.isFirst && e1->isAccount() && e1->hasCurrency() && e1->getPathAsset().isXRP())
((e1->getNodeType() & STPathElement::TypeCurrency) != 0u) && e1->getPathAsset().isXRP())
{
return makeXrpEndpointStep(ctx, e1->getAccountID()); return makeXrpEndpointStep(ctx, e1->getAccountID());
}
if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount()) if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
return makeXrpEndpointStep(ctx, e2->getAccountID()); return makeXrpEndpointStep(ctx, e2->getAccountID());
@@ -117,15 +112,9 @@ toStep(
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
XRPL_ASSERT( XRPL_ASSERT(e2->hasAsset() || e2->hasIssuer(), "xrpl::toStep : currency or issuer");
(e2->getNodeType() & STPathElement::TypeAsset) || PathAsset const outAsset = e2->hasAsset() ? e2->getPathAsset() : curAsset;
(e2->getNodeType() & STPathElement::TypeIssuer), auto const outIssuer = e2->hasIssuer() ? e2->getIssuerID() : curAsset.getIssuer();
"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()) if (isXRP(curAsset) && outAsset.isXRP())
{ {
@@ -197,16 +186,15 @@ toStrand(
for (std::size_t i = 0; i < path.size(); ++i) for (std::size_t i = 0; i < path.size(); ++i)
{ {
auto const& pe = path[i]; auto const& pe = path[i];
auto const t = pe.getNodeType();
if (((t & ~STPathElement::TypeAll) != 0u) || (t == 0u)) if ((pe.getNodeType() & ~STPathElement::TypeAll) != STPathElement::TypeNone || pe.isNone())
return {temBAD_PATH, Strand{}}; return {temBAD_PATH, Strand{}};
bool const hasAccount = (t & STPathElement::TypeAccount) != 0u; bool const hasAccount = pe.isType(STPathElement::TypeAccount);
bool const hasIssuer = (t & STPathElement::TypeIssuer) != 0u; bool const hasIssuer = pe.hasIssuer();
bool const hasCurrency = (t & STPathElement::TypeCurrency) != 0u; bool const hasCurrency = pe.hasCurrency();
bool const hasMPT = (t & STPathElement::TypeMpt) != 0u; bool const hasMPT = pe.hasMPT();
bool const hasAsset = (t & STPathElement::TypeAsset) != 0u; bool const hasAsset = pe.hasAsset();
if (hasAccount && (hasIssuer || hasCurrency)) if (hasAccount && (hasIssuer || hasCurrency))
return {temBAD_PATH, Strand{}}; return {temBAD_PATH, Strand{}};
@@ -250,9 +238,7 @@ toStrand(
}(); }();
// Currency or MPT // Currency or MPT
auto hasAsset = [](STPathElement const pe) { auto hasAsset = [](STPathElement const& pe) { return pe.hasAsset(); };
return pe.getNodeType() & STPathElement::TypeAsset;
};
std::vector<STPathElement> normPath; std::vector<STPathElement> normPath;
// reserve enough for the path, the implied source, destination, // reserve enough for the path, the implied source, destination,
+1 -2
View File
@@ -4,7 +4,6 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -343,7 +342,7 @@ XRPEndpointStep<TDerived>::check(StrandContext const& ctx) const
return temBAD_PATH; return temBAD_PATH;
} }
auto sleAcc = AccountRootEntryR(acc_, ctx.view); auto sleAcc = ctx.view.read(keylet::account(acc_));
if (!sleAcc) if (!sleAcc)
{ {
JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from " JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
@@ -9,7 +9,6 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
@@ -225,7 +224,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
AccountID const account{ctx.tx[sfAccount]}; AccountID const account{ctx.tx[sfAccount]};
AccountID const dst{ctx.tx[sfDestination]}; AccountID const dst{ctx.tx[sfDestination]};
auto sleDst = AccountRootEntryR(dst, ctx.view); auto sleDst = ctx.view.read(keylet::account(dst));
if (!sleDst) if (!sleDst)
return tecNO_DST; return tecNO_DST;
@@ -251,7 +250,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
} }
} }
auto sleAccount = AccountRootEntryR(account, ctx.view); auto sleAccount = ctx.view.read(keylet::account(account));
XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account"); XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account");
if (!sleAccount) if (!sleAccount)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -354,11 +353,11 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
TER TER
AccountDelete::doApply() AccountDelete::doApply()
{ {
auto src = AccountRootEntryW(accountID_, view()); auto src = view().peek(keylet::account(accountID_));
XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account"); XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account");
auto const dstID = ctx_.tx[sfDestination]; auto const dstID = ctx_.tx[sfDestination];
auto dst = AccountRootEntryW(dstID, view()); auto dst = view().peek(keylet::account(dstID));
XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account"); XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account");
if (!src || !dst) if (!src || !dst)
@@ -366,8 +365,8 @@ AccountDelete::doApply()
if (ctx_.tx.isFieldPresent(sfCredentialIDs)) if (ctx_.tx.isFieldPresent(sfCredentialIDs))
{ {
if (auto err = verifyDepositPreauth( if (auto err =
ctx_.tx, ctx_.view(), accountID_, dstID, dst.rawSle(), ctx_.journal); verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dstID, dst, ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
} }
@@ -408,7 +407,7 @@ AccountDelete::doApply()
if (src->isFieldPresent(sfSponsor)) if (src->isFieldPresent(sfSponsor))
{ {
auto const sponsorID = src->getAccountID(sfSponsor); auto const sponsorID = src->getAccountID(sfSponsor);
auto sponsorSle = AccountRootEntryW(sponsorID, view()); auto sponsorSle = view().peek(keylet::account(sponsorID));
if (!sponsorSle) if (!sponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -426,7 +425,7 @@ AccountDelete::doApply()
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
} }
sponsorSle->at(sfSponsoringAccountCount) = sponsoringAccountCount - 1; sponsorSle->at(sfSponsoringAccountCount) = sponsoringAccountCount - 1;
sponsorSle.update(); view().update(sponsorSle);
// Following line might look redundant, but without it, sfSponsor // Following line might look redundant, but without it, sfSponsor
// would end up remaining in after-ltAccountRoot during the // would end up remaining in after-ltAccountRoot during the
@@ -449,8 +448,8 @@ AccountDelete::doApply()
if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent)) if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
dst->clearFlag(lsfPasswordSpent); dst->clearFlag(lsfPasswordSpent);
dst.update(); view().update(dst);
src.erase(); view().erase(src);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -6,7 +6,6 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -171,7 +170,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
{ {
auto const id = ctx.tx[sfAccount]; auto const id = ctx.tx[sfAccount];
auto const sle = AccountRootEntryR(id, ctx.view); auto const sle = ctx.view.read(keylet::account(id));
if (!sle) if (!sle)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -188,7 +187,8 @@ AccountSet::preclaim(PreclaimContext const& ctx)
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id))) if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
{ {
JLOG(ctx.j.trace()) << "Retry: Owner directory not empty."; JLOG(ctx.j.trace()) << "Retry: Owner directory not empty.";
return ((ctx.flags & TapRetry) != 0u) ? TER{terOWNERS} : TER{tecOWNERS}; return ((ctx.flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terOWNERS}
: TER{tecOWNERS};
} }
} }
@@ -225,7 +225,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
TER TER
AccountSet::doApply() AccountSet::doApply()
{ {
auto sle = AccountRootEntryW(accountID_, view()); auto const sle = view().peek(keylet::account(accountID_));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -583,7 +583,7 @@ AccountSet::doApply()
if (uFlagsIn != uFlagsOut) if (uFlagsIn != uFlagsOut)
sle->setFieldU32(sfFlags, uFlagsOut); sle->setFieldU32(sfFlags, uFlagsOut);
sle.update(); ctx_.view().update(sle);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -3,7 +3,6 @@
#include <xrpl/basics/Slice.h> #include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/PublicKey.h> #include <xrpl/protocol/PublicKey.h>
@@ -26,7 +25,7 @@ SetRegularKey::calculateBaseFee(ReadView const& view, STTx const& tx)
{ {
if (calcAccountID(PublicKey(makeSlice(spk))) == id) if (calcAccountID(PublicKey(makeSlice(spk))) == id)
{ {
auto const sle = AccountRootEntryR(id, view); auto const sle = view.read(keylet::account(id));
if (sle && !sle->isFlag(lsfPasswordSpent)) if (sle && !sle->isFlag(lsfPasswordSpent))
{ {
@@ -54,7 +53,7 @@ SetRegularKey::preflight(PreflightContext const& ctx)
TER TER
SetRegularKey::doApply() SetRegularKey::doApply()
{ {
auto sle = AccountRootEntryW(accountID_, view()); auto const sle = view().peek(keylet::account(accountID_));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -74,7 +73,7 @@ SetRegularKey::doApply()
sle->makeFieldAbsent(sfRegularKey); sle->makeFieldAbsent(sfRegularKey);
} }
sle.update(); ctx_.view().update(sle);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -6,7 +6,6 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -214,9 +213,8 @@ removeSignersFromLedger(
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
AccountRootEntryW accountSle(accountKeylet, view);
decreaseOwnerCountForObject( decreaseOwnerCountForObject(
view, accountSle, signers, removeFromOwnerCount, registry.getJournal("View")); view, view.peek(accountKeylet), signers, removeFromOwnerCount, registry.getJournal("View"));
view.erase(signers); view.erase(signers);
@@ -311,7 +309,7 @@ SignerListSet::replaceSignerList()
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_)) ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_))
return ter; return ter;
auto sle = AccountRootEntryW(accountKeylet, view(), ctx_.journal); auto const sle = view().peek(accountKeylet);
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -321,8 +319,12 @@ SignerListSet::replaceSignerList()
// We check the reserve against the starting balance because we want to // We check the reserve against the starting balance because we want to
// allow dipping into the reserve to pay fees. This behavior is consistent // allow dipping into the reserve to pay fees. This behavior is consistent
// with TicketCreate. // with TicketCreate.
if (auto const ret = sle.checkReserve( if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = kAddedOwnerCount}); ctx_.getApplyViewContext(),
sle,
preFeeBalance_,
{.ownerCountDelta = kAddedOwnerCount},
ctx_.journal);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -331,6 +333,7 @@ SignerListSet::replaceSignerList()
view().insert(signerList); view().insert(signerList);
writeSignersToSLE(signerList, flags); writeSignersToSLE(signerList, flags);
auto viewJ = ctx_.registry.get().getJournal("View");
// Add the signer list to the account's directory. // Add the signer list to the account's directory.
auto const page = auto const page =
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_)); ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_));
@@ -345,7 +348,7 @@ SignerListSet::replaceSignerList()
// If we succeeded, the new entry counts against the // If we succeeded, the new entry counts against the
// creator's reserve. // creator's reserve.
sle.increaseOwnerCount(ctx_.getApplyViewContext(), kAddedOwnerCount); increaseOwnerCount(ctx_.getApplyViewContext(), sle, kAddedOwnerCount, viewJ);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), signerList); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), signerList);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -356,7 +359,7 @@ SignerListSet::destroySignerList()
auto const accountKeylet = keylet::account(accountID_); auto const accountKeylet = keylet::account(accountID_);
// Destroying the signer list is only allowed if either the master key // Destroying the signer list is only allowed if either the master key
// is enabled or there is a regular key. // is enabled or there is a regular key.
auto const ledgerEntry = AccountRootEntryW(accountKeylet, view()); SLE::pointer const ledgerEntry = view().peek(accountKeylet);
if (!ledgerEntry) if (!ledgerEntry)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -10,7 +10,6 @@
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/RawView.h> #include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -126,7 +125,8 @@ checkAttestationPublicKey(
AccountID const accountFromPK = calcAccountID(pk); AccountID const accountFromPK = calcAccountID(pk);
if (auto const sleAttestationSigningAccount = AccountRootEntryR(attestationSignerAccount, view)) if (auto const sleAttestationSigningAccount =
view.read(keylet::account(attestationSignerAccount)))
{ {
if (accountFromPK == attestationSignerAccount) if (accountFromPK == attestationSignerAccount)
{ {
@@ -404,7 +404,7 @@ transferHelper(
return tesSUCCESS; return tesSUCCESS;
auto const dstK = keylet::account(dst); auto const dstK = keylet::account(dst);
if (auto sleDst = AccountRootEntryR(dstK, psb)) if (auto sleDst = psb.read(dstK))
{ {
// Check dst tag and deposit auth // Check dst tag and deposit auth
@@ -430,13 +430,13 @@ transferHelper(
if (amt.native()) if (amt.native())
{ {
auto sleSrc = AccountRootEntryW(src, psb, j); auto const sleSrc = psb.peek(keylet::account(src));
XRPL_ASSERT(sleSrc, "xrpl::transferHelper : non-null source account"); XRPL_ASSERT(sleSrc, "xrpl::transferHelper : non-null source account");
if (!sleSrc) if (!sleSrc)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
{ {
auto const reserve = sleSrc.reserve(); auto const reserve = accountReserve(psb, sleSrc, j);
auto const availableBalance = [&]() -> STAmount { auto const availableBalance = [&]() -> STAmount {
STAmount curBal = (*sleSrc)[sfBalance]; STAmount curBal = (*sleSrc)[sfBalance];
@@ -455,7 +455,7 @@ transferHelper(
} }
} }
auto sleDst = AccountRootEntryW(dstK, psb); auto sleDst = psb.peek(dstK);
if (!sleDst) if (!sleDst)
{ {
if (canCreate == CanCreateDstPolicy::No) if (canCreate == CanCreateDstPolicy::No)
@@ -470,17 +470,17 @@ transferHelper(
} }
// Create the account. // Create the account.
sleDst.newSLE(); sleDst = std::make_shared<SLE>(dstK);
sleDst->setAccountID(sfAccount, dst); sleDst->setAccountID(sfAccount, dst);
sleDst->setFieldU32(sfSequence, psb.seq()); sleDst->setFieldU32(sfSequence, psb.seq());
sleDst.insert(); psb.insert(sleDst);
} }
(*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt; (*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt;
(*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt; (*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt;
sleSrc.update(); psb.update(sleSrc);
sleDst.update(); psb.update(sleDst);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -724,7 +724,7 @@ finalizeClaimHelper(
auto const cidOwner = (*sleClaimID)[sfAccount]; auto const cidOwner = (*sleClaimID)[sfAccount];
{ {
// Remove the claim id // Remove the claim id
auto sleOwner = AccountRootEntryW(cidOwner, outerSb); auto const sleOwner = outerSb.peek(keylet::account(cidOwner));
auto const page = (*sleClaimID)[sfOwnerNode]; auto const page = (*sleClaimID)[sfOwnerNode];
if (!outerSb.dirRemove(keylet::ownerDir(cidOwner), page, sleClaimID->key(), true)) if (!outerSb.dirRemove(keylet::ownerDir(cidOwner), page, sleClaimID->key(), true))
{ {
@@ -759,7 +759,7 @@ getSignersListAndQuorum(ReadView const& view, SLE const& sleBridge, beast::Journ
std::uint32_t q = std::numeric_limits<std::uint32_t>::max(); std::uint32_t q = std::numeric_limits<std::uint32_t>::max();
AccountID const thisDoor = sleBridge[sfAccount]; AccountID const thisDoor = sleBridge[sfAccount];
auto const sleDoor = [&] { return AccountRootEntryR(thisDoor, view); }(); auto const sleDoor = [&] { return view.read(keylet::account(thisDoor)); }();
if (!sleDoor) if (!sleDoor)
{ {
@@ -1036,7 +1036,7 @@ applyCreateAccountAttestations(
// Check reserve // Check reserve
auto const balance = (*sleDoor)[sfBalance]; auto const balance = (*sleDoor)[sfBalance];
auto const reserve = AccountRootEntryR(sleDoor, psb, j).reserve({.ownerCountDelta = 1}); auto const reserve = accountReserve(psb, sleDoor, j, {.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return std::unexpected(tecINSUFFICIENT_RESERVE); return std::unexpected(tecINSUFFICIENT_RESERVE);
@@ -1145,9 +1145,7 @@ applyCreateAccountAttestations(
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
// Reserve was already checked // Reserve was already checked
AccountRootEntryW doorSle(doorK, psb, j); increaseOwnerCount(psb, sleDoor, {}, 1, j);
std::optional<AccountRootEntryW> noSponsor;
doorSle.increaseOwnerCount(noSponsor, 1);
psb.insert(createdSleClaimID); psb.insert(createdSleClaimID);
psb.update(sleDoor); psb.update(sleDoor);
} }
@@ -1431,7 +1429,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
if (!isXRP(bridgeSpec.issue(chainType))) if (!isXRP(bridgeSpec.issue(chainType)))
{ {
auto const sleIssuer = AccountRootEntryR(bridgeSpec.issue(chainType).account, ctx.view); auto const sleIssuer = ctx.view.read(keylet::account(bridgeSpec.issue(chainType).account));
if (!sleIssuer) if (!sleIssuer)
return tecNO_ISSUER; return tecNO_ISSUER;
@@ -1444,12 +1442,12 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
{ {
// Check reserve // Check reserve
auto const sleAcc = AccountRootEntryR(account, ctx.view, ctx.j); auto const sleAcc = ctx.view.read(keylet::account(account));
if (!sleAcc) if (!sleAcc)
return terNO_ACCOUNT; return terNO_ACCOUNT;
auto const balance = (*sleAcc)[sfBalance]; auto const balance = (*sleAcc)[sfBalance];
auto const reserve = sleAcc.reserve({.ownerCountDelta = 1}); auto const reserve = accountReserve(ctx.view, sleAcc, ctx.j, {.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
@@ -1466,7 +1464,7 @@ XChainCreateBridge::doApply()
auto const reward = ctx_.tx[sfSignatureReward]; auto const reward = ctx_.tx[sfSignatureReward];
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
auto sleAcct = AccountRootEntryW(account, ctx_.view(), ctx_.journal); auto const sleAcct = ctx_.view().peek(keylet::account(account));
if (!sleAcct) if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1494,11 +1492,10 @@ XChainCreateBridge::doApply()
(*sleBridge)[sfOwnerNode] = *page; (*sleBridge)[sfOwnerNode] = *page;
} }
std::optional<AccountRootEntryW> noSponsor; increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal);
sleAcct.increaseOwnerCount(noSponsor, 1);
ctx_.view().insert(sleBridge); ctx_.view().insert(sleBridge);
sleAcct.update(); ctx_.view().update(sleAcct);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -1578,7 +1575,7 @@ BridgeModify::doApply()
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
bool const clearAccountCreate = ctx_.tx.isFlag(tfClearAccountCreateAmount); bool const clearAccountCreate = ctx_.tx.isFlag(tfClearAccountCreateAmount);
auto const sleAcct = AccountRootEntryW(account, ctx_.view()); auto const sleAcct = ctx_.view().peek(keylet::account(account));
if (!sleAcct) if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1636,7 +1633,7 @@ XChainClaim::preclaim(PreclaimContext const& ctx)
return tecNO_ENTRY; return tecNO_ENTRY;
} }
if (!AccountRootEntryR(ctx.tx[sfDestination], ctx.view)) if (!ctx.view.read(keylet::account(ctx.tx[sfDestination])))
{ {
return tecNO_DST; return tecNO_DST;
} }
@@ -1741,7 +1738,7 @@ XChainClaim::doApply()
// `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
// views, it's important that the sle's lifetime doesn't overlap. // views, it's important that the sle's lifetime doesn't overlap.
auto const sleAcct = AccountRootEntryW(account, psb); auto const sleAcct = psb.peek(keylet::account(account));
auto const sleBridge = peekBridge(psb, bridgeSpec); auto const sleBridge = peekBridge(psb, bridgeSpec);
auto const sleClaimID = psb.peek(claimIDKeylet); auto const sleClaimID = psb.peek(claimIDKeylet);
@@ -1922,7 +1919,7 @@ XChainCommit::doApply()
auto const amount = ctx_.tx[sfAmount]; auto const amount = ctx_.tx[sfAmount];
auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const bridgeSpec = ctx_.tx[sfXChainBridge];
auto const sleAccount = AccountRootEntryR(account, psb); auto const sleAccount = psb.read(keylet::account(account));
if (!sleAccount) if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -1993,12 +1990,12 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx)
{ {
// Check reserve // Check reserve
auto const sleAcc = AccountRootEntryR(account, ctx.view, ctx.j); auto const sleAcc = ctx.view.read(keylet::account(account));
if (!sleAcc) if (!sleAcc)
return terNO_ACCOUNT; return terNO_ACCOUNT;
auto const balance = (*sleAcc)[sfBalance]; auto const balance = (*sleAcc)[sfBalance];
auto const reserve = sleAcc.reserve({.ownerCountDelta = 1}); auto const reserve = accountReserve(ctx.view, sleAcc, ctx.j, {.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
} }
@@ -2014,7 +2011,7 @@ XChainCreateClaimID::doApply()
auto const reward = ctx_.tx[sfSignatureReward]; auto const reward = ctx_.tx[sfSignatureReward];
auto const otherChainSrc = ctx_.tx[sfOtherChainSource]; auto const otherChainSrc = ctx_.tx[sfOtherChainSource];
auto sleAcct = AccountRootEntryW(account, ctx_.view(), ctx_.journal); auto const sleAcct = ctx_.view().peek(keylet::account(account));
if (!sleAcct) if (!sleAcct)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -2056,12 +2053,11 @@ XChainCreateClaimID::doApply()
(*sleClaimID)[sfOwnerNode] = *page; (*sleClaimID)[sfOwnerNode] = *page;
} }
std::optional<AccountRootEntryW> noSponsor; increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal);
sleAcct.increaseOwnerCount(noSponsor, 1);
ctx_.view().insert(sleClaimID); ctx_.view().insert(sleClaimID);
ctx_.view().update(sleBridge); ctx_.view().update(sleBridge);
sleAcct.update(); ctx_.view().update(sleAcct);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -2199,7 +2195,7 @@ XChainCreateAccountCommit::doApply()
STAmount const reward = ctx_.tx[sfSignatureReward]; STAmount const reward = ctx_.tx[sfSignatureReward];
STXChainBridge const bridge = ctx_.tx[sfXChainBridge]; STXChainBridge const bridge = ctx_.tx[sfXChainBridge];
auto const sle = AccountRootEntryW(account, psb); auto const sle = psb.peek(keylet::account(account));
if (!sle) if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+18 -22
View File
@@ -7,7 +7,6 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -113,8 +112,8 @@ CheckCash::preclaim(PreclaimContext const& ctx)
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
{ {
auto const sleSrc = AccountRootEntryR(srcId, ctx.view); auto const sleSrc = ctx.view.read(keylet::account(srcId));
auto const sleDst = AccountRootEntryR(dstId, ctx.view); auto const sleDst = ctx.view.read(keylet::account(dstId));
if (!sleSrc || !sleDst) if (!sleSrc || !sleDst)
{ {
// If the check exists this should never occur. // If the check exists this should never occur.
@@ -202,7 +201,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
auto const sleTrustLine = auto const sleTrustLine =
ctx.view.read(keylet::trustLine(dstId, issuerId, currency)); ctx.view.read(keylet::trustLine(dstId, issuerId, currency));
auto const sleIssuer = AccountRootEntryR(issuerId, ctx.view); auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
if (!sleIssuer) if (!sleIssuer)
{ {
JLOG(ctx.j.warn()) << "Can't receive IOUs from " JLOG(ctx.j.warn()) << "Can't receive IOUs from "
@@ -254,7 +253,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
return tesSUCCESS; return tesSUCCESS;
}, },
[&](MPTIssue const& issue) -> TER { [&](MPTIssue const& issue) -> TER {
auto const sleIssuer = AccountRootEntryR(issuerId, ctx.view); auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
if (!sleIssuer) if (!sleIssuer)
{ {
JLOG(ctx.j.warn()) << "Can't receive MPTs from " JLOG(ctx.j.warn()) << "Can't receive MPTs from "
@@ -407,18 +406,23 @@ CheckCash::doApply()
optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)}; optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)};
auto applyViewContext = ApplyViewContext({.view = psb, .tx = ctx_.tx}); auto applyViewContext = ApplyViewContext({.view = psb, .tx = ctx_.tx});
auto sponsorSle = getTxReserveSponsor(applyViewContext); auto const sponsorSle = getTxReserveSponsor(applyViewContext);
if (!sponsorSle) if (!sponsorSle)
return sponsorSle.error(); // LCOV_EXCL_LINE return sponsorSle.error(); // LCOV_EXCL_LINE
// Check reserve. Return destination account SLE if enough reserve, // Check reserve. Return destination account SLE if enough reserve,
// otherwise return nullptr. // otherwise return nullptr.
auto checkDstReserve = [&]() -> SLE::pointer { auto checkDstReserve = [&]() -> SLE::pointer {
auto sleDst = AccountRootEntryW(accountID_, psb, j_); auto sleDst = psb.peek(keylet::account(accountID_));
// Can the account cover the trust line's or MPT reserve? // Can the account cover the trust line's or MPT reserve?
if (auto const ret = sleDst.checkReserve( if (auto const ret = checkReserve(
applyViewContext, preFeeBalance_, *sponsorSle, {.ownerCountDelta = 1}); applyViewContext,
sleDst,
preFeeBalance_,
*sponsorSle,
{.ownerCountDelta = 1},
j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
JLOG(j_.trace()) << "Trust line does not exist. " JLOG(j_.trace()) << "Trust line does not exist. "
@@ -426,7 +430,7 @@ CheckCash::doApply()
return nullptr; return nullptr;
} }
return sleDst.mutableRawSle(); return sleDst;
}; };
std::optional<Keylet> trustLineKey; std::optional<Keylet> trustLineKey;
@@ -482,10 +486,8 @@ CheckCash::doApply()
Issue(currency, accountID_), // limit of zero Issue(currency, accountID_), // limit of zero
0, // quality in 0, // quality in
0, // quality out 0, // quality out
sponsorSle->has_value() // sponsor *sponsorSle, // sponsor
? (*sponsorSle)->mutableRawSle() viewJ); // journal
: SLE::pointer{},
viewJ); // journal
!isTesSuccess(ter)) !isTesSuccess(ter))
{ {
return ter; return ter;
@@ -531,14 +533,8 @@ CheckCash::doApply()
if (sleDst == nullptr) if (sleDst == nullptr)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
if (auto const err = checkCreateMPT( if (auto const err =
psb, checkCreateMPT(psb, mptID, accountID_, *sponsorSle, 0, j_);
mptID,
accountID_,
sponsorSle->has_value() ? (*sponsorSle)->mutableRawSle()
: SLE::pointer{},
0,
j_);
!isTesSuccess(err)) !isTesSuccess(err))
{ {
return err; return err;
@@ -4,7 +4,6 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -80,7 +79,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
{ {
AccountID const dstId{ctx.tx[sfDestination]}; AccountID const dstId{ctx.tx[sfDestination]};
AccountID const srcId{ctx.tx[sfAccount]}; AccountID const srcId{ctx.tx[sfAccount]};
auto const sleDst = AccountRootEntryR(dstId, ctx.view); auto const sleDst = ctx.view.read(keylet::account(dstId));
if (!sleDst) if (!sleDst)
{ {
JLOG(ctx.j.warn()) << "Destination account does not exist."; JLOG(ctx.j.warn()) << "Destination account does not exist.";
@@ -188,15 +187,15 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
TER TER
CheckCreate::doApply() CheckCreate::doApply()
{ {
auto sle = AccountRootEntryW(accountID_, view(), ctx_.journal); auto const sle = view().peek(keylet::account(accountID_));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// A check counts against the reserve of the issuing account, but we // A check counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the // check the starting balance because we want to allow dipping into the
// reserve to pay fees. // reserve to pay fees.
if (auto const ret = if (auto const ret = checkReserve(
sle.checkReserve(ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1}); ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, ctx_.journal);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
// Note that we use the value from the sequence or ticket as the // Note that we use the value from the sequence or ticket as the
@@ -221,6 +220,7 @@ CheckCreate::doApply()
view().insert(sleCheck); view().insert(sleCheck);
auto viewJ = ctx_.registry.get().getJournal("View");
// If it's not a self-send (and it shouldn't be), add Check to the // If it's not a self-send (and it shouldn't be), add Check to the
// destination's owner directory. // destination's owner directory.
if (dstAccountId != accountID_) if (dstAccountId != accountID_)
@@ -251,7 +251,7 @@ CheckCreate::doApply()
} }
// If we succeeded, the new entry counts against the creator's reserve. // If we succeeded, the new entry counts against the creator's reserve.
sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1); increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, viewJ);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -2,7 +2,6 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -88,8 +87,8 @@ CredentialAccept::doApply()
AccountID const issuer{ctx_.tx[sfIssuer]}; AccountID const issuer{ctx_.tx[sfIssuer]};
// Both exist as credential object exist itself (checked in preclaim) // Both exist as credential object exist itself (checked in preclaim)
auto sleSubject = AccountRootEntryW(accountID_, view(), j_); auto const sleSubject = view().peek(keylet::account(accountID_));
auto sleIssuer = AccountRootEntryW(issuer, view()); auto const sleIssuer = view().peek(keylet::account(issuer));
if (!sleSubject || !sleIssuer) if (!sleSubject || !sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -98,8 +97,13 @@ CredentialAccept::doApply()
if (!txSponsorSle) if (!txSponsorSle)
return txSponsorSle.error(); // LCOV_EXCL_LINE return txSponsorSle.error(); // LCOV_EXCL_LINE
if (auto const ret = sleSubject.checkReserve( if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, *txSponsorSle, {.ownerCountDelta = 1}); ctx_.getApplyViewContext(),
sleSubject,
preFeeBalance_,
*txSponsorSle,
{.ownerCountDelta = 1},
j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -126,7 +130,7 @@ CredentialAccept::doApply()
removeSponsorFromLedgerEntry(sleCred); removeSponsorFromLedgerEntry(sleCred);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
sleSubject.increaseOwnerCount(ctx_.getApplyViewContext(), 1); increaseOwnerCount(ctx_.getApplyViewContext(), sleSubject, 1, j_);
view().update(sleCred); view().update(sleCred);
return tesSUCCESS; return tesSUCCESS;
@@ -4,7 +4,6 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> // IWYU pragma: keep #include <xrpl/ledger/helpers/CredentialHelpers.h> // IWYU pragma: keep
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -85,7 +84,7 @@ CredentialCreate::preclaim(PreclaimContext const& ctx)
auto const credType(ctx.tx[sfCredentialType]); auto const credType(ctx.tx[sfCredentialType]);
auto const subject = ctx.tx[sfSubject]; auto const subject = ctx.tx[sfSubject];
auto const subjectSle = AccountRootEntryR(subject, ctx.view); auto const subjectSle = ctx.view.read(keylet::account(subject));
if (!subjectSle) if (!subjectSle)
{ {
@@ -135,12 +134,12 @@ CredentialCreate::doApply()
sleCred->setFieldU32(sfExpiration, *optExp); sleCred->setFieldU32(sfExpiration, *optExp);
} }
auto sleIssuer = AccountRootEntryW(accountID_, view(), j_); auto const sleIssuer = view().peek(keylet::account(accountID_));
if (!sleIssuer) if (!sleIssuer)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ret = sleIssuer.checkReserve( if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1}); ctx_.getApplyViewContext(), sleIssuer, preFeeBalance_, {.ownerCountDelta = 1}, j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -160,7 +159,7 @@ CredentialCreate::doApply()
return tecDIR_FULL; return tecDIR_FULL;
sleCred->setFieldU64(sfIssuerNode, *page); sleCred->setFieldU64(sfIssuerNode, *page);
sleIssuer.increaseOwnerCount(ctx_.getApplyViewContext(), 1); increaseOwnerCount(ctx_.getApplyViewContext(), sleIssuer, 1, j_);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
} }
@@ -3,7 +3,6 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -53,7 +52,7 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount]))) if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount])))
return terNO_ACCOUNT; // LCOV_EXCL_LINE return terNO_ACCOUNT; // LCOV_EXCL_LINE
auto const sleAuthorize = AccountRootEntryR(ctx.tx[sfAuthorize], ctx.view); auto const sleAuthorize = ctx.view.read(keylet::account(ctx.tx[sfAuthorize]));
if (!sleAuthorize) if (!sleAuthorize)
return tecNO_TARGET; return tecNO_TARGET;
@@ -73,7 +72,7 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
TER TER
DelegateSet::doApply() DelegateSet::doApply()
{ {
auto sleOwner = AccountRootEntryW(accountID_, ctx_.view(), ctx_.journal); auto const sleOwner = ctx_.view().peek(keylet::account(accountID_));
if (!sleOwner) if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -99,8 +98,12 @@ DelegateSet::doApply()
if (permissions.empty()) if (permissions.empty())
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
if (auto const ret = sleOwner.checkReserve( if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1}); ctx_.getApplyViewContext(),
sleOwner,
preFeeBalance_,
{.ownerCountDelta = 1},
ctx_.journal);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -130,7 +133,7 @@ DelegateSet::doApply()
(*sle)[sfDestinationNode] = *destPage; (*sle)[sfDestinationNode] = *destPage;
ctx_.view().insert(sle); ctx_.view().insert(sle);
sleOwner.increaseOwnerCount(ctx_.getApplyViewContext(), 1); increaseOwnerCount(ctx_.getApplyViewContext(), sleOwner, 1, ctx_.journal);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sle); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sle);
return tesSUCCESS; return tesSUCCESS;
@@ -167,7 +170,7 @@ DelegateSet::deleteDelegate(ApplyView& view, SLE::Ref sle, beast::Journal j)
} }
// Only the delegating account's owner count was incremented on creation // Only the delegating account's owner count was incremented on creation
auto sleOwner = AccountRootEntryW(delegator, view); auto const sleOwner = view.peek(keylet::account(delegator));
if (!sleOwner) if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+2 -3
View File
@@ -5,7 +5,6 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h> #include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -120,7 +119,7 @@ AMMBid::preclaim(PreclaimContext const& ctx)
{ {
for (auto const& account : ctx.tx.getFieldArray(sfAuthAccounts)) for (auto const& account : ctx.tx.getFieldArray(sfAuthAccounts))
{ {
if (!AccountRootEntryR(account[sfAccount], ctx.view)) if (!ctx.view.read(keylet::account(account[sfAccount])))
{ {
JLOG(ctx.j.debug()) << "AMM Bid: Invalid Account."; JLOG(ctx.j.debug()) << "AMM Bid: Invalid Account.";
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -209,7 +208,7 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa
auto validOwner = [&](AccountID const& account) { auto validOwner = [&](AccountID const& account) {
// Valid range is 0-19 but the tailing slot pays MinSlotPrice // Valid range is 0-19 but the tailing slot pays MinSlotPrice
// and doesn't refund so the check is < instead of <= to optimize. // and doesn't refund so the check is < instead of <= to optimize.
return timeSlot && *timeSlot < kTailingSlot && AccountRootEntryR(account, sb); return timeSlot && *timeSlot < kTailingSlot && sb.read(keylet::account(account));
}; };
auto updateSlot = [&](std::uint32_t fee, Number const& minPrice, Number const& burn) -> TER { auto updateSlot = [&](std::uint32_t fee, Number const& minPrice, Number const& burn) -> TER {
@@ -5,7 +5,6 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h> #include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -103,11 +102,11 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
{ {
auto const asset = ctx.tx[sfAsset]; auto const asset = ctx.tx[sfAsset];
auto const asset2 = ctx.tx[sfAsset2]; auto const asset2 = ctx.tx[sfAsset2];
auto const sleIssuer = AccountRootEntryR(ctx.tx[sfAccount], ctx.view); auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
if (!sleIssuer) if (!sleIssuer)
return terNO_ACCOUNT; // LCOV_EXCL_LINE return terNO_ACCOUNT; // LCOV_EXCL_LINE
if (!AccountRootEntryR(ctx.tx[sfHolder], ctx.view)) if (!ctx.view.read(keylet::account(ctx.tx[sfHolder])))
return terNO_ACCOUNT; return terNO_ACCOUNT;
auto const ammSle = ctx.view.read(keylet::amm(asset, asset2)); auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
@@ -179,7 +178,7 @@ AMMClawback::applyGuts(Sandbox& sb)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const ammAccount = (*ammSle)[sfAccount]; auto const ammAccount = (*ammSle)[sfAccount];
auto const accountSle = AccountRootEntryR(ammAccount, sb); auto const accountSle = sb.read(keylet::account(ammAccount));
if (!accountSle) if (!accountSle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+3 -4
View File
@@ -5,7 +5,6 @@
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h> #include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -134,7 +133,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
if (asset.holds<MPTIssue>() || isXRP(asset)) if (asset.holds<MPTIssue>() || isXRP(asset))
return false; return false;
if (auto const issuerAccount = AccountRootEntryR(asset.getIssuer(), view)) if (auto const issuerAccount = view.read(keylet::account(asset.getIssuer())))
return !issuerAccount->isFlag(lsfDefaultRipple); return !issuerAccount->isFlag(lsfDefaultRipple);
return false; return false;
@@ -174,7 +173,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
} }
auto isLPToken = [&](STAmount const& amount) -> bool { auto isLPToken = [&](STAmount const& amount) -> bool {
if (auto const sle = AccountRootEntryR(amount.asset().getIssuer(), ctx.view)) if (auto const sle = ctx.view.read(keylet::account(amount.asset().getIssuer())))
return sle->isFieldPresent(sfAMMID); return sle->isFieldPresent(sfAMMID);
return false; return false;
}; };
@@ -238,7 +237,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
[&](Issue const& issue) -> TER { [&](Issue const& issue) -> TER {
if (isXRP(issue)) if (isXRP(issue))
return tesSUCCESS; return tesSUCCESS;
auto const sle = AccountRootEntryR(issue.account, ctx.view); auto const sle = ctx.view.read(keylet::account(issue.account));
if (!sle) if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
if (sle->isFlag(lsfAllowTrustLineClawback)) if (sle->isFlag(lsfAllowTrustLineClawback))
@@ -6,8 +6,8 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h> #include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h> #include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -344,7 +344,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
if (!ammSle) if (!ammSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const ammAccountID = (*ammSle)[sfAccount]; auto const ammAccountID = (*ammSle)[sfAccount];
auto const accountSle = AccountRootEntryR(ammAccountID, sb); auto const accountSle = sb.read(keylet::account(ammAccountID));
if (!accountSle) if (!accountSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal); auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
@@ -691,15 +691,16 @@ AMMWithdraw::withdraw(
reserveHandling == ReserveHandling::IgnoreReserve) reserveHandling == ReserveHandling::IgnoreReserve)
return tesSUCCESS; return tesSUCCESS;
auto sleAccount = AccountRootEntryW(account, view, journal); auto sleAccount = view.peek(keylet::account(account));
if (!sleAccount) if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const balance = (*sleAccount)[sfBalance]->xrp(); auto const balance = (*sleAccount)[sfBalance]->xrp();
// See also TrustSet::doApply() and MPTokenAuthorize::authorize() // See also TrustSet::doApply() and MPTokenAuthorize::authorize()
XRPAmount const reserve( XRPAmount const reserve(
(sleAccount.ownerCount() < 2) ? XRPAmount(beast::kZero) (ownerCount(sleAccount, journal) < 2)
: sleAccount.reserve({.ownerCountDelta = 1})); ? XRPAmount(beast::kZero)
: accountReserve(view, sleAccount, journal, {.ownerCountDelta = 1}));
auto const balanceAdj = std::max(priorBalance, balance); auto const balanceAdj = std::max(priorBalance, balance);
if (balanceAdj < reserve) if (balanceAdj < reserve)
@@ -1,8 +1,6 @@
#include <xrpl/tx/transactors/dex/OfferCancel.h> #include <xrpl/tx/transactors/dex/OfferCancel.h>
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/OfferHelpers.h> #include <xrpl/ledger/helpers/OfferHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
@@ -35,7 +33,7 @@ OfferCancel::preclaim(PreclaimContext const& ctx)
auto const id = ctx.tx[sfAccount]; auto const id = ctx.tx[sfAccount];
auto const offerSequence = ctx.tx[sfOfferSequence]; auto const offerSequence = ctx.tx[sfOfferSequence];
auto const sle = AccountRootEntryR(id, ctx.view); auto const sle = ctx.view.read(keylet::account(id));
if (!sle) if (!sle)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -56,7 +54,7 @@ OfferCancel::doApply()
{ {
auto const offerSequence = ctx_.tx[sfOfferSequence]; auto const offerSequence = ctx_.tx[sfOfferSequence];
auto const sle = AccountRootEntryW(accountID_, view()); auto const sle = view().read(keylet::account(accountID_));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
+16 -13
View File
@@ -9,7 +9,7 @@
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/Sandbox.h> #include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -181,7 +181,7 @@ OfferCreate::preclaim(PreclaimContext const& ctx)
auto const cancelSequence = ctx.tx[~sfOfferSequence]; auto const cancelSequence = ctx.tx[~sfOfferSequence];
auto const sleCreator = AccountRootEntryR(id, ctx.view); auto const sleCreator = ctx.view.read(keylet::account(id));
if (!sleCreator) if (!sleCreator)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -288,14 +288,15 @@ OfferCreate::checkAcceptAsset(
// Only valid for custom currencies // Only valid for custom currencies
XRPL_ASSERT(!isXRP(asset), "xrpl::OfferCreate::checkAcceptAsset : input is not XRP"); XRPL_ASSERT(!isXRP(asset), "xrpl::OfferCreate::checkAcceptAsset : input is not XRP");
auto const issuerAccount = AccountRootEntryR(asset.getIssuer(), view); auto const issuerAccount = view.read(keylet::account(asset.getIssuer()));
if (!issuerAccount) if (!issuerAccount)
{ {
JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: " JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: "
<< to_string(asset.getIssuer()); << to_string(asset.getIssuer());
return ((flags & TapRetry) != 0u) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER}; return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_ACCOUNT}
: TER{tecNO_ISSUER};
} }
// An account cannot create a trustline to itself, so no line can exist // An account cannot create a trustline to itself, so no line can exist
@@ -318,7 +319,8 @@ OfferCreate::checkAcceptAsset(
{ {
JLOG(j.debug()) << "delay: can't receive IOUs from issuer with " JLOG(j.debug()) << "delay: can't receive IOUs from issuer with "
"DisallowIncomingTrustline set"; "DisallowIncomingTrustline set";
return ((flags & TapRetry) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE}; return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_LINE}
: TER{tecNO_LINE};
} }
} }
@@ -326,7 +328,8 @@ OfferCreate::checkAcceptAsset(
{ {
if (!trustLine) if (!trustLine)
{ {
return ((flags & TapRetry) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE}; return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_LINE}
: TER{tecNO_LINE};
} }
// Entries have a canonical representation, determined by a // Entries have a canonical representation, determined by a
@@ -342,7 +345,8 @@ OfferCreate::checkAcceptAsset(
JLOG(j.debug()) << "delay: can't receive IOUs from " JLOG(j.debug()) << "delay: can't receive IOUs from "
"issuer without auth."; "issuer without auth.";
return ((flags & TapRetry) != 0u) ? TER{terNO_AUTH} : TER{tecNO_AUTH}; return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_AUTH}
: TER{tecNO_AUTH};
} }
} }
@@ -707,14 +711,14 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
// Not XRP or MPT // Not XRP or MPT
if (!saTakerPays.integral()) if (!saTakerPays.integral())
{ {
auto const sle = AccountRootEntryR(uPaysIssuerID, sb); auto const sle = sb.read(keylet::account(uPaysIssuerID));
if (sle && sle->isFieldPresent(sfTickSize)) if (sle && sle->isFieldPresent(sfTickSize))
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]); uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
} }
// Not XRP or MPT // Not XRP or MPT
if (!saTakerGets.integral()) if (!saTakerGets.integral())
{ {
auto const sle = AccountRootEntryR(uGetsIssuerID, sb); auto const sle = sb.read(keylet::account(uGetsIssuerID));
if (sle && sle->isFieldPresent(sfTickSize)) if (sle && sle->isFieldPresent(sfTickSize))
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]); uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
} }
@@ -884,12 +888,12 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
return {tesSUCCESS, true}; return {tesSUCCESS, true};
} }
auto sleCreator = AccountRootEntryW(accountID_, sb, viewJ); auto const sleCreator = sb.peek(keylet::account(accountID_));
if (!sleCreator) if (!sleCreator)
return {tefINTERNAL, false}; return {tefINTERNAL, false};
{ {
XRPAmount const reserve = sleCreator.reserve({.ownerCountDelta = 1}); XRPAmount const reserve = accountReserve(sb, sleCreator, viewJ, {.ownerCountDelta = 1});
if (preFeeBalance_ < reserve) if (preFeeBalance_ < reserve)
{ {
// If we are here, the signing account had an insufficient reserve // If we are here, the signing account had an insufficient reserve
@@ -923,8 +927,7 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
} }
// Update owner count. // Update owner count.
std::optional<AccountRootEntryW> noSponsor; increaseOwnerCount(sb, sleCreator, {}, 1, viewJ);
sleCreator.increaseOwnerCount(noSponsor, 1);
JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.asset()) << " : " JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.asset()) << " : "
<< to_string(saTakerGets.asset()) << to_string(saTakerGets.asset())
+1 -2
View File
@@ -4,7 +4,6 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -47,7 +46,7 @@ DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, b
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
auto sleOwner = AccountRootEntryW(owner, view); auto const sleOwner = view.peek(keylet::account(owner));
if (!sleOwner) if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+6 -7
View File
@@ -3,7 +3,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -20,7 +20,6 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <optional>
namespace xrpl { namespace xrpl {
@@ -66,14 +65,15 @@ DIDSet::preflight(PreflightContext const& ctx)
static TER static TER
addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner) addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner)
{ {
auto sleAccount = AccountRootEntryW(owner, ctx.view(), ctx.journal); auto const sleAccount = ctx.view().peek(keylet::account(owner));
if (!sleAccount) if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Check reserve availability for new object creation // Check reserve availability for new object creation
{ {
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp(); auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
auto const reserve = sleAccount.reserve({.ownerCountDelta = 1}); auto const reserve =
accountReserve(ctx.view(), sleAccount, ctx.journal, {.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
@@ -90,9 +90,8 @@ addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner)
return tecDIR_FULL; // LCOV_EXCL_LINE return tecDIR_FULL; // LCOV_EXCL_LINE
(*sle)[sfOwnerNode] = *page; (*sle)[sfOwnerNode] = *page;
} }
std::optional<AccountRootEntryW> noSponsor; increaseOwnerCount(ctx.view(), sleAccount, {}, 1, ctx.journal);
sleAccount.increaseOwnerCount(noSponsor, 1); ctx.view().update(sleAccount);
sleAccount.update();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -3,7 +3,7 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h> #include <xrpl/ledger/helpers/EscrowHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -95,13 +95,12 @@ EscrowCancel::preclaim(PreclaimContext const& ctx)
if (ctx.view.rules().enabled(featureTokenEscrow)) if (ctx.view.rules().enabled(featureTokenEscrow))
{ {
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]); auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy); EscrowEntryR const escrow(ctx.tx[sfOwner], seqProxy, ctx.view);
auto const slep = ctx.view.read(k); if (!escrow)
if (!slep)
return tecNO_TARGET; return tecNO_TARGET;
AccountID const account = (*slep)[sfAccount]; AccountID const account = (*escrow)[sfAccount];
STAmount const amount = (*slep)[sfAmount]; STAmount const amount = (*escrow)[sfAmount];
if (!isXRP(amount)) if (!isXRP(amount))
{ {
@@ -121,9 +120,8 @@ TER
EscrowCancel::doApply() EscrowCancel::doApply()
{ {
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]); auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
auto const k = keylet::escrow(ctx_.tx[sfOwner], seqProxy); EscrowEntryW escrow(ctx_.tx[sfOwner], seqProxy, ctx_.view(), j_);
auto const slep = ctx_.view().peek(k); if (!escrow)
if (!slep)
{ {
if (ctx_.view().rules().enabled(featureTokenEscrow)) if (ctx_.view().rules().enabled(featureTokenEscrow))
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -134,19 +132,19 @@ EscrowCancel::doApply()
auto const now = ctx_.view().header().parentCloseTime; auto const now = ctx_.view().header().parentCloseTime;
// No cancel time specified: can't execute at all. // No cancel time specified: can't execute at all.
if (!(*slep)[~sfCancelAfter]) if (!(*escrow)[~sfCancelAfter])
return tecNO_PERMISSION; return tecNO_PERMISSION;
// Too soon: can't execute before the cancel time. // Too soon: can't execute before the cancel time.
if (!after(now, (*slep)[sfCancelAfter])) if (!after(now, (*escrow)[sfCancelAfter]))
return tecNO_PERMISSION; return tecNO_PERMISSION;
AccountID const account = (*slep)[sfAccount]; AccountID const account = (*escrow)[sfAccount];
// Remove escrow from owner directory // Remove escrow from owner directory
{ {
auto const page = (*slep)[sfOwnerNode]; auto const page = (*escrow)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true)) if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, escrow.key(), true))
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from owner."; JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
@@ -156,9 +154,10 @@ EscrowCancel::doApply()
} }
// Remove escrow from recipient's owner directory, if present. // Remove escrow from recipient's owner directory, if present.
if (auto const optPage = (*slep)[~sfDestinationNode]; optPage) if (auto const optPage = (*escrow)[~sfDestinationNode]; optPage)
{ {
if (!ctx_.view().dirRemove(keylet::ownerDir((*slep)[sfDestination]), *optPage, k.key, true)) if (!ctx_.view().dirRemove(
keylet::ownerDir((*escrow)[sfDestination]), *optPage, escrow.key(), true))
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -167,14 +166,14 @@ EscrowCancel::doApply()
} }
} }
auto sle = AccountRootEntryW(account, ctx_.view()); auto const sle = ctx_.view().peek(keylet::account(account));
STAmount const amount = slep->getFieldAmount(sfAmount); STAmount const amount = escrow->getFieldAmount(sfAmount);
// The return can re-create a holding the owner deleted while the escrow // The return can re-create a holding the owner deleted while the escrow
// was pending; the removed escrow must not be counted against its reserve. // was pending; the removed escrow must not be counted against its reserve.
bool const recycleReserve = ctx_.view().rules().enabled(fixCleanup3_4_0); bool const recycleReserve = ctx_.view().rules().enabled(fixCleanup3_4_0);
if (recycleReserve) if (recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal); decreaseOwnerCountForObject(ctx_.view(), sle, escrow.mutableRawSle(), 1, ctx_.journal);
// Transfer amount back to the owner // Transfer amount back to the owner
if (isXRP(amount)) if (isXRP(amount))
@@ -193,7 +192,7 @@ EscrowCancel::doApply()
return escrowUnlockApplyHelper<T>( return escrowUnlockApplyHelper<T>(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
kParityRate, kParityRate,
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle.mutableRawSle() : slep, ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : escrow.mutableRawSle(),
preFeeBalance_, preFeeBalance_,
amount, amount,
issuer, issuer,
@@ -207,9 +206,9 @@ EscrowCancel::doApply()
return ret; // LCOV_EXCL_LINE return ret; // LCOV_EXCL_LINE
// Remove escrow from issuers owner directory, if present. // Remove escrow from issuers owner directory, if present.
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage) if (auto const optPage = (*escrow)[~sfIssuerNode]; optPage)
{ {
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true)) if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, escrow.key(), true))
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -220,10 +219,10 @@ EscrowCancel::doApply()
} }
if (!recycleReserve) if (!recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal); decreaseOwnerCountForObject(ctx_.view(), sle, escrow.mutableRawSle(), 1, ctx_.journal);
// Remove escrow from ledger // Remove escrow from ledger
ctx_.view().erase(slep); escrow.erase();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -7,7 +7,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -208,7 +208,7 @@ escrowCreatePreclaimHelper<Issue>(
return tecNO_PERMISSION; return tecNO_PERMISSION;
// If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION // If the lsfAllowTrustLineLocking is not enabled, return tecNO_PERMISSION
auto const sleIssuer = AccountRootEntryR(issuer, ctx.view); auto const sleIssuer = ctx.view.read(keylet::account(issuer));
if (!sleIssuer) if (!sleIssuer)
return tecNO_ISSUER; return tecNO_ISSUER;
if (!sleIssuer->isFlag(lsfAllowTrustLineLocking)) if (!sleIssuer->isFlag(lsfAllowTrustLineLocking))
@@ -348,7 +348,7 @@ EscrowCreate::preclaim(PreclaimContext const& ctx)
AccountID const account{ctx.tx[sfAccount]}; AccountID const account{ctx.tx[sfAccount]};
AccountID const dest{ctx.tx[sfDestination]}; AccountID const dest{ctx.tx[sfDestination]};
auto const sled = AccountRootEntryR(dest, ctx.view); auto const sled = ctx.view.read(keylet::account(dest));
if (!sled) if (!sled)
return tecNO_DST; return tecNO_DST;
@@ -434,7 +434,7 @@ EscrowCreate::doApply()
if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter])) if (ctx_.tx[~sfFinishAfter] && after(closeTime, ctx_.tx[sfFinishAfter]))
return tecNO_PERMISSION; return tecNO_PERMISSION;
auto sle = AccountRootEntryW(accountID_, ctx_.view(), j_); auto const sle = ctx_.view().peek(keylet::account(accountID_));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -448,7 +448,7 @@ EscrowCreate::doApply()
// unsponsored this hits the source branch and validates the // unsponsored this hits the source branch and validates the
// source's pre-lock balance against base + (currentOC+1)*increment. // source's pre-lock balance against base + (currentOC+1)*increment.
if (auto const ret = if (auto const ret =
sle.checkReserve(ctx_.getApplyViewContext(), balance, {.ownerCountDelta = 1}); checkReserve(ctx_.getApplyViewContext(), sle, balance, {.ownerCountDelta = 1}, j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -465,15 +465,15 @@ EscrowCreate::doApply()
// - sponsored: 0 — sponsor covers the new owner increment, so the // - sponsored: 0 — sponsor covers the new owner increment, so the
// source only owes reserve for its current owners. // source only owes reserve for its current owners.
// - unsponsored: 1 — source owes reserve including the new increment. // - unsponsored: 1 — source owes reserve including the new increment.
auto const sourceReserve = auto const sourceReserve = accountReserve(
sle.reserve({.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1}); ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
if (balance - STAmount(amount).xrp() < sourceReserve) if (balance - STAmount(amount).xrp() < sourceReserve)
return tecUNFUNDED; return tecUNFUNDED;
} }
// Check destination account // Check destination account
{ {
auto const sled = AccountRootEntryW(ctx_.tx[sfDestination], ctx_.view()); auto const sled = ctx_.view().read(keylet::account(ctx_.tx[sfDestination]));
if (!sled) if (!sled)
return tecNO_DST; // LCOV_EXCL_LINE return tecNO_DST; // LCOV_EXCL_LINE
if (sled->isFlag(lsfRequireDestTag) && !ctx_.tx[~sfDestinationTag]) if (sled->isFlag(lsfRequireDestTag) && !ctx_.tx[~sfDestinationTag])
@@ -482,30 +482,31 @@ EscrowCreate::doApply()
// Create escrow in ledger. Note that we use the value from the // Create escrow in ledger. Note that we use the value from the
// sequence or ticket. For more explanation see comments in SeqProxy.h. // sequence or ticket. For more explanation see comments in SeqProxy.h.
Keylet const escrowKeylet = keylet::escrow(accountID_, ctx_.tx.getSeqProxy()); EscrowEntryW escrow(accountID_, ctx_.tx.getSeqProxy(), ctx_.view(), j_);
auto const slep = std::make_shared<SLE>(escrowKeylet); Keylet const escrowKeylet = escrow.keylet();
(*slep)[sfAmount] = amount; escrow.newSLE();
(*slep)[sfAccount] = accountID_; (*escrow)[sfAmount] = amount;
(*slep)[~sfCondition] = ctx_.tx[~sfCondition]; (*escrow)[sfAccount] = accountID_;
(*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; (*escrow)[~sfCondition] = ctx_.tx[~sfCondition];
(*slep)[sfDestination] = ctx_.tx[sfDestination]; (*escrow)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; (*escrow)[sfDestination] = ctx_.tx[sfDestination];
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter]; (*escrow)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; (*escrow)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
(*escrow)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
if (ctx_.view().rules().enabled(fixIncludeKeyletFields)) if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
{ {
(*slep)[sfSequence] = ctx_.tx.getSeqProxy().value(); (*escrow)[sfSequence] = ctx_.tx.getSeqProxy().value();
} }
if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount)) if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
{ {
auto const xferRate = transferRate(ctx_.view(), amount); auto const xferRate = transferRate(ctx_.view(), amount);
if (xferRate != kParityRate) if (xferRate != kParityRate)
(*slep)[sfTransferRate] = xferRate.value; (*escrow)[sfTransferRate] = xferRate.value;
} }
ctx_.view().insert(slep); escrow.insert();
// Add escrow to sender's owner directory // Add escrow to sender's owner directory
{ {
@@ -513,7 +514,7 @@ EscrowCreate::doApply()
keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_)); keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_));
if (!page) if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE return tecDIR_FULL; // LCOV_EXCL_LINE
(*slep)[sfOwnerNode] = *page; (*escrow)[sfOwnerNode] = *page;
} }
// If it's not a self-send, add escrow to recipient's owner directory. // If it's not a self-send, add escrow to recipient's owner directory.
@@ -524,7 +525,7 @@ EscrowCreate::doApply()
ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest)); ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
if (!page) if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE return tecDIR_FULL; // LCOV_EXCL_LINE
(*slep)[sfDestinationNode] = *page; (*escrow)[sfDestinationNode] = *page;
} }
// IOU escrow objects are added to the issuer's owner directory to help // IOU escrow objects are added to the issuer's owner directory to help
@@ -537,7 +538,7 @@ EscrowCreate::doApply()
ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer)); ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
if (!page) if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE return tecDIR_FULL; // LCOV_EXCL_LINE
(*slep)[sfIssuerNode] = *page; (*escrow)[sfIssuerNode] = *page;
} }
// Deduct owner's balance // Deduct owner's balance
@@ -559,9 +560,9 @@ EscrowCreate::doApply()
} }
// increment owner count // increment owner count
sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1); increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), escrow.mutableRawSle());
sle.update(); ctx_.view().update(sle);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -9,7 +9,7 @@
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h> #include <xrpl/ledger/helpers/EscrowHelpers.h>
@@ -212,13 +212,12 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
if (ctx.view.rules().enabled(featureTokenEscrow)) if (ctx.view.rules().enabled(featureTokenEscrow))
{ {
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]); auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy); EscrowEntryR const escrow(ctx.tx[sfOwner], seqProxy, ctx.view);
auto const slep = ctx.view.read(k); if (!escrow)
if (!slep)
return tecNO_TARGET; return tecNO_TARGET;
AccountID const dest = (*slep)[sfDestination]; AccountID const dest = (*escrow)[sfDestination];
STAmount const amount = (*slep)[sfAmount]; STAmount const amount = (*escrow)[sfAmount];
if (!isXRP(amount)) if (!isXRP(amount))
{ {
@@ -238,9 +237,8 @@ TER
EscrowFinish::doApply() EscrowFinish::doApply()
{ {
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]); auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
auto const k = keylet::escrow(ctx_.tx[sfOwner], seqProxy); EscrowEntryW escrow(ctx_.tx[sfOwner], seqProxy, ctx_.view(), j_);
auto const slep = ctx_.view().peek(k); if (!escrow)
if (!slep)
{ {
if (ctx_.view().rules().enabled(featureTokenEscrow)) if (ctx_.view().rules().enabled(featureTokenEscrow))
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -253,11 +251,11 @@ EscrowFinish::doApply()
auto const now = ctx_.view().header().parentCloseTime; auto const now = ctx_.view().header().parentCloseTime;
// Too soon: can't execute before the finish time // Too soon: can't execute before the finish time
if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter])) if ((*escrow)[~sfFinishAfter] && !after(now, (*escrow)[sfFinishAfter]))
return tecNO_PERMISSION; return tecNO_PERMISSION;
// Too late: can't execute after the cancel time // Too late: can't execute after the cancel time
if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter])) if ((*escrow)[~sfCancelAfter] && after(now, (*escrow)[sfCancelAfter]))
return tecNO_PERMISSION; return tecNO_PERMISSION;
// Check cryptocondition fulfillment // Check cryptocondition fulfillment
@@ -297,7 +295,7 @@ EscrowFinish::doApply()
return tecCRYPTOCONDITION_ERROR; return tecCRYPTOCONDITION_ERROR;
// Check against condition in the ledger entry: // Check against condition in the ledger entry:
auto const cond = (*slep)[~sfCondition]; auto const cond = (*escrow)[~sfCondition];
// If a condition wasn't specified during creation, // If a condition wasn't specified during creation,
// one shouldn't be included now. // one shouldn't be included now.
@@ -314,22 +312,22 @@ EscrowFinish::doApply()
} }
// NOTE: Escrow payments cannot be used to fund accounts. // NOTE: Escrow payments cannot be used to fund accounts.
AccountID const destID = (*slep)[sfDestination]; AccountID const destID = (*escrow)[sfDestination];
auto sled = AccountRootEntryW(destID, ctx_.view()); auto const sled = ctx_.view().peek(keylet::account(destID));
if (!sled) if (!sled)
return tecNO_DST; return tecNO_DST;
if (auto err = verifyDepositPreauth( if (auto err =
ctx_.tx, ctx_.view(), accountID_, destID, sled.rawSle(), ctx_.journal); verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
AccountID const account = (*slep)[sfAccount]; AccountID const account = (*escrow)[sfAccount];
// Remove escrow from owner directory // Remove escrow from owner directory
{ {
auto const page = (*slep)[sfOwnerNode]; auto const page = (*escrow)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true)) if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, escrow.key(), true))
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from owner."; JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
@@ -339,9 +337,9 @@ EscrowFinish::doApply()
} }
// Remove escrow from recipient's owner directory, if present. // Remove escrow from recipient's owner directory, if present.
if (auto const optPage = (*slep)[~sfDestinationNode]) if (auto const optPage = (*escrow)[~sfDestinationNode])
{ {
if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, k.key, true)) if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, escrow.key(), true))
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -356,9 +354,9 @@ EscrowFinish::doApply()
bool const recycleReserve = bool const recycleReserve =
ctx_.view().rules().enabled(featureSponsor) || ctx_.view().rules().enabled(fixCleanup3_4_0); ctx_.view().rules().enabled(featureSponsor) || ctx_.view().rules().enabled(fixCleanup3_4_0);
if (recycleReserve) if (recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal); decreaseOwnerCountForObject(ctx_.view(), account, escrow.mutableRawSle(), 1, ctx_.journal);
STAmount const amount = slep->getFieldAmount(sfAmount); STAmount const amount = escrow->getFieldAmount(sfAmount);
// Transfer amount to destination // Transfer amount to destination
if (isXRP(amount)) if (isXRP(amount))
{ {
@@ -369,8 +367,8 @@ EscrowFinish::doApply()
if (!ctx_.view().rules().enabled(featureTokenEscrow)) if (!ctx_.view().rules().enabled(featureTokenEscrow))
return temDISABLED; // LCOV_EXCL_LINE return temDISABLED; // LCOV_EXCL_LINE
Rate lockedRate = slep->isFieldPresent(sfTransferRate) Rate lockedRate = escrow->isFieldPresent(sfTransferRate)
? xrpl::Rate(slep->getFieldU32(sfTransferRate)) ? xrpl::Rate(escrow->getFieldU32(sfTransferRate))
: kParityRate; : kParityRate;
auto const issuer = amount.getIssuer(); auto const issuer = amount.getIssuer();
bool const createAsset = destID == accountID_; bool const createAsset = destID == accountID_;
@@ -379,7 +377,7 @@ EscrowFinish::doApply()
return escrowUnlockApplyHelper<T>( return escrowUnlockApplyHelper<T>(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
lockedRate, lockedRate,
sled.mutableRawSle(), sled,
preFeeBalance_, preFeeBalance_,
amount, amount,
issuer, issuer,
@@ -393,9 +391,9 @@ EscrowFinish::doApply()
return ret; return ret;
// Remove escrow from issuers owner directory, if present. // Remove escrow from issuers owner directory, if present.
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage) if (auto const optPage = (*escrow)[~sfIssuerNode]; optPage)
{ {
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true)) if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, escrow.key(), true))
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient."; JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -405,13 +403,13 @@ EscrowFinish::doApply()
} }
} }
sled.update(); ctx_.view().update(sled);
if (!recycleReserve) if (!recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal); decreaseOwnerCountForObject(ctx_.view(), account, escrow.mutableRawSle(), 1, ctx_.journal);
// Remove escrow from ledger // Remove escrow from ledger
ctx_.view().erase(slep); escrow.erase();
return tesSUCCESS; return tesSUCCESS;
} }
@@ -6,7 +6,6 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -109,7 +108,7 @@ determineBrokerID(ReadView const& view, STTx const& tx)
// Thus, Amount.issuer _should_ be the loan broker's // Thus, Amount.issuer _should_ be the loan broker's
// pseudo-account, but we don't know yet whether it is. // pseudo-account, but we don't know yet whether it is.
auto const maybePseudo = dstAmount->getIssuer(); auto const maybePseudo = dstAmount->getIssuer();
auto const sle = AccountRootEntryR(maybePseudo, view); auto const sle = view.read(keylet::account(maybePseudo));
// If the account was not found, the transaction can't go further. // If the account was not found, the transaction can't go further.
if (!sle) if (!sle)
@@ -320,7 +319,7 @@ LoanBrokerCoverClawback::preclaim(PreclaimContext const& ctx)
return tecINTERNAL; // tecINSUFFICIENT_FUNDS; LCOV_EXCL_LINE return tecINTERNAL; // tecINSUFFICIENT_FUNDS; LCOV_EXCL_LINE
// Check if the vault asset issuer has the correct flags // Check if the vault asset issuer has the correct flags
auto const sleIssuer = AccountRootEntryR(vaultAsset.getIssuer(), ctx.view); auto const sleIssuer = ctx.view.read(keylet::account(vaultAsset.getIssuer()));
if (!sleIssuer) if (!sleIssuer)
{ {
// LCOV_EXCL_START // LCOV_EXCL_START
@@ -3,7 +3,6 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h> #include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -162,7 +161,7 @@ LoanBrokerDelete::doApply()
if (auto ter = removeEmptyHolding(ctx_.getApplyViewContext(), brokerPseudoID, vaultAsset, j_)) if (auto ter = removeEmptyHolding(ctx_.getApplyViewContext(), brokerPseudoID, vaultAsset, j_))
return ter; return ter;
auto brokerPseudoSLE = AccountRootEntryW(brokerPseudoID, view()); auto brokerPseudoSLE = view().peek(keylet::account(brokerPseudoID));
if (!brokerPseudoSLE) if (!brokerPseudoSLE)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -184,10 +183,10 @@ LoanBrokerDelete::doApply()
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
} }
brokerPseudoSLE.erase(); view().erase(brokerPseudoSLE);
{ {
auto owner = AccountRootEntryW(accountID_, view()); auto owner = view().peek(keylet::account(accountID_));
if (!owner) if (!owner)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -5,7 +5,6 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -24,7 +23,6 @@
#include <xrpl/tx/Transactor.h> #include <xrpl/tx/Transactor.h>
#include <memory> #include <memory>
#include <optional>
#include <vector> #include <vector>
namespace xrpl { namespace xrpl {
@@ -238,7 +236,7 @@ LoanBrokerSet::doApply()
auto const vaultAsset = sleVault->at(sfAsset); auto const vaultAsset = sleVault->at(sfAsset);
auto const sequence = tx.getSeqProxy(); auto const sequence = tx.getSeqProxy();
auto owner = AccountRootEntryW(accountID_, view, j_); auto owner = view.peek(keylet::account(accountID_));
if (!owner) if (!owner)
{ {
// This should be impossible // This should be impossible
@@ -256,9 +254,8 @@ LoanBrokerSet::doApply()
// Increases the owner count by two: one for the LoanBroker object, and // Increases the owner count by two: one for the LoanBroker object, and
// one for the pseudo-account. // one for the pseudo-account.
std::optional<AccountRootEntryW> noSponsor; increaseOwnerCount(view, owner, {}, 2, j_);
owner.increaseOwnerCount(noSponsor, 2); if (preFeeBalance_ < accountReserve(view, owner, j_))
if (preFeeBalance_ < owner.reserve())
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID); auto maybePseudo = createPseudoAccount(view, broker->key(), sfLoanBrokerID);
@@ -4,7 +4,6 @@
#include <xrpl/basics/Number.h> // IWYU pragma: keep #include <xrpl/basics/Number.h> // IWYU pragma: keep
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -81,7 +80,7 @@ LoanDelete::doApply()
if (!loanSle) if (!loanSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const borrower = loanSle->at(sfBorrower); auto const borrower = loanSle->at(sfBorrower);
auto borrowerSle = AccountRootEntryW(borrower, view); auto const borrowerSle = view.peek(keylet::account(borrower));
if (!borrowerSle) if (!borrowerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -7,7 +7,6 @@
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/to_string.h> #include <xrpl/json/to_string.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -57,7 +56,7 @@ conservationBalance(ReadView const& view, AccountID const& id, Asset const& asse
{ {
if (isXRP(asset)) if (isXRP(asset))
{ {
auto const sle = AccountRootEntryR(id, view); auto const sle = view.read(keylet::account(id));
if (!sle) if (!sle)
return STAmount{asset}; // LCOV_EXCL_LINE return STAmount{asset}; // LCOV_EXCL_LINE
return view.balanceHookIOU(id, xrpAccount(), sle->getFieldAmount(sfBalance)); return view.balanceHookIOU(id, xrpAccount(), sle->getFieldAmount(sfBalance));
@@ -392,7 +391,7 @@ LoanPay::doApply()
}(); }();
auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount; auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount;
auto const brokerPayeeSle = AccountRootEntryW(brokerPayee, view); auto const brokerPayeeSle = view.peek(keylet::account(brokerPayee));
if (!sendBrokerFeeToOwner) if (!sendBrokerFeeToOwner)
{ {
// If we can't send the fee to the owner, and the pseudo-account is // If we can't send the fee to the owner, and the pseudo-account is
@@ -7,7 +7,6 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/LendingHelpers.h> #include <xrpl/ledger/helpers/LendingHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -92,7 +91,8 @@ LoanSet::preflight(PreflightContext const& ctx)
// A proposed LoanSet is stored unsigned; its CounterpartySignature is // A proposed LoanSet is stored unsigned; its CounterpartySignature is
// collected on-ledger afterward, so its absence here is expected, not an // collected on-ledger afterward, so its absence here is expected, not an
// error (On-Chain Cosigner spec §5.3.1.2). // error (On-Chain Cosigner spec §5.3.1.2).
if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig && (ctx.flags & TapProposal) == 0) if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig &&
(ctx.flags & ApplyFlags::Proposal) == ApplyFlags::None)
{ {
JLOG(ctx.j.warn()) << "LoanSet transaction must have a CounterpartySignature."; JLOG(ctx.j.warn()) << "LoanSet transaction must have a CounterpartySignature.";
return temBAD_SIGNER; return temBAD_SIGNER;
@@ -306,7 +306,7 @@ LoanSet::preclaim(PreclaimContext const& ctx)
auto const brokerPseudo = brokerSle->at(sfAccount); auto const brokerPseudo = brokerSle->at(sfAccount);
auto const borrower = counterparty == brokerOwner ? account : counterparty; auto const borrower = counterparty == brokerOwner ? account : counterparty;
if (auto const borrowerSle = AccountRootEntryR(borrower, ctx.view); !borrowerSle) if (auto const borrowerSle = ctx.view.read(keylet::account(borrower)); !borrowerSle)
{ {
// It may not be possible to hit this case, because it'll fail the // It may not be possible to hit this case, because it'll fail the
// signature check with terNO_ACCOUNT. // signature check with terNO_ACCOUNT.
@@ -444,7 +444,7 @@ LoanSet::doApply()
if (!brokerSle) if (!brokerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const brokerOwner = brokerSle->at(sfOwner); auto const brokerOwner = brokerSle->at(sfOwner);
auto const brokerOwnerSle = AccountRootEntryW(brokerOwner, view); auto const brokerOwnerSle = view.peek(keylet::account(brokerOwner));
if (!brokerOwnerSle) if (!brokerOwnerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -456,14 +456,14 @@ LoanSet::doApply()
auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner); auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner);
auto const borrower = counterparty == brokerOwner ? accountID_ : counterparty; auto const borrower = counterparty == brokerOwner ? accountID_ : counterparty;
auto borrowerSle = AccountRootEntryW(borrower, view, j_); auto const borrowerSle = view.peek(keylet::account(borrower));
if (!borrowerSle) if (!borrowerSle)
{ {
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
} }
auto const brokerPseudo = brokerSle->at(sfAccount); auto const brokerPseudo = brokerSle->at(sfAccount);
auto const brokerPseudoSle = AccountRootEntryW(brokerPseudo, view); auto const brokerPseudoSle = view.peek(keylet::account(brokerPseudo));
if (!brokerPseudoSle) if (!brokerPseudoSle)
{ {
return tefBAD_LEDGER; // LCOV_EXCL_LINE return tefBAD_LEDGER; // LCOV_EXCL_LINE
@@ -582,13 +582,12 @@ LoanSet::doApply()
} }
} }
std::optional<AccountRootEntryW> noSponsor; increaseOwnerCount(view, borrowerSle, {}, 1, j_);
borrowerSle.increaseOwnerCount(noSponsor, 1);
{ {
auto const balance = auto const balance =
accountID_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp(); accountID_ == borrower ? preFeeBalance_ : borrowerSle->at(sfBalance).value().xrp();
if (balance < borrowerSle.reserve()) if (balance < accountReserve(view, borrowerSle, j_))
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
} }
@@ -5,7 +5,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h> #include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/Asset.h> #include <xrpl/protocol/Asset.h>
@@ -376,7 +376,7 @@ NFTokenAcceptOffer::transferNFToken(
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
auto const sleBuyer = AccountRootEntryW(buyer, view(), j_); auto const sleBuyer = view().read(keylet::account(buyer));
if (!sleBuyer) if (!sleBuyer)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -397,7 +397,7 @@ NFTokenAcceptOffer::transferNFToken(
auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount); auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
if (buyerOwnerCountAfter > buyerOwnerCountBefore) if (buyerOwnerCountAfter > buyerOwnerCountBefore)
{ {
if (buyerBalance < sleBuyer.reserve()) if (buyerBalance < accountReserve(view(), sleBuyer, j_))
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
} }
@@ -1,8 +1,6 @@
#include <xrpl/tx/transactors/nft/NFTokenBurn.h> #include <xrpl/tx/transactors/nft/NFTokenBurn.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
@@ -45,7 +43,7 @@ NFTokenBurn::preclaim(PreclaimContext const& ctx)
if (auto const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account) if (auto const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account)
{ {
if (auto const sle = AccountRootEntryR(issuer, ctx.view); sle) if (auto const sle = ctx.view.read(keylet::account(issuer)); sle)
{ {
if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account) if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account)
return tecNO_PERMISSION; return tecNO_PERMISSION;
@@ -70,10 +68,10 @@ NFTokenBurn::doApply()
if (!isTesSuccess(ret)) if (!isTesSuccess(ret))
return ret; return ret;
if (auto issuer = AccountRootEntryW(nft::getIssuer(ctx_.tx[sfNFTokenID]), view())) if (auto issuer = view().peek(keylet::account(nft::getIssuer(ctx_.tx[sfNFTokenID]))))
{ {
(*issuer)[~sfBurnedNFTokens] = (*issuer)[~sfBurnedNFTokens].valueOr(0) + 1; (*issuer)[~sfBurnedNFTokens] = (*issuer)[~sfBurnedNFTokens].valueOr(0) + 1;
issuer.update(); view().update(issuer);
} }
// Delete up to 500 offers in total. // Delete up to 500 offers in total.
@@ -2,12 +2,12 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/InnerObjectFormats.h> #include <xrpl/protocol/InnerObjectFormats.h>
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
@@ -186,7 +186,7 @@ NFTokenMint::preclaim(PreclaimContext const& ctx)
// transaction. Check that and verify that this is allowed: // transaction. Check that and verify that this is allowed:
if (auto issuer = ctx.tx[~sfIssuer]) if (auto issuer = ctx.tx[~sfIssuer])
{ {
auto const sle = AccountRootEntryR(*issuer, ctx.view); auto const sle = ctx.view.read(keylet::account(*issuer));
if (!sle) if (!sle)
return tecNO_ISSUER; return tecNO_ISSUER;
@@ -225,8 +225,8 @@ NFTokenMint::doApply()
auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_); auto const issuer = ctx_.tx[~sfIssuer].value_or(accountID_);
auto const tokenSeq = [this, &issuer]() -> std::expected<std::uint32_t, TER> { auto const tokenSeq = [this, &issuer]() -> std::expected<std::uint32_t, TER> {
auto root = AccountRootEntryW(issuer, view()); auto const root = view().peek(keylet::account(issuer));
if (!root) if (root == nullptr)
{ {
// Should not happen. Checked in preclaim. // Should not happen. Checked in preclaim.
return std::unexpected(tecNO_ISSUER); return std::unexpected(tecNO_ISSUER);
@@ -271,7 +271,7 @@ NFTokenMint::doApply()
if (tokenSeq + 1u == 0u || tokenSeq < offset) if (tokenSeq + 1u == 0u || tokenSeq < offset)
return std::unexpected(tecMAX_SEQUENCE_REACHED); return std::unexpected(tecMAX_SEQUENCE_REACHED);
root.update(); ctx_.view().update(root);
return tokenSeq; return tokenSeq;
}(); }();
@@ -279,7 +279,7 @@ NFTokenMint::doApply()
return (tokenSeq.error()); return (tokenSeq.error());
std::uint32_t const ownerCountBefore = std::uint32_t const ownerCountBefore =
AccountRootEntryW(accountID_, view())->getFieldU32(sfOwnerCount); view().read(keylet::account(accountID_))->getFieldU32(sfOwnerCount);
// Assemble the new NFToken. // Assemble the new NFToken.
SOTemplate const* nfTokenTemplate = SOTemplate const* nfTokenTemplate =
@@ -332,14 +332,14 @@ NFTokenMint::doApply()
// allows NFTs to be added to the page (and burn fees) without // allows NFTs to be added to the page (and burn fees) without
// requiring the reserve to be met each time. The reserve is // requiring the reserve to be met each time. The reserve is
// only managed when a new NFT page or sell offer is added. // only managed when a new NFT page or sell offer is added.
auto const sleAccount = AccountRootEntryW(accountID_, view(), j_); auto const sleAccount = view().read(keylet::account(accountID_));
if (!sleAccount) if (!sleAccount)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ownerCountAfter = sleAccount->getFieldU32(sfOwnerCount); if (auto const ownerCountAfter = sleAccount->getFieldU32(sfOwnerCount);
ownerCountAfter > ownerCountBefore) ownerCountAfter > ownerCountBefore)
{ {
if (preFeeBalance_ < sleAccount.reserve()) if (preFeeBalance_ < accountReserve(view(), sleAccount, j_))
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
} }
return tesSUCCESS; return tesSUCCESS;
@@ -2,10 +2,9 @@
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h> #include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h> #include <xrpl/protocol/STLedgerEntry.h>
@@ -48,7 +47,7 @@ NFTokenModify::preclaim(PreclaimContext const& ctx)
// Verify permissions for the issuer // Verify permissions for the issuer
if (AccountID const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account) if (AccountID const issuer = nft::getIssuer(ctx.tx[sfNFTokenID]); issuer != account)
{ {
auto const sle = AccountRootEntryR(issuer, ctx.view); auto const sle = ctx.view.read(keylet::account(issuer));
if (!sle) if (!sle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account) if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != account)
@@ -3,7 +3,6 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h> #include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
@@ -68,7 +67,7 @@ OracleDelete::deleteOracle(
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
auto sleOwner = AccountRootEntryW(account, view); auto const sleOwner = view.peek(keylet::account(account));
if (!sleOwner) if (!sleOwner)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
+19 -17
View File
@@ -3,7 +3,7 @@
#include <xrpl/basics/chrono.h> #include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/OracleHelpers.h> #include <xrpl/ledger/helpers/OracleHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -27,7 +27,6 @@
#include <cstdlib> #include <cstdlib>
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional>
#include <set> #include <set>
#include <utility> #include <utility>
@@ -65,7 +64,7 @@ OracleSet::preflight(PreflightContext const& ctx)
TER TER
OracleSet::preclaim(PreclaimContext const& ctx) OracleSet::preclaim(PreclaimContext const& ctx)
{ {
auto const sleSetter = AccountRootEntryR(ctx.tx.getAccountID(sfAccount), ctx.view, ctx.j); auto const sleSetter = ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount)));
if (!sleSetter) if (!sleSetter)
return terNO_ACCOUNT; // LCOV_EXCL_LINE return terNO_ACCOUNT; // LCOV_EXCL_LINE
@@ -173,7 +172,8 @@ OracleSet::preclaim(PreclaimContext const& ctx)
if (pairs.size() > kMaxOracleDataSeries) if (pairs.size() > kMaxOracleDataSeries)
return tecARRAY_TOO_LARGE; return tecARRAY_TOO_LARGE;
auto const reserve = sleSetter.reserve({.ownerCountDelta = adjustReserve}); auto const reserve =
accountReserve(ctx.view, sleSetter, ctx.j, {.ownerCountDelta = adjustReserve});
auto const& balance = sleSetter->getFieldAmount(sfBalance); auto const& balance = sleSetter->getFieldAmount(sfBalance);
if (balance < reserve) if (balance < reserve)
@@ -187,20 +187,22 @@ adjustOracleOwnerCount(ApplyContext& ctx, int count)
{ {
XRPL_ASSERT(std::abs(count) <= 2, "xrpl::adjustOracleOwnerCount abs(counter) <= 2"); XRPL_ASSERT(std::abs(count) <= 2, "xrpl::adjustOracleOwnerCount abs(counter) <= 2");
auto sleAccount = AccountRootEntryW(ctx.tx[sfAccount], ctx.view(), ctx.journal); if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.tx[sfAccount])))
if (!sleAccount) {
return false; // LCOV_EXCL_LINE 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;
}
std::optional<AccountRootEntryW> noSponsor; return false; // LCOV_EXCL_LINE
if (count > 0)
{
sleAccount.increaseOwnerCount(noSponsor, static_cast<std::uint32_t>(count));
}
else if (count < 0)
{
sleAccount.decreaseOwnerCount(noSponsor, static_cast<std::uint32_t>(-count));
}
return true;
} }
static void static void
@@ -4,7 +4,6 @@
#include <xrpl/basics/Slice.h> #include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h> #include <xrpl/basics/base_uint.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -104,7 +103,7 @@ DepositPreauth::preclaim(PreclaimContext const& ctx)
{ {
// Verify that the Authorize account is present in the ledger. // Verify that the Authorize account is present in the ledger.
AccountID const auth{ctx.tx[sfAuthorize]}; AccountID const auth{ctx.tx[sfAuthorize]};
auto const sleAuth = AccountRootEntryR(auth, ctx.view); auto const sleAuth = ctx.view.read(keylet::account(auth));
if (!sleAuth) if (!sleAuth)
return tecNO_TARGET; return tecNO_TARGET;
@@ -162,15 +161,15 @@ DepositPreauth::doApply()
auto applyViewContext = ctx_.getApplyViewContext(); auto applyViewContext = ctx_.getApplyViewContext();
if (ctx_.tx.isFieldPresent(sfAuthorize)) if (ctx_.tx.isFieldPresent(sfAuthorize))
{ {
auto sleOwner = AccountRootEntryW(accountID_, view(), j_); auto const sleOwner = view().peek(keylet::account(accountID_));
if (!sleOwner) if (!sleOwner)
return {tefINTERNAL}; return {tefINTERNAL};
// A preauth counts against the reserve of the issuing account, but we // A preauth counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the // check the starting balance because we want to allow dipping into the
// reserve to pay fees. // reserve to pay fees.
if (auto const ret = if (auto const ret = checkReserve(
sleOwner.checkReserve(applyViewContext, preFeeBalance_, {.ownerCountDelta = 1}); applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -196,7 +195,7 @@ DepositPreauth::doApply()
slePreauth->setFieldU64(sfOwnerNode, *page); slePreauth->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve. // If we succeeded, the new entry counts against the creator's reserve.
sleOwner.increaseOwnerCount(applyViewContext, 1); increaseOwnerCount(applyViewContext, sleOwner, 1, j_);
addSponsorToLedgerEntry(applyViewContext, slePreauth); addSponsorToLedgerEntry(applyViewContext, slePreauth);
} }
else if (ctx_.tx.isFieldPresent(sfUnauthorize)) else if (ctx_.tx.isFieldPresent(sfUnauthorize))
@@ -207,15 +206,15 @@ DepositPreauth::doApply()
} }
else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials)) else if (ctx_.tx.isFieldPresent(sfAuthorizeCredentials))
{ {
auto sleOwner = AccountRootEntryW(accountID_, view(), j_); auto const sleOwner = view().peek(keylet::account(accountID_));
if (!sleOwner) if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// A preauth counts against the reserve of the issuing account, but we // A preauth counts against the reserve of the issuing account, but we
// check the starting balance because we want to allow dipping into the // check the starting balance because we want to allow dipping into the
// reserve to pay fees. // reserve to pay fees.
if (auto const ret = if (auto const ret = checkReserve(
sleOwner.checkReserve(applyViewContext, preFeeBalance_, {.ownerCountDelta = 1}); applyViewContext, sleOwner, preFeeBalance_, {.ownerCountDelta = 1}, j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -255,7 +254,7 @@ DepositPreauth::doApply()
slePreauth->setFieldU64(sfOwnerNode, *page); slePreauth->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve. // If we succeeded, the new entry counts against the creator's reserve.
sleOwner.increaseOwnerCount(applyViewContext, 1); increaseOwnerCount(applyViewContext, sleOwner, 1, j_);
addSponsorToLedgerEntry(applyViewContext, slePreauth); addSponsorToLedgerEntry(applyViewContext, slePreauth);
} }
else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials)) else if (ctx_.tx.isFieldPresent(sfUnauthorizeCredentials))
@@ -290,7 +289,7 @@ DepositPreauth::removeFromLedger(ApplyView& view, UInt256 const& preauthIndex, b
} }
// If we succeeded, update the DepositPreauth owner's reserve. // If we succeeded, update the DepositPreauth owner's reserve.
auto sleOwner = AccountRootEntryW(account, view); auto const sleOwner = view.peek(keylet::account(account));
if (!sleOwner) if (!sleOwner)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
+13 -29
View File
@@ -6,7 +6,6 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/PaymentSandbox.h> #include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h> #include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -387,7 +386,7 @@ Payment::preclaim(PreclaimContext const& ctx)
STAmount const dstAmount(ctx.tx[sfAmount]); STAmount const dstAmount(ctx.tx[sfAmount]);
auto const k = keylet::account(dstAccountID); auto const k = keylet::account(dstAccountID);
auto const sleDst = AccountRootEntryR(k, ctx.view); auto const sleDst = ctx.view.read(k);
if (!sleDst) if (!sleDst)
{ {
@@ -565,19 +564,19 @@ Payment::doApply()
// Open a ledger for editing. // Open a ledger for editing.
auto const k = keylet::account(dstAccountID); auto const k = keylet::account(dstAccountID);
AccountRootEntryW sleDst(k, view()); SLE::pointer sleDst = view().peek(k);
if (!sleDst) if (!sleDst)
{ {
// Create the account. // Create the account.
sleDst.newSLE(); sleDst = std::make_shared<SLE>(k);
sleDst->setAccountID(sfAccount, dstAccountID); sleDst->setAccountID(sfAccount, dstAccountID);
sleDst->setFieldU32(sfSequence, view().seq()); sleDst->setFieldU32(sfSequence, view().seq());
sleDst->setFieldAmount(sfBalance, XRPAmount(beast::kZero)); sleDst->setFieldAmount(sfBalance, XRPAmount(beast::kZero));
if (ctx_.tx.isFlag(tfSponsorCreatedAccount)) if (ctx_.tx.isFlag(tfSponsorCreatedAccount))
{ {
auto sponsor = AccountRootEntryW(accountID_, view()); auto const sponsor = view().peek(keylet::account(accountID_));
if (!sponsor) if (!sponsor)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const currentSponsoringAccountCount = auto const currentSponsoringAccountCount =
@@ -592,18 +591,18 @@ Payment::doApply()
} }
sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1); sponsor->setFieldU32(sfSponsoringAccountCount, currentSponsoringAccountCount + 1);
addSponsorToLedgerEntry(sleDst.mutableRawSle(), sponsor.rawSle()); addSponsorToLedgerEntry(sleDst, sponsor);
sponsor.update(); view().update(sponsor);
} }
sleDst.insert(); view().insert(sleDst);
} }
else else
{ {
// Tell the engine that we are intending to change the destination // Tell the engine that we are intending to change the destination
// account. The source account gets always charged a fee so it's always // account. The source account gets always charged a fee so it's always
// marked as modified. // marked as modified.
sleDst.update(); view().update(sleDst);
} }
bool const mpTokensV2 = view().rules().enabled(featureMPTokensV2); bool const mpTokensV2 = view().rules().enabled(featureMPTokensV2);
@@ -622,12 +621,7 @@ Payment::doApply()
// 2. If Account is deposit preauthorized by destination. // 2. If Account is deposit preauthorized by destination.
if (auto err = verifyDepositPreauth( if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal);
ctx_.view(),
accountID_,
dstAccountID,
sleDst.mutableRawSle(),
ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
@@ -697,12 +691,7 @@ Payment::doApply()
return ter; return ter;
if (auto err = verifyDepositPreauth( if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal);
ctx_.view(),
accountID_,
dstAccountID,
sleDst.mutableRawSle(),
ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
@@ -768,13 +757,13 @@ Payment::doApply()
// Direct XRP payment. // Direct XRP payment.
auto sleSrc = AccountRootEntryW(accountID_, view(), j_); auto const sleSrc = view().peek(keylet::account(accountID_));
if (!sleSrc) if (!sleSrc)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// the number of reserves in this ledger for this account that require a // the number of reserves in this ledger for this account that require a
// reserve. // reserve.
auto const reserve = sleSrc.reserve(); auto const reserve = accountReserve(view(), sleSrc, j_);
// In a delegated / fee sponsored payment, the fee payer is not the source account (accountID_). // In a delegated / fee sponsored payment, the fee payer is not the source account (accountID_).
bool const accountIsPayer = ctx_.tx.getFeePayerID() == accountID_; bool const accountIsPayer = ctx_.tx.getFeePayerID() == accountID_;
@@ -831,12 +820,7 @@ Payment::doApply()
if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve) if (dstAmount > dstReserve || sleDst->getFieldAmount(sfBalance) > dstReserve)
{ {
if (auto err = verifyDepositPreauth( if (auto err = verifyDepositPreauth(
ctx_.tx, ctx_.tx, ctx_.view(), accountID_, dstAccountID, sleDst, ctx_.journal);
ctx_.view(),
accountID_,
dstAccountID,
sleDst.mutableRawSle(),
ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
} }
@@ -3,11 +3,11 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h> #include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/PayChan.h> #include <xrpl/protocol/PayChan.h>
@@ -159,12 +159,12 @@ PaymentChannelClaim::doApply()
return tecUNFUNDED_PAYMENT; return tecUNFUNDED_PAYMENT;
} }
auto sled = AccountRootEntryW(dst, ctx_.view()); auto const sled = ctx_.view().peek(keylet::account(dst));
if (!sled) if (!sled)
return tecNO_DST; return tecNO_DST;
if (auto err = verifyDepositPreauth( if (auto err =
ctx_.tx, ctx_.view(), txAccount, dst, sled.rawSle(), ctx_.journal); verifyDepositPreauth(ctx_.tx, ctx_.view(), txAccount, dst, sled, ctx_.journal);
!isTesSuccess(err)) !isTesSuccess(err))
return err; return err;
@@ -173,7 +173,7 @@ PaymentChannelClaim::doApply()
XRPL_ASSERT( XRPL_ASSERT(
reqDelta >= beast::kZero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta"); reqDelta >= beast::kZero, "xrpl::PaymentChannelClaim::doApply : minimum balance delta");
(*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta; (*sled)[sfBalance] = (*sled)[sfBalance] + reqDelta;
sled.update(); ctx_.view().update(sled);
ctx_.view().update(slep); ctx_.view().update(slep);
} }
@@ -5,7 +5,6 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -74,7 +73,7 @@ TER
PaymentChannelCreate::preclaim(PreclaimContext const& ctx) PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
{ {
auto const account = ctx.tx[sfAccount]; auto const account = ctx.tx[sfAccount];
auto const sle = AccountRootEntryR(account, ctx.view); auto const sle = ctx.view.read(keylet::account(account));
if (!sle) if (!sle)
return terNO_ACCOUNT; return terNO_ACCOUNT;
@@ -95,7 +94,7 @@ PaymentChannelCreate::preclaim(PreclaimContext const& ctx)
{ {
// Check destination account // Check destination account
auto const sled = AccountRootEntryR(dst, ctx.view); auto const sled = ctx.view.read(keylet::account(dst));
if (!sled) if (!sled)
return tecNO_DST; return tecNO_DST;
@@ -123,7 +122,7 @@ TER
PaymentChannelCreate::doApply() PaymentChannelCreate::doApply()
{ {
auto const account = ctx_.tx[sfAccount]; auto const account = ctx_.tx[sfAccount];
auto sle = AccountRootEntryW(account, ctx_.view(), j_); auto const sle = ctx_.view().peek(keylet::account(account));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -141,8 +140,8 @@ PaymentChannelCreate::doApply()
// validates the sponsor's reserve + remaining credit. When // validates the sponsor's reserve + remaining credit. When
// unsponsored this hits the source branch and validates the // unsponsored this hits the source branch and validates the
// source's pre-lock balance against base + (currentOC+1)*increment. // source's pre-lock balance against base + (currentOC+1)*increment.
if (auto const ret = sle.checkReserve( if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1}); ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -158,8 +157,8 @@ PaymentChannelCreate::doApply()
// - sponsored: 0 — sponsor covers the new owner increment, so // - sponsored: 0 — sponsor covers the new owner increment, so
// the source only owes reserve for its current owners. // the source only owes reserve for its current owners.
// - unsponsored: 1 — source owes reserve including the new increment. // - unsponsored: 1 — source owes reserve including the new increment.
auto const sourceReserve = auto const sourceReserve = accountReserve(
sle.reserve({.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1}); ctx_.view(), sle, j_, {.ownerCountDelta = getTxReserveSponsorID(ctx_.tx) ? 0 : 1});
if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve) if (preFeeBalance_ - ctx_.tx[sfAmount].xrp() < sourceReserve)
return tecUNFUNDED; return tecUNFUNDED;
} }
@@ -211,9 +210,9 @@ PaymentChannelCreate::doApply()
// Deduct owner's balance, increment owner count // Deduct owner's balance, increment owner count
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1); increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
sle.update(); ctx_.view().update(sle);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -4,10 +4,11 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/PaymentChannelHelpers.h> #include <xrpl/ledger/helpers/PaymentChannelHelpers.h>
#include <xrpl/protocol/AccountID.h> #include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h> #include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h> #include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
@@ -81,14 +82,14 @@ PaymentChannelFund::doApply()
ctx_.view().update(slep); ctx_.view().update(slep);
} }
auto sle = AccountRootEntryW(txAccount, ctx_.view(), j_); auto const sle = ctx_.view().peek(keylet::account(txAccount));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
{ {
// Check reserve and funds availability // Check reserve and funds availability
STAmount const balance = (*sle)[sfBalance]; STAmount const balance = (*sle)[sfBalance];
if (auto const ret = sle.checkReserve(ctx_.getApplyViewContext(), balance.xrp(), {}); if (auto const ret = checkReserve(ctx_.getApplyViewContext(), sle, balance.xrp(), {}, j_);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -98,12 +99,12 @@ PaymentChannelFund::doApply()
// when a sponsor is present and would ignore the source's post-lock // when a sponsor is present and would ignore the source's post-lock
// balance entirely. Funding an existing channel adds no owned object, // balance entirely. Funding an existing channel adds no owned object,
// so there is no owner-count delta. // so there is no owner-count delta.
if (balance < sle.reserve() + ctx_.tx[sfAmount]) if (balance < accountReserve(ctx_.view(), sle, j_) + ctx_.tx[sfAmount])
return tecUNFUNDED; return tecUNFUNDED;
} }
// do not allow adding funds if dst does not exist // do not allow adding funds if dst does not exist
if (AccountID const dst = (*slep)[sfDestination]; !AccountRootEntryW(dst, ctx_.view())) if (AccountID const dst = (*slep)[sfDestination]; !ctx_.view().read(keylet::account(dst)))
{ {
return tecNO_DST; return tecNO_DST;
} }
@@ -112,7 +113,7 @@ PaymentChannelFund::doApply()
ctx_.view().update(slep); ctx_.view().update(slep);
(*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount];
sle.update(); ctx_.view().update(sle);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -3,7 +3,6 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/SField.h> #include <xrpl/protocol/SField.h>
@@ -64,7 +63,7 @@ PermissionedDomainDelete::doApply()
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
auto ownerSle = AccountRootEntryW(accountID_, view()); auto const ownerSle = view().peek(keylet::account(accountID_));
XRPL_ASSERT( XRPL_ASSERT(
ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0, ownerSle && ownerSle->getFieldU32(sfOwnerCount) > 0,
"xrpl::PermissionedDomainDelete::doApply : nonzero owner count"); "xrpl::PermissionedDomainDelete::doApply : nonzero owner count");
@@ -2,7 +2,7 @@
#include <xrpl/beast/utility/Zero.h> #include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h> #include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -19,7 +19,6 @@
#include <xrpl/tx/Transactor.h> #include <xrpl/tx/Transactor.h>
#include <memory> #include <memory>
#include <optional>
#include <utility> #include <utility>
namespace xrpl { namespace xrpl {
@@ -81,7 +80,7 @@ PermissionedDomainSet::preclaim(PreclaimContext const& ctx)
TER TER
PermissionedDomainSet::doApply() PermissionedDomainSet::doApply()
{ {
auto ownerSle = AccountRootEntryW(accountID_, view(), ctx_.journal); auto const ownerSle = view().peek(keylet::account(accountID_));
if (!ownerSle) if (!ownerSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -110,7 +109,8 @@ PermissionedDomainSet::doApply()
// Create new permissioned domain. // Create new permissioned domain.
// Check reserve availability for new object creation // Check reserve availability for new object creation
auto const balance = STAmount((*ownerSle)[sfBalance]).xrp(); auto const balance = STAmount((*ownerSle)[sfBalance]).xrp();
auto const reserve = ownerSle.reserve({.ownerCountDelta = 1}); auto const reserve =
accountReserve(ctx_.view(), ownerSle, ctx_.journal, {.ownerCountDelta = 1});
if (balance < reserve) if (balance < reserve)
return tecINSUFFICIENT_RESERVE; return tecINSUFFICIENT_RESERVE;
@@ -130,8 +130,7 @@ PermissionedDomainSet::doApply()
slePd->setFieldU64(sfOwnerNode, *page); slePd->setFieldU64(sfOwnerNode, *page);
// If we succeeded, the new entry counts against the creator's reserve. // If we succeeded, the new entry counts against the creator's reserve.
std::optional<AccountRootEntryW> noSponsor; increaseOwnerCount(view(), ownerSle, {}, 1, ctx_.journal);
ownerSle.increaseOwnerCount(noSponsor, 1);
view().insert(slePd); view().insert(slePd);
} }
@@ -4,7 +4,6 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h> #include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/ProposalHelpers.h> #include <xrpl/ledger/helpers/ProposalHelpers.h>
@@ -55,8 +54,8 @@ TransactionProposalCreate::preflight(PreflightContext const& ctx)
try try
{ {
STTx const stx{STObject{proposedTx}}; STTx const stx{STObject{proposedTx}};
auto const inner = auto const inner = xrpl::preflight(
xrpl::preflight(ctx.registry, ctx.rules, stx, TapDryRun | TapProposal, ctx.j); ctx.registry, ctx.rules, stx, ApplyFlags::DryRun | ApplyFlags::Proposal, ctx.j);
if (!isTesSuccess(inner.ter)) if (!isTesSuccess(inner.ter))
{ {
JLOG(ctx.j.debug()) << "TransactionProposalCreate: proposed txn " JLOG(ctx.j.debug()) << "TransactionProposalCreate: proposed txn "
@@ -160,7 +159,7 @@ TransactionProposalCreate::preclaim(PreclaimContext const& ctx)
} }
AccountID const target = proposedTx.getAccountID(sfAccount); AccountID const target = proposedTx.getAccountID(sfAccount);
auto const sleTarget = AccountRootEntryR(target, ctx.view); auto const sleTarget = ctx.view.read(keylet::account(target));
if (!sleTarget) if (!sleTarget)
{ {
JLOG(ctx.j.debug()) << "TransactionProposalCreate: target account " JLOG(ctx.j.debug()) << "TransactionProposalCreate: target account "
@@ -298,7 +297,7 @@ TransactionProposalCreate::preclaim(PreclaimContext const& ctx)
TER TER
TransactionProposalCreate::doApply() TransactionProposalCreate::doApply()
{ {
auto sle = AccountRootEntryW(accountID_, view(), ctx_.journal); auto const sle = view().peek(keylet::account(accountID_));
if (!sle) if (!sle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -308,10 +307,12 @@ TransactionProposalCreate::doApply()
// The proposal holds a full transaction plus its collected signatures, so // The proposal holds a full transaction plus its collected signatures, so
// it reserves more than a typical ledger entry (5 increments; 10 for a // it reserves more than a typical ledger entry (5 increments; 10 for a
// proposed Batch). // proposed Batch).
if (auto const ret = sle.checkReserve( if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sle,
preFeeBalance_, preFeeBalance_,
{.ownerCountDelta = static_cast<int>(ownerCount)}); {.ownerCountDelta = static_cast<int>(ownerCount)},
ctx_.journal);
!isTesSuccess(ret)) !isTesSuccess(ret))
return ret; return ret;
@@ -326,6 +327,7 @@ TransactionProposalCreate::doApply()
view().insert(sleProposal); view().insert(sleProposal);
auto viewJ = ctx_.registry.get().getJournal("View");
{ {
auto const page = view().dirInsert( auto const page = view().dirInsert(
keylet::ownerDir(accountID_), proposalKeylet, describeOwnerDir(accountID_)); keylet::ownerDir(accountID_), proposalKeylet, describeOwnerDir(accountID_));
@@ -334,7 +336,7 @@ TransactionProposalCreate::doApply()
sleProposal->setFieldU64(sfOwnerNode, *page); sleProposal->setFieldU64(sfOwnerNode, *page);
} }
sle.increaseOwnerCount(ctx_.getApplyViewContext(), ownerCount); increaseOwnerCount(ctx_.getApplyViewContext(), sle, ownerCount, viewJ);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleProposal); addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleProposal);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -7,7 +7,6 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h> #include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -165,11 +164,11 @@ SponsorshipSet::preclaim(PreclaimContext const& ctx)
if (sponseeID == sponsorID) if (sponseeID == sponsorID)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto const sponsorAccSle = AccountRootEntryR(sponsorID, ctx.view); auto const sponsorAccSle = ctx.view.read(keylet::account(sponsorID));
if (!sponsorAccSle) if (!sponsorAccSle)
return tecNO_DST; return tecNO_DST;
auto const sponseeSle = AccountRootEntryR(sponseeID, ctx.view); auto const sponseeSle = ctx.view.read(keylet::account(sponseeID));
if (!sponseeSle) if (!sponseeSle)
return tecNO_DST; return tecNO_DST;
@@ -213,7 +212,7 @@ deleteSponsorship(ApplyView& view, SLE::Ref sle, beast::Journal j)
// The sponsor owns the Sponsorship object, so deletion releases the // The sponsor owns the Sponsorship object, so deletion releases the
// sponsor's owner reserve. // sponsor's owner reserve.
auto sponsorAccSle = AccountRootEntryW(sponsorID, view); auto sponsorAccSle = view.peek(keylet::account(sponsorID));
if (!sponsorAccSle) if (!sponsorAccSle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -238,7 +237,7 @@ deleteSponsorship(ApplyView& view, SLE::Ref sle, beast::Journal j)
if (sle->isFieldPresent(sfFeeAmount)) if (sle->isFieldPresent(sfFeeAmount))
{ {
(*sponsorAccSle)[sfBalance] += sle->getFieldAmount(sfFeeAmount); (*sponsorAccSle)[sfBalance] += sle->getFieldAmount(sfFeeAmount);
sponsorAccSle.update(); view.update(sponsorAccSle);
} }
view.erase(sle); view.erase(sle);
@@ -251,8 +250,8 @@ SponsorshipSet::createSponsorship(
Keylet const& sponsorshipKeylet, Keylet const& sponsorshipKeylet,
AccountID const& sponsorID, AccountID const& sponsorID,
AccountID const& sponseeID, AccountID const& sponseeID,
AccountRootEntryW& sponsorAccSle, SLE::Ref sponsorAccSle,
std::optional<AccountRootEntryW>& reserveSponsorAccSle) SLE::Ref reserveSponsorAccSle)
{ {
auto const feeAmountDelta = ctx_.tx[~sfFeeAmountDelta]; auto const feeAmountDelta = ctx_.tx[~sfFeeAmountDelta];
auto const maxFee = ctx_.tx[~sfMaxFee]; auto const maxFee = ctx_.tx[~sfMaxFee];
@@ -277,11 +276,13 @@ SponsorshipSet::createSponsorship(
if (hasPositiveFeeAmount) if (hasPositiveFeeAmount)
sponsorBalanceAfterFee -= *feeAmountDelta; sponsorBalanceAfterFee -= *feeAmountDelta;
if (auto const ret = sponsorAccSle.checkReserve( if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponsorAccSle,
sponsorBalanceAfterFee.xrp(), sponsorBalanceAfterFee.xrp(),
reserveSponsorAccSle, reserveSponsorAccSle,
{.ownerCountDelta = 1}, {.ownerCountDelta = 1},
ctx_.journal,
tecUNFUNDED); tecUNFUNDED);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
@@ -322,9 +323,8 @@ SponsorshipSet::createSponsorship(
(*newSle)[sfSponseeNode] = *sponseePage; (*newSle)[sfSponseeNode] = *sponseePage;
// NOLINTNEXTLINE(readability-suspicious-call-argument) // NOLINTNEXTLINE(readability-suspicious-call-argument)
sponsorAccSle.increaseOwnerCount(reserveSponsorAccSle, 1); increaseOwnerCount(view(), sponsorAccSle, reserveSponsorAccSle, 1, ctx_.journal);
if (reserveSponsorAccSle.has_value()) addSponsorToLedgerEntry(newSle, reserveSponsorAccSle);
addSponsorToLedgerEntry(newSle, reserveSponsorAccSle->rawSle());
ctx_.view().insert(newSle); ctx_.view().insert(newSle);
return tesSUCCESS; return tesSUCCESS;
@@ -339,7 +339,7 @@ SponsorshipSet::doApply()
if (sponseeID == sponsorID) if (sponseeID == sponsorID)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
auto sponsorAccSle = AccountRootEntryW(sponsorID, ctx_.view(), ctx_.journal); auto const sponsorAccSle = ctx_.view().peek(keylet::account(sponsorID));
if (!sponsorAccSle) if (!sponsorAccSle)
return tecINTERNAL; // LCOV_EXCL_LINE return tecINTERNAL; // LCOV_EXCL_LINE
@@ -388,11 +388,13 @@ SponsorshipSet::doApply()
// object. // object.
(*sponsorAccSle)[sfBalance] -= actualDelta; (*sponsorAccSle)[sfBalance] -= actualDelta;
if (auto const ret = sponsorAccSle.checkReserve( if (auto const ret = checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponsorAccSle,
(*sponsorAccSle)[sfBalance]->xrp(), (*sponsorAccSle)[sfBalance]->xrp(),
*reserveSponsorAccSle, *reserveSponsorAccSle,
{}, {},
ctx_.journal,
tecUNFUNDED); tecUNFUNDED);
!isTesSuccess(ret)) !isTesSuccess(ret))
{ {
@@ -411,7 +413,7 @@ SponsorshipSet::doApply()
{ {
(*sponsorshipSle)[sfFeeAmount] = newFee; (*sponsorshipSle)[sfFeeAmount] = newFee;
} }
sponsorAccSle.update(); ctx_.view().update(sponsorAccSle);
} }
if (maxFee) if (maxFee)
@@ -7,7 +7,7 @@
#include <xrpl/core/ServiceRegistry.h> #include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h> #include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h> #include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AccountRootEntry.h> #include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h> #include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -20,13 +20,17 @@
#include <bit> #include <bit>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include <optional> #include <memory>
namespace xrpl { namespace xrpl {
// Increment an uint32 sponsor count field and update the SLE. // Increment an uint32 sponsor count field and update the SLE.
static TER static TER
incrementSponsorCount(AccountRootEntryW& sle, SF_UINT32 const& field, std::uint32_t const delta) incrementSponsorCount(
ApplyView& view,
SLE::Ref sle,
SF_UINT32 const& field,
std::uint32_t const delta)
{ {
auto const currentValue = sle->getFieldU32(field); auto const currentValue = sle->getFieldU32(field);
if (std::numeric_limits<std::uint32_t>::max() - currentValue < delta) if (std::numeric_limits<std::uint32_t>::max() - currentValue < delta)
@@ -38,13 +42,17 @@ incrementSponsorCount(AccountRootEntryW& sle, SF_UINT32 const& field, std::uint3
} }
sle->at(field) = currentValue + delta; sle->at(field) = currentValue + delta;
sle.update(); view.update(sle);
return tesSUCCESS; return tesSUCCESS;
} }
// Decrement an uint32 sponsor count field and update the SLE. // Decrement an uint32 sponsor count field and update the SLE.
static TER static TER
decrementSponsorCount(AccountRootEntryW& sle, SF_UINT32 const& field, std::uint32_t const delta) decrementSponsorCount(
ApplyView& view,
SLE::Ref sle,
SF_UINT32 const& field,
std::uint32_t const delta)
{ {
auto const currentValue = sle->getFieldU32(field); auto const currentValue = sle->getFieldU32(field);
if (currentValue < delta) if (currentValue < delta)
@@ -56,7 +64,7 @@ decrementSponsorCount(AccountRootEntryW& sle, SF_UINT32 const& field, std::uint3
} }
sle->at(field) = currentValue - delta; sle->at(field) = currentValue - delta;
sle.update(); view.update(sle);
return tesSUCCESS; return tesSUCCESS;
} }
@@ -197,7 +205,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx)
// is collected on-ledger afterward, so its absence here is expected, not // is collected on-ledger afterward, so its absence here is expected, not
// an error (On-Chain Cosigner spec §5.3.1.2). // an error (On-Chain Cosigner spec §5.3.1.2).
if (isAccountReserveSponsorship && !ctx.tx.isFieldPresent(sfSponsorSignature) && if (isAccountReserveSponsorship && !ctx.tx.isFieldPresent(sfSponsorSignature) &&
(ctx.flags & TapProposal) == 0) (ctx.flags & ApplyFlags::Proposal) == ApplyFlags::None)
{ {
JLOG(ctx.j.debug()) << "preflight: account sponsorship requires sfSponsorSignature"; JLOG(ctx.j.debug()) << "preflight: account sponsorship requires sfSponsorSignature";
return temMALFORMED; return temMALFORMED;
@@ -224,7 +232,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
auto const account = ctx.tx[sfAccount]; auto const account = ctx.tx[sfAccount];
auto const sponseeID = ctx.tx[~sfSponsee].value_or(account); auto const sponseeID = ctx.tx[~sfSponsee].value_or(account);
auto const sponseeSle = AccountRootEntryR(sponseeID, ctx.view); auto const sponseeSle = ctx.view.read(keylet::account(sponseeID));
if (!sponseeSle) if (!sponseeSle)
{ {
// If it is ending sponsorship, sfSponsee is user input, return terNO_ACCOUNT if it does not // If it is ending sponsorship, sfSponsee is user input, return terNO_ACCOUNT if it does not
@@ -240,7 +248,7 @@ SponsorshipTransfer::preclaim(PreclaimContext const& ctx)
// Default setup with an account sponsorship transfer. If it is an object transfer, they will be // Default setup with an account sponsorship transfer. If it is an object transfer, they will be
// overridden to the object SLE and its type-specific sponsor field: // overridden to the object SLE and its type-specific sponsor field:
// sfHighSponsor/sfLowSponsor for a RippleState, sfSponsor for other object types. // sfHighSponsor/sfLowSponsor for a RippleState, sfSponsor for other object types.
SLE::const_pointer targetSle = sponseeSle.rawSle(); SLE::const_pointer targetSle = sponseeSle;
auto const* sponsorField = &sfSponsor; auto const* sponsorField = &sfSponsor;
if (objectID.has_value()) if (objectID.has_value())
@@ -305,7 +313,7 @@ SponsorshipTransfer::doApply()
auto const objectID = ctx_.tx[~sfObjectID]; auto const objectID = ctx_.tx[~sfObjectID];
auto const sponseeID = ctx_.tx[~sfSponsee].value_or(accountID_); auto const sponseeID = ctx_.tx[~sfSponsee].value_or(accountID_);
auto sponseeSle = AccountRootEntryW(sponseeID, view(), ctx_.journal); auto const sponseeSle = view().peek(keylet::account(sponseeID));
if (!sponseeSle) if (!sponseeSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -329,7 +337,7 @@ SponsorshipTransfer::doApply()
if (!isLedgerEntryOwner(view(), *objectSle, sponseeID)) if (!isLedgerEntryOwner(view(), *objectSle, sponseeID))
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto ownerSle = AccountRootEntryW(sponseeID, view()); auto const ownerSle = view().peek(keylet::account(sponseeID));
if (!ownerSle) if (!ownerSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -347,25 +355,27 @@ SponsorshipTransfer::doApply()
if (!newSponsor) if (!newSponsor)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const newSponsorID = *newSponsor; auto const newSponsorID = *newSponsor;
auto newSponsorSle = AccountRootEntryW(newSponsorID, view()); auto const newSponsorSle = view().peek(keylet::account(newSponsorID));
if (!newSponsorSle) if (!newSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Check if new sponsor has sufficient balance // Check if new sponsor has sufficient balance
// NOLINTNEXTLINE(readability-suspicious-call-argument) // NOLINTNEXTLINE(readability-suspicious-call-argument)
if (auto const ter = sponseeSle.checkReserve( if (auto const ter = checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponseeSle,
sponseeSle->getFieldAmount(sfBalance).xrp(), sponseeSle->getFieldAmount(sfBalance).xrp(),
std::optional<AccountRootEntryR>{newSponsorSle}, newSponsorSle,
{.ownerCountDelta = ownerCountDelta}); {.ownerCountDelta = ownerCountDelta},
ctx_.journal);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; return ter;
if (isCreate) if (isCreate)
{ {
// Update owner's sponsored count // Update owner's sponsored count
if (auto const ter = if (auto const ter = incrementSponsorCount(
incrementSponsorCount(ownerSle, sfSponsoredOwnerCount, ownerCountDelta); view(), ownerSle, sfSponsoredOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
@@ -374,20 +384,20 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = objectSle->getAccountID(sponsorField); auto const oldSponsorID = objectSle->getAccountID(sponsorField);
if (!oldSponsorID) if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view()); auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
if (!oldSponsorSle) if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Decrement old sponsor's sponsoring count // Decrement old sponsor's sponsoring count
if (auto const ter = decrementSponsorCount( if (auto const ter = decrementSponsorCount(
oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
// Increment new sponsor's sponsoring count // Increment new sponsor's sponsoring count
if (auto const ter = if (auto const ter = incrementSponsorCount(
incrementSponsorCount(newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); view(), newSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
@@ -411,7 +421,7 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = objectSle->getAccountID(sponsorField); auto const oldSponsorID = objectSle->getAccountID(sponsorField);
if (!oldSponsorID) if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view()); auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
if (!oldSponsorSle) if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
@@ -423,24 +433,26 @@ SponsorshipTransfer::doApply()
// steps in (Reassign), or the object/account is deleted. // steps in (Reassign), or the object/account is deleted.
if (view().rules().enabled(fixCleanup3_4_0)) if (view().rules().enabled(fixCleanup3_4_0))
{ {
if (auto const ter = sponseeSle.checkReserve( if (auto const ter = checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
balanceBeforeFee(sponseeSle.rawSle()), sponseeSle,
std::nullopt, balanceBeforeFee(sponseeSle),
{.ownerCountDelta = ownerCountDelta}); SLE::pointer(),
{.ownerCountDelta = ownerCountDelta},
ctx_.journal);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; return ter;
} }
// Decrement sponsored count // Decrement sponsored count
if (auto const ter = if (auto const ter = decrementSponsorCount(
decrementSponsorCount(sponseeSle, sfSponsoredOwnerCount, ownerCountDelta); view(), sponseeSle, sfSponsoredOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
// Decrement old sponsoring count // Decrement old sponsoring count
if (auto const ter = if (auto const ter = decrementSponsorCount(
decrementSponsorCount(oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta); view(), oldSponsorSle, sfSponsoringOwnerCount, ownerCountDelta);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
@@ -463,15 +475,17 @@ SponsorshipTransfer::doApply()
if (!newSponsor) if (!newSponsor)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto const newSponsorID = *newSponsor; auto const newSponsorID = *newSponsor;
auto newSponsorSle = AccountRootEntryW(newSponsorID, view()); auto const newSponsorSle = view().peek(keylet::account(newSponsorID));
if (!newSponsorSle) if (!newSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ter = sponseeSle.checkReserve( if (auto const ter = checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
sponseeSle,
sponseeSle->getFieldAmount(sfBalance).xrp(), sponseeSle->getFieldAmount(sfBalance).xrp(),
std::optional<AccountRootEntryR>{newSponsorSle}, newSponsorSle,
{.accountCountDelta = 1}); {.accountCountDelta = 1},
ctx_.journal);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; return ter;
@@ -480,25 +494,26 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor); auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
if (!oldSponsorID) if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view()); auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
if (!oldSponsorSle) if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// Decrement old sponsoring count // Decrement old sponsoring count
if (auto const ter = if (auto const ter =
decrementSponsorCount(oldSponsorSle, sfSponsoringAccountCount, 1); decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
} }
// Increment new sponsoring count // Increment new sponsoring count
if (auto const ter = incrementSponsorCount(newSponsorSle, sfSponsoringAccountCount, 1); if (auto const ter =
incrementSponsorCount(view(), newSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE return ter; // LCOV_EXCL_LINE
// Account is now sponsored by new sponsor // Account is now sponsored by new sponsor
sponseeSle->setAccountID(sfSponsor, newSponsorID); sponseeSle->setAccountID(sfSponsor, newSponsorID);
sponseeSle.update(); view().update(sponseeSle);
} }
else if (ctx_.tx.isFlag(tfSponsorshipEnd)) else if (ctx_.tx.isFlag(tfSponsorshipEnd))
{ {
@@ -506,25 +521,28 @@ SponsorshipTransfer::doApply()
auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor); auto const oldSponsorID = sponseeSle->getAccountID(sfSponsor);
if (!oldSponsorID) if (!oldSponsorID)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
auto oldSponsorSle = AccountRootEntryW(oldSponsorID, view()); auto const oldSponsorSle = view().peek(keylet::account(oldSponsorID));
if (!oldSponsorSle) if (!oldSponsorSle)
return tefINTERNAL; // LCOV_EXCL_LINE return tefINTERNAL; // LCOV_EXCL_LINE
// The sponsee must be able to hold its own account reserve after // The sponsee must be able to hold its own account reserve after
// the sponsorship is removed. // the sponsorship is removed.
if (auto const ter = sponseeSle.checkReserve( if (auto const ter = checkReserve(
ctx_.getApplyViewContext(), ctx_.getApplyViewContext(),
balanceBeforeFee(sponseeSle.rawSle()), sponseeSle,
std::optional<AccountRootEntryR>{}, balanceBeforeFee(sponseeSle),
{.accountCountDelta = 1}); SLE::pointer(),
{.accountCountDelta = 1},
ctx_.journal);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; return ter;
sponseeSle->makeFieldAbsent(sfSponsor); sponseeSle->makeFieldAbsent(sfSponsor);
sponseeSle.update(); view().update(sponseeSle);
// Decrement account sponsoring count // Decrement account sponsoring count
if (auto const ter = decrementSponsorCount(oldSponsorSle, sfSponsoringAccountCount, 1); if (auto const ter =
decrementSponsorCount(view(), oldSponsorSle, sfSponsoringAccountCount, 1);
!isTesSuccess(ter)) !isTesSuccess(ter))
return ter; // LCOV_EXCL_LINE 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