mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-21 19:18:22 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16389dcb2e | ||
|
|
97a780b29f | ||
|
|
654edc9a08 | ||
|
|
658d04f694 | ||
|
|
93498952b1 | ||
|
|
269f79e568 | ||
|
|
e7a8e0336a | ||
|
|
1d7e40777a | ||
|
|
7bb43a243b | ||
|
|
d697b7843c | ||
|
|
2de5140f20 | ||
|
|
edf085a697 | ||
|
|
c35ac1339b | ||
|
|
dc1cfbc474 | ||
|
|
3044073401 |
No files matched your search
@@ -30,11 +30,6 @@ Version 3.5.0 is not yet released.
|
||||
|
||||
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive a message with `type` `mptTransaction` for each validated transaction whose metadata affects a subscribed issuance; the message has the same fields as the `transactions` stream. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
|
||||
|
||||
### Bugfixes in 3.5.0
|
||||
|
||||
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
|
||||
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
|
||||
|
||||
## XRP Ledger server version 3.4.0
|
||||
|
||||
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace xrpl::ledger_entries {
|
||||
// builder's STObject and the wrapper's SLE.
|
||||
TEST(${name}Tests, BuilderSettersRoundTrip)
|
||||
{
|
||||
constexpr UInt256 index{1};
|
||||
UInt256 const index{1u};
|
||||
|
||||
% for field in fields:
|
||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||
@@ -85,7 +85,7 @@ TEST(${name}Tests, BuilderSettersRoundTrip)
|
||||
// from that SLE, build a new wrapper, and verify all fields (and validate()).
|
||||
TEST(${name}Tests, BuilderFromSleRoundTrip)
|
||||
{
|
||||
constexpr UInt256 index{2};
|
||||
UInt256 const index{2u};
|
||||
|
||||
% for field in fields:
|
||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||
@@ -146,7 +146,7 @@ TEST(${name}Tests, BuilderFromSleRoundTrip)
|
||||
// 3) Verify wrapper throws when constructed from wrong ledger entry type.
|
||||
TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
|
||||
{
|
||||
constexpr UInt256 index{3};
|
||||
UInt256 const index{3u};
|
||||
|
||||
// Build a valid ledger entry of a different type
|
||||
// Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq
|
||||
@@ -177,7 +177,7 @@ TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
|
||||
// 4) Verify builder throws when constructed from wrong ledger entry type.
|
||||
TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
|
||||
{
|
||||
constexpr UInt256 index{4};
|
||||
UInt256 const index{4u};
|
||||
|
||||
// Build a valid ledger entry of a different type
|
||||
% if wrong_le_include == "Ticket":
|
||||
@@ -207,7 +207,7 @@ TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
|
||||
// 5) Build with only required fields and verify optional fields return nullopt.
|
||||
TEST(${name}Tests, OptionalFieldsReturnNullopt)
|
||||
{
|
||||
constexpr UInt256 index{3};
|
||||
UInt256 const index{3u};
|
||||
|
||||
% for field in required_fields:
|
||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||
|
||||
Generated
+6
-6
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1791130267,
|
||||
"narHash": "sha256-1sjwcQMcgAbmiip/VivBSiK8p2bTkQGOvCuO7vnwfx4=",
|
||||
"lastModified": 1781173989,
|
||||
"narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9013764fcc0ea99fa16cf7aa7decf8a5c3889dd2",
|
||||
"rev": "8c91a71d13451abc40eb9dae8910f972f979852f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -47,11 +47,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1791189933,
|
||||
"narHash": "sha256-Y8eN7ki0Urx4Ojf4w1xSpWg8uk/3lJqK+E0Oth77rH0=",
|
||||
"lastModified": 1784611586,
|
||||
"narHash": "sha256-OfqgY+0hp/zseZB7uyH0U8kIDPS4scZZCyAurEplvG0=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "e60029353d0c48d216bc4b065168ccd4079c166f",
|
||||
"rev": "14f58845249f3552a89b07772626b8d3c632fa86",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
|
||||
#include <boost/core/type_name.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Manages all counted object types.
|
||||
*
|
||||
* Counters register themselves on a lock-free intrusive list maintained
|
||||
* by this object when constructed. Because counters are never destroyed
|
||||
* or removed, the ABA problem does not apply.
|
||||
*
|
||||
* The registry is iterable as a forward range.
|
||||
*/
|
||||
class CountedObjects
|
||||
{
|
||||
public:
|
||||
static CountedObjects&
|
||||
getInstance() noexcept;
|
||||
|
||||
using Entry = std::pair<std::string, int>;
|
||||
using List = std::vector<Entry>;
|
||||
|
||||
[[nodiscard]] List
|
||||
getCounts(int minimumThreshold) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Implementation for @ref CountedObject.
|
||||
@@ -35,147 +33,68 @@ public:
|
||||
class Counter
|
||||
{
|
||||
public:
|
||||
Counter(std::string name) noexcept;
|
||||
Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
|
||||
{
|
||||
// Insert ourselves at the front of the lock-free linked list
|
||||
CountedObjects& instance = CountedObjects::getInstance();
|
||||
Counter* head = nullptr;
|
||||
|
||||
// Counters are intrusive list nodes whose addresses are published
|
||||
// in the registry; they must never be copied or moved. The atomic
|
||||
// members already force this, but we make it explicit.
|
||||
Counter(Counter const&) = delete;
|
||||
Counter&
|
||||
operator=(Counter const&) = delete;
|
||||
Counter(Counter&&) = delete;
|
||||
Counter&
|
||||
operator=(Counter&&) = delete;
|
||||
do
|
||||
{
|
||||
head = instance.head_.load();
|
||||
next_ = head;
|
||||
} while (instance.head_.exchange(this) != head);
|
||||
|
||||
std::uint32_t
|
||||
++instance.count_;
|
||||
}
|
||||
|
||||
~Counter() noexcept = default;
|
||||
|
||||
int
|
||||
increment() noexcept
|
||||
{
|
||||
auto const newCount = count_.fetch_add(1, std::memory_order::relaxed) + 1;
|
||||
XRPL_ASSERT(newCount != 0, "xrpl::CountedObjects::Counter::increment : no overflow");
|
||||
|
||||
auto maxCount = maxCount_.load(std::memory_order::relaxed);
|
||||
|
||||
while (newCount > maxCount &&
|
||||
!maxCount_.compare_exchange_weak(maxCount, newCount, std::memory_order::relaxed))
|
||||
{
|
||||
}
|
||||
|
||||
return newCount;
|
||||
return ++count_;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
int
|
||||
decrement() noexcept
|
||||
{
|
||||
auto const prev = count_.fetch_sub(1, std::memory_order::relaxed);
|
||||
XRPL_ASSERT(prev != 0, "xrpl::CountedObjects::Counter::decrement : no underflow");
|
||||
return prev - 1;
|
||||
return --count_;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint32_t
|
||||
count() const noexcept
|
||||
[[nodiscard]] int
|
||||
getCount() const noexcept
|
||||
{
|
||||
return count_.load(std::memory_order::relaxed);
|
||||
return count_.load();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint32_t
|
||||
max() const noexcept
|
||||
[[nodiscard]] Counter*
|
||||
getNext() const noexcept
|
||||
{
|
||||
return std::max(
|
||||
count_.load(std::memory_order::relaxed),
|
||||
maxCount_.load(std::memory_order::relaxed));
|
||||
return next_;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string const&
|
||||
name() const noexcept
|
||||
getName() const noexcept
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class CountedObjects;
|
||||
|
||||
Counter* next_ = nullptr;
|
||||
std::atomic<std::uint32_t> count_ = 0;
|
||||
std::atomic<std::uint32_t> maxCount_ = 0;
|
||||
std::string const name_;
|
||||
std::atomic<int> count_;
|
||||
Counter* next_;
|
||||
};
|
||||
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
using value_type = Counter const;
|
||||
using reference = value_type&;
|
||||
using pointer = value_type*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
explicit Iterator(Counter* c = nullptr) noexcept : current_(c)
|
||||
{
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const noexcept
|
||||
{
|
||||
return *current_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const noexcept
|
||||
{
|
||||
return current_;
|
||||
}
|
||||
|
||||
Iterator&
|
||||
operator++() noexcept
|
||||
{
|
||||
current_ = current_->next_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator
|
||||
operator++(int) noexcept
|
||||
{
|
||||
auto tmp = *this;
|
||||
++*this;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(Iterator const&) const noexcept = default;
|
||||
|
||||
private:
|
||||
Counter* current_;
|
||||
};
|
||||
|
||||
constexpr CountedObjects() noexcept = default;
|
||||
|
||||
[[nodiscard]] auto
|
||||
begin() const noexcept
|
||||
{
|
||||
return Iterator{head_.load(std::memory_order::acquire)};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto
|
||||
end() const noexcept
|
||||
{
|
||||
return Iterator{};
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<Counter*> head_ = nullptr;
|
||||
CountedObjects() noexcept;
|
||||
~CountedObjects() noexcept = default;
|
||||
|
||||
private:
|
||||
std::atomic<int> count_;
|
||||
std::atomic<Counter*> head_;
|
||||
};
|
||||
|
||||
/** The global counted object registry. */
|
||||
inline constinit CountedObjects gCountedObjects;
|
||||
|
||||
inline CountedObjects::Counter::Counter(std::string name) noexcept
|
||||
: next_(gCountedObjects.head_.load(std::memory_order::relaxed)), name_(std::move(name))
|
||||
{
|
||||
while (!gCountedObjects.head_.compare_exchange_weak(
|
||||
next_, this, std::memory_order::release, std::memory_order::relaxed))
|
||||
;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -184,37 +103,27 @@ inline CountedObjects::Counter::Counter(std::string name) noexcept
|
||||
* Derived classes have their instances counted automatically. This is used
|
||||
* for reporting purposes.
|
||||
*
|
||||
* The constructors are private and `Object` is befriended so that the
|
||||
* CRTP parameter must be the deriving class itself: a copy-paste error
|
||||
* like `class B : public CountedObject<A>` fails to compile instead of
|
||||
* silently polluting A's count.
|
||||
*
|
||||
* @note This class has no move operations by design: a derived class's
|
||||
* move constructor falls back to the copy constructor for this
|
||||
* base, so the newly created instance is counted. This keeps the
|
||||
* invariant that count is the number of outstanding subobjects.
|
||||
*
|
||||
* @warning Counted objects constructed during dynamic initialization of
|
||||
* other translation units may have their increments discarded when
|
||||
* counter itself is dynamically initialized. Do not create counted
|
||||
* objects before main() begins.
|
||||
*
|
||||
* @ingroup basics
|
||||
*/
|
||||
template <class Object>
|
||||
requires std::is_class_v<Object>
|
||||
class CountedObject
|
||||
{
|
||||
static inline CountedObjects::Counter counter{boost::core::type_name<Object>()};
|
||||
private:
|
||||
static auto&
|
||||
getCounter() noexcept
|
||||
{
|
||||
static CountedObjects::Counter kC{beast::typeName<Object>()};
|
||||
return kC;
|
||||
}
|
||||
|
||||
CountedObject() noexcept
|
||||
{
|
||||
counter.increment();
|
||||
getCounter().increment();
|
||||
}
|
||||
|
||||
CountedObject(CountedObject const&) noexcept
|
||||
{
|
||||
counter.increment();
|
||||
getCounter().increment();
|
||||
}
|
||||
|
||||
CountedObject&
|
||||
@@ -223,7 +132,7 @@ class CountedObject
|
||||
public:
|
||||
~CountedObject() noexcept
|
||||
{
|
||||
counter.decrement();
|
||||
getCounter().decrement();
|
||||
}
|
||||
|
||||
friend Object;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <boost/align.hpp>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <boost/core/type_name.hpp>
|
||||
#include <boost/predef.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
@@ -336,7 +335,7 @@ public:
|
||||
}) != cfg.end())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"SlabAllocatorSet<" + boost::core::type_name<Type>() + ">: duplicate slab size");
|
||||
"SlabAllocatorSet<" + beast::typeName<Type>() + ">: duplicate slab size");
|
||||
}
|
||||
|
||||
for (auto const& c : cfg)
|
||||
|
||||
+429
-461
File diff suppressed because it is too large.
Load diff
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/sanitizers.h>
|
||||
|
||||
#include <boost/core/type_name.hpp>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
@@ -56,7 +55,7 @@ Throw(Args&&... args)
|
||||
std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception.");
|
||||
|
||||
E e(std::forward<Args>(args)...);
|
||||
logThrow("Throwing exception of type " + boost::core::type_name<E>() + ": " + e.what());
|
||||
logThrow(std::string("Throwing exception of type " + beast::typeName<E>() + ": ") + e.what());
|
||||
throw std::move(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Opt-in bitwise operators for scoped enumerations.
|
||||
*
|
||||
* Provides `&`, `|`, `^`, `~` and the corresponding compound assignment forms
|
||||
* for any scoped enumeration with an unsigned underlying type that opts in by
|
||||
* specializing @ref OptIn:
|
||||
*
|
||||
* @code
|
||||
* namespace xrpl {
|
||||
*
|
||||
* enum class MyFlags : std::uint32_t { a = 1, b = 2, c = 4 };
|
||||
*
|
||||
* template <>
|
||||
* struct enum_bitops::OptIn<MyFlags> : std::true_type {};
|
||||
*
|
||||
* } // namespace xrpl
|
||||
* @endcode
|
||||
*
|
||||
* The operators are constexpr and noexcept, and return the enumeration type.
|
||||
*
|
||||
* @par Where the specialization may be declared
|
||||
* In namespace xrpl, or at global scope with full qualification, and after
|
||||
* the enumeration is defined but before the operators are first used. This
|
||||
* cannot be done in a nested namespace or at class scope; for enumerations
|
||||
* nested in a class this means after the class definition.
|
||||
*
|
||||
* @par Where the operators are found
|
||||
* The operators are declared in namespace xrpl, so argument-dependent lookup
|
||||
* will find them only for enumerations declared in xrpl or nested in a class
|
||||
* declared directly in xrpl. Enumerations in a nested namespace will only be
|
||||
* found from code inside xrpl, and only if no enclosing scope declares an
|
||||
* operator of the same name; elsewhere they require using-declarations.
|
||||
*/
|
||||
namespace enum_bitops {
|
||||
|
||||
/**
|
||||
* Types that may be opted in to the bitwise operators.
|
||||
*
|
||||
* Satisfied by a scoped enumeration whose underlying type is an unsigned
|
||||
* integer type.
|
||||
*
|
||||
* @tparam T The type to test.
|
||||
*/
|
||||
template <typename T>
|
||||
concept Eligible = std::is_scoped_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>;
|
||||
|
||||
/**
|
||||
* Opt-in switch for the bitwise operators.
|
||||
*
|
||||
* The primary template derives from std::false_type. Specialize it to derive
|
||||
* from std::true_type to enable the operators for an @ref Eligible enumeration.
|
||||
*
|
||||
* @tparam T The scoped enumeration to opt in.
|
||||
*/
|
||||
template <Eligible T>
|
||||
struct OptIn : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* Enumerations for which the bitwise operators are enabled.
|
||||
*
|
||||
* Satisfied when @p T satisfies @ref Eligible and @ref OptIn has been
|
||||
* specialized for it to derive from std::true_type.
|
||||
*
|
||||
* @tparam T The type to test.
|
||||
*/
|
||||
template <typename T>
|
||||
concept Candidate = Eligible<T> && OptIn<T>::value;
|
||||
|
||||
} // namespace enum_bitops
|
||||
|
||||
// These are declared in xrpl, not in enum_bitops, so that argument-dependent
|
||||
// lookup finds them for enumerations whose associated namespace is xrpl.
|
||||
|
||||
/**
|
||||
* @name Bitwise operators for opted-in scoped enumerations
|
||||
*
|
||||
* Each operator applies the corresponding built-in operator to the
|
||||
* underlying values and converts the result back to the enumeration type.
|
||||
* Both operands must have the same enumeration type; there is no implicit
|
||||
* conversion to or from the underlying type.
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
/**
|
||||
* Bitwise AND.
|
||||
*
|
||||
* @param lhs The left operand.
|
||||
* @param rhs The right operand.
|
||||
* @return The bits set in both @p lhs and @p rhs.
|
||||
*/
|
||||
template <enum_bitops::Candidate T>
|
||||
constexpr T
|
||||
operator&(T lhs, T rhs) noexcept
|
||||
{
|
||||
return static_cast<T>(std::to_underlying(lhs) & std::to_underlying(rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise OR.
|
||||
*
|
||||
* @param lhs The left operand.
|
||||
* @param rhs The right operand.
|
||||
* @return The bits set in @p lhs, in @p rhs, or in both.
|
||||
*/
|
||||
template <enum_bitops::Candidate T>
|
||||
constexpr T
|
||||
operator|(T lhs, T rhs) noexcept
|
||||
{
|
||||
return static_cast<T>(std::to_underlying(lhs) | std::to_underlying(rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise exclusive OR.
|
||||
*
|
||||
* @param lhs The left operand.
|
||||
* @param rhs The right operand.
|
||||
* @return The bits set in exactly one of @p lhs and @p rhs.
|
||||
*/
|
||||
template <enum_bitops::Candidate T>
|
||||
constexpr T
|
||||
operator^(T lhs, T rhs) noexcept
|
||||
{
|
||||
return static_cast<T>(std::to_underlying(lhs) ^ std::to_underlying(rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise complement.
|
||||
*
|
||||
* The result has every bit of the underlying type that is clear in
|
||||
* @p val, including bits that no enumerator names. It is intended for
|
||||
* clearing flags, as in `flags & ~flag`.
|
||||
*
|
||||
* @param val The operand.
|
||||
* @return The complement of @p val.
|
||||
*/
|
||||
template <enum_bitops::Candidate T>
|
||||
constexpr T
|
||||
operator~(T val) noexcept
|
||||
{
|
||||
return static_cast<T>(~std::to_underlying(val));
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise AND assignment.
|
||||
*
|
||||
* @param lhs The value to modify.
|
||||
* @param rhs The right operand.
|
||||
* @return A reference to @p lhs.
|
||||
*/
|
||||
template <enum_bitops::Candidate T>
|
||||
constexpr T&
|
||||
operator&=(T& lhs, T rhs) noexcept
|
||||
{
|
||||
lhs = lhs & rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise OR assignment.
|
||||
*
|
||||
* @param lhs The value to modify.
|
||||
* @param rhs The right operand.
|
||||
* @return A reference to @p lhs.
|
||||
*/
|
||||
template <enum_bitops::Candidate T>
|
||||
constexpr T&
|
||||
operator|=(T& lhs, T rhs) noexcept
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise exclusive OR assignment.
|
||||
*
|
||||
* @param lhs The value to modify.
|
||||
* @param rhs The right operand.
|
||||
* @return A reference to @p lhs.
|
||||
*/
|
||||
template <enum_bitops::Candidate T>
|
||||
constexpr T&
|
||||
operator^=(T& lhs, T rhs) noexcept
|
||||
{
|
||||
lhs = lhs ^ rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
} // namespace xrpl
|
||||
+46
-151
@@ -2,212 +2,107 @@
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Every value of @p Src can be represented by @p Dest.
|
||||
// safe_cast adds compile-time checks to a static_cast to ensure that
|
||||
// the destination can hold all values of the source. This is particularly
|
||||
// handy when the source or destination is an enumeration type.
|
||||
|
||||
Given two integral types, the cast is safe when the destination can
|
||||
hold every possible value of the source.
|
||||
template <class Src, class Dest>
|
||||
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
|
||||
(std::is_signed_v<Src> || std::is_unsigned_v<Dest>) &&
|
||||
(std::is_signed_v<Src> != std::is_signed_v<Dest> ? sizeof(Dest) > sizeof(Src)
|
||||
: sizeof(Dest) >= sizeof(Src));
|
||||
|
||||
Comparing the bounds requires care: we use @c std::cmp_less_equal and
|
||||
@c std::cmp_greater_equal; the plain relational operators would apply
|
||||
arithmetic conversions, resulting in incorrect results when comparing
|
||||
across signedness.
|
||||
|
||||
Because @c std::cmp_* requires standard signed or unsigned integer
|
||||
arguments, which excludes character types and bool, we first widen
|
||||
all type bounds to the maximum-width integer type while preserving
|
||||
signedness.
|
||||
|
||||
@note Extended integer types, like __int128 on gcc, cannot be safely
|
||||
widened to a standard integer type, so the concept will reject
|
||||
them.
|
||||
*/
|
||||
template <typename Src, typename Dest>
|
||||
concept SafeToCast = std::is_integral_v<Src> && std::is_integral_v<Dest> && []() consteval {
|
||||
using WideSrc = std::conditional_t<std::is_signed_v<Src>, std::intmax_t, std::uintmax_t>;
|
||||
using WideDest = std::conditional_t<std::is_signed_v<Dest>, std::intmax_t, std::uintmax_t>;
|
||||
|
||||
// Note: this guard must be an evaluated branch, and not a
|
||||
// static_assert. The lambda body is outside the immediate
|
||||
// context, so a substitution-time failure here would be a
|
||||
// hard error rather than leaving the concept unsatisfied.
|
||||
if constexpr (sizeof(Src) > sizeof(WideSrc) || sizeof(Dest) > sizeof(WideDest))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::cmp_less_equal(
|
||||
static_cast<WideDest>(std::numeric_limits<Dest>::min()),
|
||||
static_cast<WideSrc>(std::numeric_limits<Src>::min())) &&
|
||||
std::cmp_greater_equal(
|
||||
static_cast<WideDest>(std::numeric_limits<Dest>::max()),
|
||||
static_cast<WideSrc>(std::numeric_limits<Src>::max()));
|
||||
}
|
||||
}();
|
||||
|
||||
/** Compile-time-checked static_cast that rejects non-value preserving casts.
|
||||
|
||||
@note There is deliberately no enum-to-enum overload, and no overload
|
||||
returning the underlying type of an enum. For the latter, use
|
||||
@c std::to_underlying.
|
||||
*/
|
||||
/** @{ */
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
safeCast(Src s) noexcept
|
||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||
{
|
||||
static_assert(
|
||||
SafeToCast<Src, Dest>, "This cast is not value-preserving. Please use unsafeCast instead.");
|
||||
std::is_signed_v<Dest> || std::is_unsigned_v<Src>, "Cannot cast signed to unsigned");
|
||||
constexpr unsigned kNotSame = std::is_signed_v<Dest> != std::is_signed_v<Src>;
|
||||
static_assert(
|
||||
sizeof(Dest) >= sizeof(Src) + kNotSame,
|
||||
"Destination is too small to hold all values of source");
|
||||
return static_cast<Dest>(s);
|
||||
}
|
||||
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
safeCast(Src s) noexcept
|
||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||
{
|
||||
return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
|
||||
}
|
||||
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
safeCast(Src s) noexcept
|
||||
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||
{
|
||||
return safeCast<Dest>(std::to_underlying(s));
|
||||
return safeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Integral-to-integral cast that is known to be narrowing or sign-erasing.
|
||||
// unsafe_cast explicitly flags a static_cast as not necessarily able to hold
|
||||
// all values of the source. It includes a compile-time check so that if
|
||||
// underlying types become safe, it can be converted to a safe_cast.
|
||||
|
||||
This explicitly flags a conversion that can lose information for some
|
||||
values of the source type, where the call site accepts that loss (or
|
||||
truncation is the intended behavior).
|
||||
|
||||
The compile-time check ensures the cast remains "unsafe": if the types
|
||||
involved later change such that the conversion becomes inherently
|
||||
value-preserving, the static assertion fires with instructions to
|
||||
migrate the call site to @ref safeCast.
|
||||
|
||||
If the conversion's safety depends on a runtime precondition rather
|
||||
than on the types, or varies across instantiations of generic code,
|
||||
use @ref checkedCast instead.
|
||||
*/
|
||||
/** @{ */
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
unsafeCast(Src s) noexcept
|
||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||
{
|
||||
static_assert(
|
||||
!SafeToCast<Src, Dest>, "This cast is value-preserving. Please use safeCast instead.");
|
||||
!SafeToCast<Src, Dest>,
|
||||
"Only unsafe if casting signed to unsigned or "
|
||||
"destination is too small");
|
||||
return static_cast<Dest>(s);
|
||||
}
|
||||
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
unsafeCast(Src s) noexcept
|
||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||
{
|
||||
return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
|
||||
}
|
||||
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
unsafeCast(Src s) noexcept
|
||||
{
|
||||
return unsafeCast<Dest>(std::to_underlying(s));
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Integral-to-integral cast when the caller has performed a bounds check.
|
||||
|
||||
This documents that a runtime precondition or external invariant, which
|
||||
is not necessarily visible to the compiler, guarantees that the requested
|
||||
conversion is value-preserving for the values that can actually occur.
|
||||
|
||||
This is primarily meant for generic code, where the same expression may
|
||||
be value-preserving for one instantiation but not for another, making
|
||||
both @ref safeCast and @ref unsafeCast unusable.
|
||||
|
||||
Unlike @ref safeCast and @ref unsafeCast, this imposes no static check
|
||||
on the type relationship: the caller's claim is about runtime values,
|
||||
not about types.
|
||||
*/
|
||||
/** @{ */
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||
constexpr Dest
|
||||
checkedCast(Src s) noexcept
|
||||
{
|
||||
return static_cast<Dest>(s);
|
||||
}
|
||||
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||
constexpr Dest
|
||||
checkedCast(Src s) noexcept
|
||||
{
|
||||
return static_cast<Dest>(checkedCast<std::underlying_type_t<Dest>>(s));
|
||||
}
|
||||
|
||||
template <typename Dest, typename Src>
|
||||
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||
constexpr Dest
|
||||
checkedCast(Src s) noexcept
|
||||
{
|
||||
return checkedCast<Dest>(std::to_underlying(s));
|
||||
return unsafeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Downcast within a class hierarchy, verified in debug builds.
|
||||
|
||||
Performs a static_cast down a hierarchy, but in debug builds verifies
|
||||
via dynamic_cast that the object's dynamic type actually permits the
|
||||
downcast. Both build modes execute the same conversion; debug builds
|
||||
merely add the check.
|
||||
|
||||
The pointer form passes null through unchanged, as dynamic_cast does.
|
||||
|
||||
@note The check requires @p Src to be polymorphic; in release builds
|
||||
an invalid downcast is undefined behavior on use, exactly as
|
||||
with a bare static_cast.
|
||||
*/
|
||||
/** @{ */
|
||||
template <class Dest, class Src>
|
||||
requires(
|
||||
std::is_pointer_v<Dest> && std::is_polymorphic_v<Src> &&
|
||||
std::derived_from<std::remove_pointer_t<Dest>, Src> &&
|
||||
(std::is_const_v<std::remove_pointer_t<Dest>> || !std::is_const_v<Src>) &&
|
||||
(std::is_volatile_v<std::remove_pointer_t<Dest>> || !std::is_volatile_v<Src>))
|
||||
requires std::is_pointer_v<Dest>
|
||||
inline Dest
|
||||
safeDowncast(Src* s) noexcept
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
s == nullptr || dynamic_cast<Dest>(s) != nullptr, "xrpl::safeDowncast : valid downcast");
|
||||
#ifdef NDEBUG
|
||||
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
#else
|
||||
auto* result = dynamic_cast<Dest>(s);
|
||||
XRPL_ASSERT(result != nullptr, "xrpl::safeDowncast : pointer downcast is valid");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
requires(
|
||||
std::is_lvalue_reference_v<Dest> && std::is_polymorphic_v<Src> &&
|
||||
std::derived_from<std::remove_reference_t<Dest>, Src>)
|
||||
requires std::is_lvalue_reference_v<Dest>
|
||||
inline Dest
|
||||
safeDowncast(Src& s) noexcept
|
||||
{
|
||||
return *safeDowncast<std::add_pointer_t<std::remove_reference_t<Dest>>>(std::addressof(s));
|
||||
#ifndef NDEBUG
|
||||
XRPL_ASSERT(
|
||||
dynamic_cast<std::add_pointer_t<std::remove_reference_t<Dest>>>(&s) != nullptr,
|
||||
"xrpl::safeDowncast : reference downcast is valid");
|
||||
#endif
|
||||
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
}
|
||||
/** @} */
|
||||
|
||||
} // namespace xrpl
|
||||
+152
-208
@@ -4,215 +4,88 @@
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <boost/predef/architecture.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#if BOOST_ARCH_X86
|
||||
#ifndef __aarch64__
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** An unsigned integral type suitable for use as a spinlock.
|
||||
|
||||
The type must be always lock-free when wrapped in std::atomic, so
|
||||
that lock operations cannot themselves take a (library-level) lock.
|
||||
*/
|
||||
template <typename T>
|
||||
concept SpinlockValueType = std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free;
|
||||
|
||||
/** A spinlock value type that additionally supports the atomic bitwise
|
||||
operations required to pack multiple locks into a single integer.
|
||||
*/
|
||||
template <typename T>
|
||||
concept PackedSpinlockValueType = SpinlockValueType<T> && requires(std::atomic<T>& a, T v) {
|
||||
{ a.fetch_or(v) } -> std::same_as<T>;
|
||||
{ a.fetch_and(v) } -> std::same_as<T>;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** Inform the processor that we are in a tight spin-wait loop.
|
||||
|
||||
Spinlocks caught in tight loops can result in the processor's pipeline
|
||||
filling up with comparison operations, resulting in a misprediction at
|
||||
the time the lock is finally acquired, necessitating pipeline flushing
|
||||
which is ridiculously expensive and results in very high latency.
|
||||
|
||||
This function instructs the processor to "pause" for some architecture
|
||||
specific amount of time, to prevent this.
|
||||
/**
|
||||
* Inform the processor that we are in a tight spin-wait loop.
|
||||
*
|
||||
* Spinlocks caught in tight loops can result in the processor's pipeline
|
||||
* filling up with comparison operations, resulting in a misprediction at
|
||||
* the time the lock is finally acquired, necessitating pipeline flushing
|
||||
* which is ridiculously expensive and results in very high latency.
|
||||
*
|
||||
* This function instructs the processor to "pause" for some architecture
|
||||
* specific amount of time, to prevent this.
|
||||
*/
|
||||
inline void
|
||||
spinPause() noexcept
|
||||
{
|
||||
#if BOOST_ARCH_X86
|
||||
_mm_pause();
|
||||
#elif BOOST_ARCH_ARM
|
||||
asm volatile("yield" ::: "memory");
|
||||
#ifdef __aarch64__
|
||||
asm volatile("yield");
|
||||
#else
|
||||
#error No implementation available for spinPause to use
|
||||
_mm_pause();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Attempt to acquire a spinlock without blocking.
|
||||
|
||||
@note This interface is primarily intended for one-shot attempts to
|
||||
acquire the lock. Avoid calling this function directly from a
|
||||
loop and use @ref spinLock instead.
|
||||
|
||||
@tparam T An unsigned integral type.
|
||||
@param lock The atomic variable used as the lock.
|
||||
@return true if the lock was acquired, false if it was already held.
|
||||
/** @{ */
|
||||
/**
|
||||
* Classes to handle arrays of spinlocks packed into a single atomic integer:
|
||||
*
|
||||
* Packed spinlocks allow for tremendously space-efficient lock-sharding
|
||||
* but they come at a cost.
|
||||
*
|
||||
* First, the implementation is necessarily low-level and uses advanced
|
||||
* features like memory ordering and highly platform-specific tricks to
|
||||
* maximize performance. This imposes a significant and ongoing cost to
|
||||
* developers.
|
||||
*
|
||||
* Second, and perhaps most important, is that the packing of multiple
|
||||
* locks into a single integer which, albeit space-efficient, also has
|
||||
* performance implications stemming from data dependencies, increased
|
||||
* cache-coherency traffic between processors and heavier loads on the
|
||||
* processor's load/store units.
|
||||
*
|
||||
* To be sure, these locks can have advantages but they are definitely
|
||||
* not general purpose locks and should not be thought of or used that
|
||||
* way. The use cases for them are likely few and far between; without
|
||||
* a compelling reason to use them, backed by profiling data, it might
|
||||
* be best to use one of the standard locking primitives instead. Note
|
||||
* that in most common platforms, `std::mutex` is so heavily optimized
|
||||
* that it can, usually, outperform spinlocks.
|
||||
*
|
||||
* @tparam T An unsigned integral type (e.g. std::uint16_t)
|
||||
*/
|
||||
template <SpinlockValueType T>
|
||||
[[nodiscard]] bool
|
||||
spinTryLock(std::atomic<T>& lock) noexcept
|
||||
{
|
||||
// A compare-exchange is required here, not an unconditional exchange:
|
||||
// a failed attempt must not modify the lock word, in case the atomic
|
||||
// is shared with PackedSpinlock).
|
||||
T expected = 0;
|
||||
|
||||
return lock.compare_exchange_strong(
|
||||
expected,
|
||||
std::numeric_limits<T>::max(),
|
||||
std::memory_order::acquire,
|
||||
std::memory_order::relaxed);
|
||||
;
|
||||
}
|
||||
|
||||
/** Acquire a spinlock, blocking until available.
|
||||
|
||||
Uses a TTAS (test-and-test-and-set) pattern, so waiters share the cache
|
||||
line read-only, helping to avoid unnecessary coherency traffic.
|
||||
|
||||
@tparam T An unsigned integral type.
|
||||
@param lock The atomic variable used as the lock.
|
||||
/**
|
||||
* A class that grabs a single packed spinlock from an atomic integer.
|
||||
*
|
||||
* This class meets the requirements of Lockable:
|
||||
* https://en.cppreference.com/w/cpp/named_req/Lockable
|
||||
*/
|
||||
template <SpinlockValueType T>
|
||||
void
|
||||
spinLock(std::atomic<T>& lock) noexcept
|
||||
{
|
||||
do
|
||||
{
|
||||
// Relaxed ordering is sufficient for the spin: this load is only
|
||||
// a filter. The acquire on the successful exchange in spinTryLock
|
||||
// is what synchronizes the critical section.
|
||||
while (lock.load(std::memory_order::relaxed) != 0)
|
||||
detail::spinPause();
|
||||
} while (!spinTryLock(lock));
|
||||
}
|
||||
|
||||
/** Release a spinlock.
|
||||
|
||||
@tparam T An unsigned integral type.
|
||||
@param lock The atomic variable used as the lock.
|
||||
*/
|
||||
template <SpinlockValueType T>
|
||||
void
|
||||
spinUnlock(std::atomic<T>& lock) noexcept
|
||||
{
|
||||
lock.store(0, std::memory_order::release);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A Lockable interface to a spinlock implemented on top of an atomic.
|
||||
|
||||
@tparam T An unsigned integral type.
|
||||
|
||||
@note Using `PackedSpinlock` and `Spinlock` against the same underlying
|
||||
atomic integer is possible but can result in `Spinlock` not being
|
||||
able to acquire the lock during periods of high contention due to
|
||||
the way the two locks operate: `Spinlock` spins and tries to grab
|
||||
all the bits at once, whereas any given `PackedSpinlock` instance
|
||||
only tries to grab one bit at a time. Caveat emptor.
|
||||
|
||||
This class meets the requirements of Lockable:
|
||||
https://en.cppreference.com/w/cpp/named_req/Lockable
|
||||
*/
|
||||
template <SpinlockValueType T>
|
||||
class Spinlock
|
||||
{
|
||||
std::atomic<T>& lock_;
|
||||
|
||||
public:
|
||||
Spinlock(Spinlock const&) = delete;
|
||||
Spinlock&
|
||||
operator=(Spinlock const&) = delete;
|
||||
|
||||
/** Construct a spinlock handle.
|
||||
|
||||
@param lock The atomic integer to spin against.
|
||||
|
||||
@note For performance reasons, you should strive to have `lock` be
|
||||
on a cacheline by itself.
|
||||
*/
|
||||
explicit Spinlock(std::atomic<T>& lock) noexcept : lock_(lock)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
try_lock() noexcept // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return spinTryLock(lock_);
|
||||
}
|
||||
|
||||
void
|
||||
lock() noexcept
|
||||
{
|
||||
spinLock(lock_);
|
||||
}
|
||||
|
||||
void
|
||||
unlock() noexcept
|
||||
{
|
||||
spinUnlock(lock_);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A Lockable interface to a packed spinlock implemented on top of an atomic.
|
||||
|
||||
Packed spinlocks offer tremendous space-efficient lock-sharding but
|
||||
they come at a cost.
|
||||
|
||||
First, the implementation is necessarily low-level and uses advanced
|
||||
features like memory ordering and highly platform-specific tricks to
|
||||
maximize performance. This imposes a significant and ongoing cost to
|
||||
developers.
|
||||
|
||||
Second, and perhaps most important, is that the packing of multiple
|
||||
locks into a single integer which, albeit space-efficient, also has
|
||||
performance implications stemming from data dependencies, increased
|
||||
cache-coherency traffic between processors and heavier loads on the
|
||||
processor's load/store units.
|
||||
|
||||
To be sure, these locks can have advantages but they are definitely
|
||||
not general purpose locks and should not be thought of or used that
|
||||
way. The use cases for them are likely few and far between; without
|
||||
a compelling reason to use them, backed by profiling data, it might
|
||||
be best to use one of the standard locking primitives instead. Note
|
||||
that in most common platforms, `std::mutex` is so heavily optimized
|
||||
that it can, usually, outperform spinlocks.
|
||||
|
||||
@tparam T An unsigned integral type (e.g. std::uint16_t)
|
||||
|
||||
This class meets the requirements of Lockable:
|
||||
https://en.cppreference.com/w/cpp/named_req/Lockable
|
||||
*/
|
||||
template <PackedSpinlockValueType T>
|
||||
template <class T>
|
||||
class PackedSpinlock
|
||||
{
|
||||
// clang-format off
|
||||
static_assert(std::is_unsigned_v<T>);
|
||||
static_assert(std::atomic<T>::is_always_lock_free);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_or(0)), T> &&
|
||||
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_and(0)), T>,
|
||||
"std::atomic<T>::fetch_and(T) and std::atomic<T>::fetch_and(T) are required by packed_spinlock");
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
std::atomic<T>& bits_;
|
||||
T const mask_;
|
||||
|
||||
@@ -221,49 +94,120 @@ public:
|
||||
PackedSpinlock&
|
||||
operator=(PackedSpinlock const&) = delete;
|
||||
|
||||
/** Construct a packed spinlock handle for a single bit.
|
||||
|
||||
@param lock The atomic integer inside which the spinlock is packed.
|
||||
@param index The index of the spinlock this object acquires.
|
||||
|
||||
@note For performance reasons, you should strive to have `lock` be
|
||||
on a cacheline by itself.
|
||||
/**
|
||||
* A single spinlock packed inside the specified atomic
|
||||
*
|
||||
* @param lock The atomic integer inside which the spinlock is packed.
|
||||
* @param index The index of the spinlock this object acquires.
|
||||
*
|
||||
* @note For performance reasons, you should strive to have `lock` be
|
||||
* on a cacheline by itself.
|
||||
*/
|
||||
PackedSpinlock(std::atomic<T>& lock, int index) noexcept
|
||||
: bits_(lock), mask_([index]() {
|
||||
XRPL_ASSERT(
|
||||
index >= 0 && index < std::numeric_limits<T>::digits,
|
||||
"xrpl::PackedSpinlock::PackedSpinlock : valid index");
|
||||
return static_cast<T>(T{1} << index);
|
||||
}())
|
||||
PackedSpinlock(std::atomic<T>& lock, int index) : bits_(lock), mask_(static_cast<T>(1) << index)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
index >= 0 && (mask_ != 0),
|
||||
"xrpl::PackedSpinlock::PackedSpinlock : valid index and mask");
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
try_lock() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0;
|
||||
}
|
||||
|
||||
void
|
||||
lock()
|
||||
{
|
||||
while (!try_lock())
|
||||
{
|
||||
// The use of relaxed memory ordering here is intentional and
|
||||
// serves to help reduce cache coherency traffic during times
|
||||
// of contention by avoiding writes that would definitely not
|
||||
// result in the lock being acquired.
|
||||
while ((bits_.load(std::memory_order_relaxed) & mask_) != 0)
|
||||
detail::spinPause();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
unlock()
|
||||
{
|
||||
bits_.fetch_and(~mask_, std::memory_order_release);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A spinlock implemented on top of an atomic integer.
|
||||
*
|
||||
* @note Using `packed_spinlock` and `spinlock` against the same underlying
|
||||
* atomic integer can result in `spinlock` not being able to actually
|
||||
* acquire the lock during periods of high contention, because of how
|
||||
* the two locks operate: `spinlock` will spin trying to grab all the
|
||||
* bits at once, whereas any given `packed_spinlock` will only try to
|
||||
* grab one bit at a time. Caveat emptor.
|
||||
*
|
||||
* This class meets the requirements of Lockable:
|
||||
* https://en.cppreference.com/w/cpp/named_req/Lockable
|
||||
*/
|
||||
template <class T>
|
||||
class Spinlock
|
||||
{
|
||||
static_assert(std::is_unsigned_v<T>);
|
||||
static_assert(std::atomic<T>::is_always_lock_free);
|
||||
|
||||
private:
|
||||
std::atomic<T>& lock_;
|
||||
|
||||
public:
|
||||
Spinlock(Spinlock const&) = delete;
|
||||
Spinlock&
|
||||
operator=(Spinlock const&) = delete;
|
||||
|
||||
/**
|
||||
* Grabs the
|
||||
*
|
||||
* @param lock The atomic integer to spin against.
|
||||
*
|
||||
* @note For performance reasons, you should strive to have `lock` be
|
||||
* on a cacheline by itself.
|
||||
*/
|
||||
Spinlock(std::atomic<T>& lock) : lock_(lock)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
try_lock() noexcept // NOLINT(readability-identifier-naming)
|
||||
try_lock() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return (bits_.fetch_or(mask_, std::memory_order::acquire) & mask_) == 0;
|
||||
T expected = 0;
|
||||
|
||||
return lock_.compare_exchange_weak(
|
||||
expected,
|
||||
std::numeric_limits<T>::max(),
|
||||
std::memory_order_acquire,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void
|
||||
lock() noexcept
|
||||
lock()
|
||||
{
|
||||
do
|
||||
while (!try_lock())
|
||||
{
|
||||
// The use of relaxed memory ordering here is intentional and
|
||||
// serves to help reduce cache coherency traffic during times
|
||||
// of contention by avoiding writes that are unlikely to grab
|
||||
// the requested lock.
|
||||
while ((bits_.load(std::memory_order::relaxed) & mask_) != 0)
|
||||
// of contention by avoiding writes that would definitely not
|
||||
// result in the lock being acquired.
|
||||
while (lock_.load(std::memory_order_relaxed) != 0)
|
||||
detail::spinPause();
|
||||
} while (!try_lock());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
unlock() noexcept
|
||||
unlock()
|
||||
{
|
||||
bits_.fetch_and(~mask_, std::memory_order::release);
|
||||
lock_.store(0, std::memory_order_release);
|
||||
}
|
||||
};
|
||||
/** @} */
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
namespace beast {
|
||||
|
||||
template <typename T>
|
||||
std::string
|
||||
typeName()
|
||||
{
|
||||
using TR = std::remove_reference_t<T>;
|
||||
|
||||
std::string name = typeid(TR).name();
|
||||
|
||||
#ifndef _MSC_VER
|
||||
if (auto s = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr))
|
||||
{
|
||||
name = s;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
|
||||
std::free(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (std::is_const_v<TR>)
|
||||
name += " const";
|
||||
if (std::is_volatile_v<TR>)
|
||||
name += " volatile";
|
||||
if (std::is_lvalue_reference_v<T>)
|
||||
{
|
||||
name += "&";
|
||||
}
|
||||
else if (std::is_rvalue_reference_v<T>)
|
||||
{
|
||||
name += "&&";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
} // namespace beast
|
||||
@@ -2,24 +2,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <compare>
|
||||
#include <concepts>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* Zero allows classes to offer efficient comparisons to zero.
|
||||
*
|
||||
* Zero is a struct to allow classes to efficiently compare with zero without
|
||||
* requiring an rvalue construction.
|
||||
*
|
||||
* It's often the case that we have classes which combine a number and a unit.
|
||||
* In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
|
||||
* like t > 1 or t != 1 do not. Comparing against kZero expresses exactly that,
|
||||
* without constructing a T.
|
||||
* like t > 1 or t != 1 do not.
|
||||
*
|
||||
* A type T participates if either `t.signum()` or an unqualified `signum(t)`
|
||||
* found by argument-dependent lookup returns an integer that is negative,
|
||||
* zero, or positive according to the sign of t. Both `t == kZero` and
|
||||
* `kZero == t` work, as do all six relational operators in either order.
|
||||
* The class Zero allows such comparisons to be easily made.
|
||||
*
|
||||
* The comparing class T either needs to have a method called signum() which
|
||||
* returns a positive number, 0, or a negative; or there needs to be a signum
|
||||
* function which resolves in the namespace which takes an instance of T and
|
||||
* returns a positive, zero or negative number.
|
||||
*/
|
||||
|
||||
struct Zero
|
||||
{
|
||||
explicit Zero() = default;
|
||||
@@ -28,44 +30,115 @@ struct Zero
|
||||
inline constexpr Zero kZero{};
|
||||
|
||||
/**
|
||||
* Default implementation of signum: call the member function.
|
||||
* Default implementation of signum calls the method on the class.
|
||||
*/
|
||||
template <class T>
|
||||
requires requires(T const& t) {
|
||||
{ t.signum() } -> std::integral;
|
||||
}
|
||||
[[nodiscard]] constexpr auto
|
||||
signum(T const& t) noexcept(noexcept(t.signum()))
|
||||
template <typename T>
|
||||
auto
|
||||
signum(T const& t)
|
||||
{
|
||||
return t.signum();
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
namespace detail::zero_helper {
|
||||
|
||||
/**
|
||||
* A type with a usable signum: either the member-based default above, or a
|
||||
* `signum(t)` overload in T's own namespace, found by ADL. A user overload
|
||||
* that is a better match than the template wins, as usual.
|
||||
*/
|
||||
// For argument dependent lookup to function properly, calls to signum must
|
||||
// be made from a namespace that does not include overloads of the function..
|
||||
template <class T>
|
||||
concept HasSignum = requires(T const& t) {
|
||||
{ signum(t) } -> std::integral;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <detail::HasSignum T>
|
||||
[[nodiscard]] constexpr bool
|
||||
operator==(T const& t, Zero) noexcept(noexcept(signum(t)))
|
||||
auto
|
||||
callSignum(T const& t)
|
||||
{
|
||||
return signum(t) == 0;
|
||||
return signum(t);
|
||||
}
|
||||
|
||||
template <detail::HasSignum T>
|
||||
[[nodiscard]] constexpr std::strong_ordering
|
||||
operator<=>(T const& t, Zero) noexcept(noexcept(signum(t)))
|
||||
} // namespace detail::zero_helper
|
||||
|
||||
// Handle operators where T is on the left side using signum.
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator==(T const& t, Zero)
|
||||
{
|
||||
return signum(t) <=> 0;
|
||||
return detail::zero_helper::callSignum(t) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator!=(T const& t, Zero)
|
||||
{
|
||||
return detail::zero_helper::callSignum(t) != 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator<(T const& t, Zero)
|
||||
{
|
||||
return detail::zero_helper::callSignum(t) < 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator>(T const& t, Zero)
|
||||
{
|
||||
return detail::zero_helper::callSignum(t) > 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator>=(T const& t, Zero)
|
||||
{
|
||||
return detail::zero_helper::callSignum(t) >= 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator<=(T const& t, Zero)
|
||||
{
|
||||
return detail::zero_helper::callSignum(t) <= 0;
|
||||
}
|
||||
|
||||
// Handle operators where T is on the right side by
|
||||
// reversing the operation, so that T is on the left side.
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator==(Zero, T const& t)
|
||||
{
|
||||
return t == kZero;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator!=(Zero, T const& t)
|
||||
{
|
||||
return t != kZero;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator<(Zero, T const& t)
|
||||
{
|
||||
return t > kZero;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator>(Zero, T const& t)
|
||||
{
|
||||
return t < kZero;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator>=(Zero, T const& t)
|
||||
{
|
||||
return t <= kZero;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
operator<=(Zero, T const& t)
|
||||
{
|
||||
return t >= kZero;
|
||||
}
|
||||
|
||||
} // namespace beast
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/enum_bitops.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/OwnerCounts.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
@@ -22,39 +22,80 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum class ApplyFlags : std::uint32_t {
|
||||
None = 0x00,
|
||||
// Bitwise flag enum with existing operator overloads
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
|
||||
enum ApplyFlags : std::uint32_t {
|
||||
TapNone = 0x00,
|
||||
|
||||
// This is a local transaction with the
|
||||
// fail_hard flag set.
|
||||
FailHard = 0x10,
|
||||
TapFailHard = 0x10,
|
||||
|
||||
// This is not the transaction's last pass
|
||||
// Transaction can be retried, soft failures allowed
|
||||
Retry = 0x20,
|
||||
TapRetry = 0x20,
|
||||
|
||||
// Transaction came from a privileged source
|
||||
Unlimited = 0x400,
|
||||
TapUnlimited = 0x400,
|
||||
|
||||
// Transaction is executing as part of a batch
|
||||
Batch = 0x800,
|
||||
TapBatch = 0x800,
|
||||
|
||||
// Transaction shouldn't be applied
|
||||
// Signatures shouldn't be checked
|
||||
DryRun = 0x1000,
|
||||
TapDryRun = 0x1000,
|
||||
|
||||
// Transaction is being preflighted as the payload of a
|
||||
// TransactionProposalCreate. Its signatures are collected on-ledger
|
||||
// afterward, so signature-presence checks (e.g. Batch signer matching)
|
||||
// are skipped at proposal-creation time (On-Chain Cosigner spec
|
||||
// §5.3.1.2).
|
||||
Proposal = 0x2000
|
||||
TapProposal = 0x2000
|
||||
};
|
||||
|
||||
template <>
|
||||
struct enum_bitops::OptIn<ApplyFlags> : std::true_type
|
||||
constexpr ApplyFlags
|
||||
operator|(ApplyFlags const& lhs, ApplyFlags const& rhs)
|
||||
{
|
||||
};
|
||||
return safeCast<ApplyFlags>(
|
||||
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) |
|
||||
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
|
||||
}
|
||||
|
||||
static_assert((TapFailHard | TapRetry) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
|
||||
static_assert((TapRetry | TapFailHard) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
|
||||
|
||||
constexpr ApplyFlags
|
||||
operator&(ApplyFlags const& lhs, ApplyFlags const& rhs)
|
||||
{
|
||||
return safeCast<ApplyFlags>(
|
||||
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) &
|
||||
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
|
||||
}
|
||||
|
||||
static_assert((TapFailHard & TapRetry) == TapNone, "ApplyFlags operator &");
|
||||
static_assert((TapRetry & TapFailHard) == TapNone, "ApplyFlags operator &");
|
||||
|
||||
constexpr ApplyFlags
|
||||
operator~(ApplyFlags const& flags)
|
||||
{
|
||||
return safeCast<ApplyFlags>(~safeCast<std::underlying_type_t<ApplyFlags>>(flags));
|
||||
}
|
||||
|
||||
static_assert(~TapRetry == safeCast<ApplyFlags>(0xFFFFFFDFu), "ApplyFlags operator ~");
|
||||
|
||||
inline ApplyFlags
|
||||
operator|=(ApplyFlags& lhs, ApplyFlags const& rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline ApplyFlags
|
||||
operator&=(ApplyFlags& lhs, ApplyFlags const& rhs)
|
||||
{
|
||||
lhs = lhs & rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
@@ -211,7 +212,7 @@ canWithdraw(
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
SLE::ConstRef toSle,
|
||||
AccountRootEntryR const& toSle,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag,
|
||||
std::optional<std::vector<UInt256>> const& credentialIDs = std::nullopt);
|
||||
|
||||
@@ -7,9 +7,20 @@
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct Adjustment
|
||||
{
|
||||
std::int32_t ownerCountDelta = 0;
|
||||
std::int32_t accountCountDelta = 0;
|
||||
};
|
||||
|
||||
template <typename ViewT>
|
||||
class AccountRootEntry : public SLEBase<ViewT, ltACCOUNT_ROOT>
|
||||
{
|
||||
@@ -27,6 +38,123 @@ public:
|
||||
: Base(keylet::account(id), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the account reserve, in drops.
|
||||
*
|
||||
* Actual owner count can be adjusted by delta in ownerCountAdj
|
||||
* Actual reserve count can be adjusted by delta in accountCountAdj
|
||||
* The reserve is calculated as:
|
||||
* (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) *
|
||||
* increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base"
|
||||
*
|
||||
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative
|
||||
* to subtract.
|
||||
* @return The account reserve amount in drops
|
||||
*/
|
||||
[[nodiscard]] XRPAmount
|
||||
reserve(Adjustment adj = {}) const;
|
||||
|
||||
/**
|
||||
* Return number of the objects which reserve is covered by the account (so called "owner
|
||||
* count"). Actual owner count can be adjusted by delta in ownerCountAdj.
|
||||
*
|
||||
* @param ownerCountAdj Adjustment to the owner count (default: 0)
|
||||
* @return The adjusted owner count
|
||||
*/
|
||||
[[nodiscard]] std::uint32_t
|
||||
ownerCount(std::int32_t ownerCountAdj = 0) const;
|
||||
|
||||
/**
|
||||
* Check if this account has sufficient reserve.
|
||||
*
|
||||
* @param ctx The apply-view context (view + tx)
|
||||
* @param accBalance The account's balance
|
||||
* @param sponsorSle The sponsor's ledger entry (if applicable)
|
||||
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative
|
||||
* to subtract.
|
||||
* @param insufReserveCode The transaction result code to return if the reserve is
|
||||
* insufficient (default: tecINSUFFICIENT_RESERVE).
|
||||
* @return Transaction result code
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkReserve(
|
||||
ApplyViewContext ctx,
|
||||
XRPAmount accBalance,
|
||||
std::optional<AccountRootEntry<ReadView>> const& sponsorSle,
|
||||
Adjustment adj,
|
||||
TER insufReserveCode = tecINSUFFICIENT_RESERVE) const;
|
||||
|
||||
/**
|
||||
* Check if this account has sufficient reserve, deriving the sponsor internally.
|
||||
*
|
||||
* Equivalent to the overload above, but resolves the sponsor via
|
||||
* getEffectiveTxReserveSponsor(ctx, *this) instead of taking it explicitly. Use this
|
||||
* in the common case where the sponsor is simply the transaction's reserve
|
||||
* sponsor for this account. Callers that must force the account's-own-reserve branch
|
||||
* (passing a null sponsor) or supply a different sponsor should use the
|
||||
* explicit overload above.
|
||||
*
|
||||
* @param ctx The apply-view context (view + tx)
|
||||
* @param accBalance The account's balance
|
||||
* @param adj Reserve adjustments (owner/account count deltas)
|
||||
* @return Transaction result code
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkReserve(ApplyViewContext ctx, XRPAmount accBalance, Adjustment adj) const;
|
||||
|
||||
/**
|
||||
* 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>;
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/SLEBase.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STVector256.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -31,31 +25,6 @@ public:
|
||||
: Base(keylet::skip(), view, j)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a hash `diff` slots back from the most recent entry in the
|
||||
* sfHashes vector (diff == 0 is the most recent entry).
|
||||
*
|
||||
* Callers decide which skip list entry to read and how to translate a
|
||||
* target ledger sequence into `diff`; this only does the bounds check
|
||||
* and vector indexing shared by both the recent (stride 1) and distant
|
||||
* (stride 256) skip lists.
|
||||
*
|
||||
* @param diff how many slots back from the most recent hash to look up;
|
||||
* 0 is the most recent hash.
|
||||
* @return the hash at that slot, or std::nullopt if the entry does not
|
||||
* exist or `diff` is out of range.
|
||||
*/
|
||||
[[nodiscard]] std::optional<UInt256>
|
||||
hashAt(std::size_t diff) const
|
||||
{
|
||||
if (!this->exists())
|
||||
return std::nullopt;
|
||||
STVector256 const vec = (*this)->getFieldV256(sfHashes);
|
||||
if (vec.size() > diff)
|
||||
return vec[vec.size() - diff - 1];
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
using LedgerHashesEntryR = LedgerHashesEntry<ReadView>;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -46,30 +46,55 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer);
|
||||
[[nodiscard]] XRPAmount
|
||||
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j);
|
||||
|
||||
struct Adjustment
|
||||
{
|
||||
std::int32_t ownerCountDelta = 0;
|
||||
std::int32_t accountCountDelta = 0;
|
||||
};
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Returns the account reserve, in drops.
|
||||
* An owner count cannot be negative. If adjustment would cause a negative
|
||||
* owner count, clamp the owner count at 0. Similarly for overflow. This
|
||||
* adjustment allows the ownerCount to be adjusted up or down in multiple steps.
|
||||
* If id != std::nullopt, then do error reporting.
|
||||
*
|
||||
* Actual owner count can be adjusted by delta in ownerCountAdj
|
||||
* Actual reserve count can be adjusted by delta in accountCountAdj
|
||||
* The reserve is calculated as:
|
||||
* (ownerCount + "sponsoring object count" - "sponsored object count" + additionalOwnerCount) *
|
||||
* increment + (1 if not sponsored account + sponsoringAccountCount) * "reserve base"
|
||||
* Shared by AccountRootEntry::ownerCount() and the owner-count helpers.
|
||||
*
|
||||
* @param view The ledger view to read from
|
||||
* @param sle The ledger entry for the account
|
||||
* @param currentOwnerCount The owner count before adjustment
|
||||
* @param ownerCountAdj Positive to add to count, negative to reduce count
|
||||
* @param id The account ID to use for error reporting, or std::nullopt to
|
||||
* skip error reporting
|
||||
* @param j Journal for logging
|
||||
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to
|
||||
* subtract.
|
||||
* @return The account reserve amount in drops
|
||||
* @return The adjusted owner count
|
||||
*/
|
||||
[[nodiscard]] XRPAmount
|
||||
accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjustment adj = {});
|
||||
std::uint32_t
|
||||
confineOwnerCount(
|
||||
std::uint32_t currentOwnerCount,
|
||||
std::int32_t ownerCountAdj,
|
||||
std::optional<AccountID> const& id = std::nullopt,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
/**
|
||||
* Clamps and writes an owner-count field of sle, then updates sle in view.
|
||||
*
|
||||
* Shared by AccountRootEntry and adjustLoanBrokerOwnerCount().
|
||||
*
|
||||
* @return The adjusted owner count
|
||||
*/
|
||||
std::uint32_t
|
||||
adjustOwnerCountImpl(
|
||||
ApplyView& view,
|
||||
SLE::Ref sle,
|
||||
SF_UINT32 const& sfield,
|
||||
AccountID const& accID,
|
||||
std::int32_t ownerCountAdj,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Returns the number of account reserves funded by this account: 1 for itself
|
||||
* (0 if sponsored by another account) plus the count of accounts it sponsors.
|
||||
* Shared by AccountRootEntry::reserve() and xrpLiquid().
|
||||
*/
|
||||
std::uint32_t
|
||||
accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, beast::Journal j);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Convenience overload that accepts AccountID instead of SLE.
|
||||
@@ -84,114 +109,9 @@ accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjust
|
||||
[[nodiscard]] inline XRPAmount
|
||||
accountReserve(ReadView const& view, AccountID const& id, beast::Journal j, Adjustment adj = {})
|
||||
{
|
||||
return accountReserve(view, view.read(keylet::account(id)), j, adj);
|
||||
return AccountRootEntryR(id, view, j).reserve(adj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an account has sufficient reserve.
|
||||
*
|
||||
* @param view The ledger view to read from
|
||||
* @param tx The transaction being processed
|
||||
* @param accSle The account's ledger entry
|
||||
* @param accBalance The account's balance
|
||||
* @param sponsorSle The sponsor's ledger entry (if applicable)
|
||||
* @param adj Adjustment to the owner/account count (default: 0/0). Positive to add, negative to
|
||||
* subtract.
|
||||
* @param j Journal for logging (default: null sink)
|
||||
* @param insufReserveCode The transaction result code to return if the reserve is insufficient
|
||||
* (default: tecINSUFFICIENT_RESERVE).
|
||||
* @return Transaction result code
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkReserve(
|
||||
ApplyViewContext ctx,
|
||||
SLE::ConstRef accSle,
|
||||
XRPAmount accBalance,
|
||||
SLE::ConstRef sponsorSle,
|
||||
Adjustment adj,
|
||||
beast::Journal j,
|
||||
TER insufReserveCode = tecINSUFFICIENT_RESERVE);
|
||||
|
||||
/**
|
||||
* Check if an account has sufficient reserve, deriving the sponsor internally.
|
||||
*
|
||||
* Equivalent to the overload above, but resolves the sponsor via
|
||||
* getEffectiveTxReserveSponsor(ctx, accSle) instead of taking it explicitly. Use this
|
||||
* in the common case where the sponsor is simply the transaction's reserve
|
||||
* sponsor for accSle. Callers that must force the account's-own-reserve branch
|
||||
* (passing a null sponsor) or supply a different sponsor should use the
|
||||
* explicit overload above.
|
||||
*
|
||||
* @param ctx The apply-view context (view + tx)
|
||||
* @param accSle The account's ledger entry
|
||||
* @param accBalance The account's balance
|
||||
* @param adj Reserve adjustments (owner/account count deltas)
|
||||
* @param j Journal for logging (default: null sink)
|
||||
* @return Transaction result code
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkReserve(
|
||||
ApplyViewContext ctx,
|
||||
SLE::ConstRef accSle,
|
||||
XRPAmount accBalance,
|
||||
Adjustment adj,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
/**
|
||||
* Return number of the objects which reserve is covered by the account(sle) (so called "owner
|
||||
* count"). Actual owner count can be adjusted by delta in ownerCountAdj.
|
||||
*
|
||||
* @param sle The account's ledger entry
|
||||
* @param j Journal for logging
|
||||
* @param ownerCountAdj Adjustment to the owner count (default: 0)
|
||||
* @return The adjusted owner count
|
||||
*/
|
||||
std::uint32_t
|
||||
ownerCount(SLE::ConstRef sle, beast::Journal j, std::int32_t ownerCountAdj = 0);
|
||||
|
||||
/**
|
||||
* Increase owner-count fields when the caller supplies the sponsor.
|
||||
*
|
||||
* This helper does not create a ledger object. It updates reserve accounting
|
||||
* after the caller has created/updated an object.
|
||||
* If sponsorSle is provided, this also adjusts the account's sponsored count
|
||||
* and the sponsor's sponsoring count.
|
||||
*
|
||||
* @param view The apply view for making changes
|
||||
* @param accountSle The account's ledger entry
|
||||
* @param sponsorSle The sponsor's ledger entry (if applicable)
|
||||
* @param count Amount to add to the owner count
|
||||
* @param j Journal for logging
|
||||
*/
|
||||
void
|
||||
increaseOwnerCount(
|
||||
ApplyView& view,
|
||||
SLE::Ref accountSle,
|
||||
SLE::Ref sponsorSle,
|
||||
std::uint32_t count,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Increase owner-count fields, deriving the tx reserve sponsor internally.
|
||||
*
|
||||
* Equivalent to the overload above, but resolves the sponsor via
|
||||
* getEffectiveTxReserveSponsor(ctx, accountSle) instead of taking it explicitly. Use
|
||||
* this when the sponsor is the transaction's reserve sponsor for accountSle
|
||||
* (the common create path). Deletion paths, which derive the sponsor from an
|
||||
* object's sfSponsor field, should keep using the explicit overload.
|
||||
*
|
||||
* @param ctx The apply-view context (view + tx)
|
||||
* @param accountSle The account's ledger entry
|
||||
* @param count Amount to add to the owner count
|
||||
* @param j Journal for logging
|
||||
*/
|
||||
void
|
||||
increaseOwnerCount(
|
||||
ApplyViewContext ctx,
|
||||
SLE::Ref accountSle,
|
||||
std::uint32_t count,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Convenience overload that accepts AccountID instead of SLE references.
|
||||
*
|
||||
@@ -209,36 +129,13 @@ increaseOwnerCount(
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
{
|
||||
increaseOwnerCount(
|
||||
view,
|
||||
view.peek(keylet::account(account)),
|
||||
sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(),
|
||||
count,
|
||||
j);
|
||||
AccountRootEntryW accountSle(account, view, j);
|
||||
std::optional<AccountRootEntryW> sponsorSle;
|
||||
if (sponsor)
|
||||
sponsorSle.emplace(*sponsor, view);
|
||||
accountSle.increaseOwnerCount(sponsorSle, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease owner-count fields when the caller supplies the sponsor.
|
||||
*
|
||||
* This helper does not delete a ledger object. It updates reserve accounting
|
||||
* after the caller has removed an owner-counted reserve, or for special
|
||||
* owner-count changes whose sponsor cannot be derived from an object's
|
||||
* sfSponsor field.
|
||||
*
|
||||
* @param view The apply view for making changes
|
||||
* @param accountSle The account's ledger entry
|
||||
* @param sponsorSle The sponsor's ledger entry (if applicable)
|
||||
* @param count Amount to remove from the owner count
|
||||
* @param j Journal for logging
|
||||
*/
|
||||
void
|
||||
decreaseOwnerCount(
|
||||
ApplyView& view,
|
||||
SLE::Ref accountSle,
|
||||
SLE::Ref sponsorSle,
|
||||
std::uint32_t count,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* Convenience overload that accepts AccountID instead of SLE references.
|
||||
*
|
||||
@@ -256,12 +153,11 @@ decreaseOwnerCount(
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
{
|
||||
decreaseOwnerCount(
|
||||
view,
|
||||
view.peek(keylet::account(account)),
|
||||
sponsor ? view.peek(keylet::account(*sponsor)) : SLE::pointer(),
|
||||
count,
|
||||
j);
|
||||
AccountRootEntryW accountSle(account, view, j);
|
||||
std::optional<AccountRootEntryW> sponsorSle;
|
||||
if (sponsor)
|
||||
sponsorSle.emplace(*sponsor, view);
|
||||
accountSle.decreaseOwnerCount(sponsorSle, count);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,7 +177,7 @@ decreaseOwnerCount(
|
||||
void
|
||||
decreaseOwnerCountForObject(
|
||||
ApplyView& view,
|
||||
SLE::Ref accountSle,
|
||||
AccountRootEntryW& accountSle,
|
||||
SLE::Ref objectSle,
|
||||
std::uint32_t count,
|
||||
beast::Journal j);
|
||||
@@ -303,7 +199,7 @@ decreaseOwnerCountForObject(
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
{
|
||||
SLE::Ref accountSle = view.peek(keylet::account(account));
|
||||
AccountRootEntryW accountSle(account, view);
|
||||
decreaseOwnerCountForObject(view, accountSle, objectSle, count, j);
|
||||
}
|
||||
|
||||
@@ -367,7 +263,7 @@ getPseudoAccountFields();
|
||||
* - null pointer
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(SLE::const_pointer sleAcct);
|
||||
isPseudoAccount(AccountRootEntryR const& sleAcct);
|
||||
|
||||
/**
|
||||
* Convenience overload that reads the account from the view.
|
||||
@@ -375,7 +271,7 @@ isPseudoAccount(SLE::const_pointer sleAcct);
|
||||
[[nodiscard]] inline bool
|
||||
isPseudoAccount(ReadView const& view, AccountID const& accountId)
|
||||
{
|
||||
return isPseudoAccount(view.read(keylet::account(accountId)));
|
||||
return isPseudoAccount(AccountRootEntryR(accountId, view));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,6 +292,6 @@ createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const
|
||||
* - If the SLE requires a destination tag, checks that there is a tag.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag);
|
||||
checkDestinationAndTag(AccountRootEntryR const& toSle, bool hasDestinationTag);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
@@ -27,6 +27,32 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Before fixCleanup3_2_0, EscrowCancel passes the escrow SLE as sleDest.
|
||||
* Return what the old reserve check returned for it, without adopting a
|
||||
* non-AccountRoot SLE into an AccountRootEntry: the tx reserve sponsor's
|
||||
* error if the escrow owner submitted the tx, else tefINTERNAL.
|
||||
*
|
||||
* @param ctx the apply view context for the escrow transaction.
|
||||
* @param sleDest the non-AccountRoot SLE that was passed as the destination.
|
||||
* @return the tx reserve sponsor's error code if the escrow owner submitted
|
||||
* the tx, else tefINTERNAL.
|
||||
*/
|
||||
inline TER
|
||||
escrowDestNotAccountResult(ApplyViewContext ctx, SLE::ConstRef sleDest)
|
||||
{
|
||||
if (sleDest->getAccountID(sfAccount) == ctx.tx[sfAccount])
|
||||
{
|
||||
if (auto const sponsor = getTxReserveSponsor(ctx); !sponsor)
|
||||
return sponsor.error(); // LCOV_EXCL_LINE
|
||||
}
|
||||
return tefINTERNAL;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <ValidIssueType T>
|
||||
TER
|
||||
escrowUnlockApplyHelper(
|
||||
@@ -70,18 +96,17 @@ escrowUnlockApplyHelper<Issue>(
|
||||
if (!ctx.view.exists(trustLineKey) && createAsset)
|
||||
{
|
||||
// Can the account cover the trust line's reserve?
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
|
||||
if (sleDest->getType() != ltACCOUNT_ROOT)
|
||||
return detail::escrowDestNotAccountResult(ctx, sleDest);
|
||||
auto const destSle = AccountRootEntryR(sleDest, ctx.view, journal);
|
||||
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, destSle);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx,
|
||||
sleDest,
|
||||
xrpBalance,
|
||||
*sponsorSle,
|
||||
{.ownerCountDelta = 1},
|
||||
journal,
|
||||
tecNO_LINE_INSUF_RESERVE);
|
||||
auto& sponsor = *sponsorSle;
|
||||
|
||||
if (auto const ret = destSle.checkReserve(
|
||||
ctx, xrpBalance, sponsor, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE);
|
||||
!isTesSuccess(ret))
|
||||
{
|
||||
JLOG(journal.trace()) << "Trust line does not exist. "
|
||||
@@ -94,22 +119,22 @@ escrowUnlockApplyHelper<Issue>(
|
||||
initialBalance.get<Issue>().account = noAccount();
|
||||
|
||||
if (TER const ter = trustCreate(
|
||||
ctx.view, // payment sandbox
|
||||
recvLow, // is dest low?
|
||||
issuer, // source
|
||||
receiver, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDest, // Account to add to
|
||||
false, // authorize account
|
||||
!sleDest->isFlag(lsfDefaultRipple), //
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, receiver), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
*sponsorSle, // sponsor
|
||||
journal); // journal
|
||||
ctx.view, // payment sandbox
|
||||
recvLow, // is dest low?
|
||||
issuer, // source
|
||||
receiver, // destination
|
||||
trustLineKey.key, // ledger index
|
||||
sleDest, // Account to add to
|
||||
false, // authorize account
|
||||
!sleDest->isFlag(lsfDefaultRipple), //
|
||||
false, // freeze trust line
|
||||
false, // deep freeze trust line
|
||||
initialBalance, // zero initial balance
|
||||
Issue(currency, receiver), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
sponsor ? sponsor->mutableRawSle() : SLE::pointer(), // sponsor
|
||||
journal); // journal
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
@@ -204,23 +229,29 @@ escrowUnlockApplyHelper<MPTIssue>(
|
||||
auto const mptKeylet = keylet::mptoken(issuanceKey.key, receiver);
|
||||
if (!ctx.view.exists(mptKeylet) && createAsset && !receiverIssuer)
|
||||
{
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, sleDest);
|
||||
if (sleDest->getType() != ltACCOUNT_ROOT)
|
||||
return detail::escrowDestNotAccountResult(ctx, sleDest);
|
||||
auto const destSle = AccountRootEntryR(sleDest, ctx.view, journal);
|
||||
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, destSle);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx, sleDest, xrpBalance, *sponsorSle, {.ownerCountDelta = 1}, journal);
|
||||
auto& sponsor = *sponsorSle;
|
||||
|
||||
if (auto const ret = destSle.checkReserve(ctx, xrpBalance, sponsor, {.ownerCountDelta = 1});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0);
|
||||
if (auto const ter = createMPToken(
|
||||
ctx.view, mptID, receiver, sponsor ? sponsor->mutableRawSle() : SLE::pointer(), 0);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// update owner count.
|
||||
increaseOwnerCount(ctx.view, sleDest, *sponsorSle, 1, journal);
|
||||
AccountRootEntryW receiverSle(receiver, ctx.view, journal);
|
||||
receiverSle.increaseOwnerCount(sponsor, 1);
|
||||
}
|
||||
|
||||
if (!ctx.view.exists(mptKeylet) && !receiverIssuer)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -54,11 +55,11 @@ getTxReserveSponsorID(STTx const& tx);
|
||||
* Return a mutable SLE for the transaction's reserve sponsor account.
|
||||
*
|
||||
* @param ctx The apply-view context (view + tx)
|
||||
* @return The sponsor account SLE, a null pointer if the tx is not
|
||||
* @return The sponsor account entry, nullopt if the tx is not
|
||||
* reserve-sponsored, or tecINTERNAL if the sponsor account cannot
|
||||
* be loaded (an already-checked invariant).
|
||||
*/
|
||||
std::expected<SLE::pointer, TER>
|
||||
std::expected<std::optional<AccountRootEntryW>, TER>
|
||||
getTxReserveSponsor(ApplyViewContext ctx);
|
||||
|
||||
/**
|
||||
@@ -66,29 +67,29 @@ getTxReserveSponsor(ApplyViewContext ctx);
|
||||
*
|
||||
* @param view The ledger read view
|
||||
* @param tx The transaction to inspect
|
||||
* @return The sponsor account SLE, a null pointer if the tx is not
|
||||
* @return The sponsor account entry, nullopt if the tx is not
|
||||
* reserve-sponsored, or tecINTERNAL if the sponsor account cannot
|
||||
* be loaded (an already-checked invariant).
|
||||
*/
|
||||
std::expected<SLE::const_pointer, TER>
|
||||
std::expected<std::optional<AccountRootEntryR>, TER>
|
||||
getTxReserveSponsor(ReadView const& view, STTx const& tx);
|
||||
|
||||
/**
|
||||
* The transaction's reserve sponsor for the given account, if applicable.
|
||||
*
|
||||
* A reserve sponsor only covers the transaction submitter's own objects, so
|
||||
* this returns the tx reserve sponsor SLE only when accountSle is the tx's own
|
||||
* (non-pseudo) account; otherwise it returns a null sponsor pointer. This is
|
||||
* this returns the tx reserve sponsor entry only when accountSle is the tx's
|
||||
* own (non-pseudo) account; otherwise it returns nullopt. This is
|
||||
* the single source of truth for the "sponsor applies to tx.Account only" rule
|
||||
* that the sponsor-deriving helper overloads in AccountRootHelpers rely on.
|
||||
*
|
||||
* @param ctx The apply-view context (view + tx)
|
||||
* @param accountSle The account whose sponsor is being resolved
|
||||
* @return The sponsor SLE (nullptr if unsponsored), or tecINTERNAL if the
|
||||
* @return The sponsor entry (nullopt if unsponsored), or tecINTERNAL if the
|
||||
* sponsor account cannot be loaded (an already-checked invariant)
|
||||
*/
|
||||
[[nodiscard]] std::expected<SLE::pointer, TER>
|
||||
getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle);
|
||||
[[nodiscard]] std::expected<std::optional<AccountRootEntryW>, TER>
|
||||
getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle);
|
||||
|
||||
/**
|
||||
* Return the AccountID stored in the given sponsor field of a ledger entry, or nullopt if absent.
|
||||
@@ -105,9 +106,9 @@ getLedgerEntryReserveSponsorID(SLE::ConstRef sle, SF_ACCOUNT const& field = sfSp
|
||||
* @param view The mutable apply view
|
||||
* @param sle The ledger entry whose sponsor field is inspected
|
||||
* @param field The field that holds the sponsor AccountID (defaults to sfSponsor)
|
||||
* @return The sponsor account SLE, or a null pointer if the entry is unsponsored.
|
||||
* @return The sponsor account entry, or nullopt if the entry is unsponsored.
|
||||
*/
|
||||
SLE::pointer
|
||||
std::optional<AccountRootEntryW>
|
||||
getLedgerEntryReserveSponsor(
|
||||
ApplyView& view,
|
||||
SLE::ConstRef sle,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl::node_store {
|
||||
|
||||
@@ -25,7 +23,7 @@ public:
|
||||
/**
|
||||
* Construct the decoded blob from raw data.
|
||||
*/
|
||||
DecodedBlob(std::optional<uint256> key, void const* value, int valueBytes);
|
||||
DecodedBlob(void const* key, void const* value, int valueBytes);
|
||||
|
||||
/**
|
||||
* Determine if the decoding was successful.
|
||||
@@ -45,10 +43,10 @@ public:
|
||||
private:
|
||||
bool success_{false};
|
||||
|
||||
uint256 key_;
|
||||
void const* key_;
|
||||
NodeObjectType objectType_{NodeObjectType::Unknown};
|
||||
unsigned char const* objectData_{nullptr};
|
||||
int dataBytes_ = 0;
|
||||
int dataBytes_;
|
||||
};
|
||||
|
||||
} // namespace xrpl::node_store
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
|
||||
@@ -38,6 +37,11 @@ namespace xrpl::node_store {
|
||||
|
||||
class EncodedBlob
|
||||
{
|
||||
/**
|
||||
* The 32-byte key of the serialized object.
|
||||
*/
|
||||
std::array<std::uint8_t, 32> key_{};
|
||||
|
||||
/**
|
||||
* A pre-allocated buffer for the serialized object.
|
||||
*
|
||||
@@ -61,11 +65,6 @@ class EncodedBlob
|
||||
*/
|
||||
std::uint8_t* const ptr_;
|
||||
|
||||
/**
|
||||
* The 32-byte key of the serialized object.
|
||||
*/
|
||||
uint256 key_;
|
||||
|
||||
public:
|
||||
explicit EncodedBlob(std::shared_ptr<NodeObject> const& obj)
|
||||
: size_([&obj]() {
|
||||
@@ -77,12 +76,11 @@ public:
|
||||
return obj->getData().size() + 9;
|
||||
}())
|
||||
, ptr_((size_ <= payload_.size()) ? payload_.data() : new std::uint8_t[size_])
|
||||
, key_(obj->getHash())
|
||||
{
|
||||
std::fill_n(ptr_, 8, std::uint8_t{0});
|
||||
ptr_[8] = static_cast<std::uint8_t>(obj->getType());
|
||||
|
||||
std::copy_n(obj->getData().data(), obj->getData().size(), ptr_ + 9);
|
||||
std::copy_n(obj->getHash().data(), obj->getHash().size(), key_.data());
|
||||
}
|
||||
|
||||
~EncodedBlob()
|
||||
@@ -97,10 +95,10 @@ public:
|
||||
delete[] ptr_;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint256 const&
|
||||
[[nodiscard]] void const*
|
||||
getKey() const noexcept
|
||||
{
|
||||
return key_;
|
||||
return static_cast<void const*>(key_.data());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t
|
||||
|
||||
@@ -61,22 +61,14 @@ parseBase58(std::string const& s);
|
||||
/**
|
||||
* A special account that's used as the "issuer" for XRP.
|
||||
*/
|
||||
constexpr inline AccountID const&
|
||||
xrpAccount() noexcept
|
||||
{
|
||||
static constexpr AccountID kAccount(beast::kZero);
|
||||
return kAccount;
|
||||
}
|
||||
AccountID const&
|
||||
xrpAccount();
|
||||
|
||||
/**
|
||||
* A placeholder for empty accounts.
|
||||
*/
|
||||
constexpr inline AccountID const&
|
||||
noAccount() noexcept
|
||||
{
|
||||
static constexpr AccountID kAccount = xrpAccount().next();
|
||||
return kAccount;
|
||||
}
|
||||
AccountID const&
|
||||
noAccount();
|
||||
|
||||
/**
|
||||
* Convert hex or base58 string to AccountID.
|
||||
@@ -88,10 +80,10 @@ bool
|
||||
toIssuer(AccountID&, std::string const&);
|
||||
|
||||
// DEPRECATED Should be checking the currency or native flag
|
||||
constexpr inline bool
|
||||
isXRP(AccountID const& c) noexcept
|
||||
inline bool
|
||||
isXRP(AccountID const& c)
|
||||
{
|
||||
return c == xrpAccount();
|
||||
return c == beast::kZero;
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
@@ -109,6 +101,22 @@ operator<<(std::ostream& os, AccountID const& x)
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the global cache used to map AccountID to base58 conversions.
|
||||
*
|
||||
* The cache is optional and need not be initialized. But because conversion
|
||||
* is expensive (it requires a SHA-256 operation) in most cases the overhead
|
||||
* of the cache is worth the benefit.
|
||||
*
|
||||
* @param count The number of entries the cache should accommodate. Zero will
|
||||
* disable the cache, releasing any memory associated with it.
|
||||
*
|
||||
* @note The function will only initialize the cache the first time it is
|
||||
* invoked. Subsequent invocations do nothing.
|
||||
*/
|
||||
void
|
||||
initAccountIdCache(std::size_t count);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
#include <xrpl/protocol/SOTemplate.h>
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/core/type_name.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
* Derived classes will load the object with all the known formats.
|
||||
*/
|
||||
private:
|
||||
KnownFormats() : name_(boost::core::type_name<Derived>())
|
||||
KnownFormats() : name_(beast::typeName<Derived>())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -33,20 +33,7 @@ public:
|
||||
|
||||
STBitString(SField const& n);
|
||||
STBitString(value_type const& v);
|
||||
|
||||
template <typename Tag>
|
||||
requires(!std::is_void_v<Tag>)
|
||||
STBitString(BaseUInt<Bits, Tag> const& v) : value_(v)
|
||||
{
|
||||
}
|
||||
|
||||
STBitString(SField const& n, value_type const& v);
|
||||
template <typename Tag>
|
||||
requires(!std::is_void_v<Tag>)
|
||||
STBitString(SField const& n, BaseUInt<Bits, Tag> const& v) : STBase(n), value_(v)
|
||||
{
|
||||
}
|
||||
|
||||
STBitString(SerialIter& sit, SField const& name);
|
||||
|
||||
[[nodiscard]] SerializedTypeID
|
||||
@@ -179,7 +166,7 @@ template <typename Tag>
|
||||
void
|
||||
STBitString<Bits>::setValue(BaseUInt<Bits, Tag> const& v)
|
||||
{
|
||||
value_ = value_type{v};
|
||||
value_ = v;
|
||||
}
|
||||
|
||||
template <int Bits>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <xrpl/basics/enum_bitops.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -16,7 +15,6 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -24,8 +22,18 @@ namespace xrpl {
|
||||
|
||||
class STPathElement final : public CountedObject<STPathElement>
|
||||
{
|
||||
unsigned int type_;
|
||||
AccountID accountID_;
|
||||
PathAsset assetID_;
|
||||
AccountID issuerID_;
|
||||
|
||||
bool isOffer_;
|
||||
std::size_t hashValue_;
|
||||
|
||||
public:
|
||||
enum class Type : std::uint8_t {
|
||||
// Bitwise values (typeCurrency | typeMPT)
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
|
||||
enum Type {
|
||||
TypeNone = 0x00,
|
||||
TypeAccount = 0x01, // Rippling through an account (vs taking an offer).
|
||||
TypeCurrency = 0x10, // Currency follows.
|
||||
@@ -37,8 +45,6 @@ public:
|
||||
// Combination of all types.
|
||||
};
|
||||
|
||||
using enum Type;
|
||||
|
||||
STPathElement();
|
||||
STPathElement(STPathElement const&) = default;
|
||||
STPathElement&
|
||||
@@ -56,42 +62,42 @@ public:
|
||||
bool forceAsset = false);
|
||||
|
||||
STPathElement(
|
||||
Type type,
|
||||
unsigned int uType,
|
||||
AccountID const& account,
|
||||
PathAsset const& asset,
|
||||
AccountID const& issuer);
|
||||
|
||||
[[nodiscard]] Type
|
||||
getNodeType() const noexcept;
|
||||
[[nodiscard]] std::uint32_t
|
||||
getNodeType() const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isOffer() const noexcept;
|
||||
isOffer() const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isAccount() const noexcept;
|
||||
isAccount() const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
hasIssuer() const noexcept;
|
||||
hasIssuer() const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
hasCurrency() const noexcept;
|
||||
hasCurrency() const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
hasMPT() const noexcept;
|
||||
hasMPT() const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
hasAsset() const noexcept;
|
||||
hasAsset() const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isNone() const noexcept;
|
||||
isNone() const;
|
||||
|
||||
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
|
||||
// class of offers.
|
||||
[[nodiscard]] AccountID const&
|
||||
getAccountID() const noexcept;
|
||||
getAccountID() const;
|
||||
|
||||
[[nodiscard]] PathAsset const&
|
||||
getPathAsset() const noexcept;
|
||||
getPathAsset() const;
|
||||
|
||||
[[nodiscard]] Currency const&
|
||||
getCurrency() const;
|
||||
@@ -100,35 +106,17 @@ public:
|
||||
getMPTID() const;
|
||||
|
||||
[[nodiscard]] AccountID const&
|
||||
getIssuerID() const noexcept;
|
||||
getIssuerID() const;
|
||||
|
||||
[[nodiscard]] bool
|
||||
isType(Type pe) const noexcept;
|
||||
isType(Type const& pe) const;
|
||||
|
||||
friend bool
|
||||
operator==(STPathElement const& lhs, STPathElement const& rhs) noexcept
|
||||
{
|
||||
return lhs.isType(TypeAccount) == rhs.isType(TypeAccount) &&
|
||||
lhs.hashValue_ == rhs.hashValue_ && lhs.accountID_ == rhs.accountID_ &&
|
||||
lhs.assetID_ == rhs.assetID_ && lhs.issuerID_ == rhs.issuerID_;
|
||||
}
|
||||
bool
|
||||
operator==(STPathElement const& t) const;
|
||||
|
||||
private:
|
||||
static std::size_t
|
||||
getHash(STPathElement const& element);
|
||||
|
||||
Type type_;
|
||||
AccountID accountID_;
|
||||
PathAsset assetID_;
|
||||
AccountID issuerID_;
|
||||
|
||||
bool isOffer_;
|
||||
std::size_t hashValue_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct enum_bitops::OptIn<STPathElement::Type> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class Hasher>
|
||||
@@ -136,7 +124,7 @@ void
|
||||
hash_append(Hasher& h, STPathElement const& e) noexcept
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, e.isType(STPathElement::TypeAccount));
|
||||
hash_append(h, (e.getNodeType() & STPathElement::TypeAccount) != 0u);
|
||||
hash_append(h, e.getAccountID());
|
||||
hash_append(h, e.getPathAsset());
|
||||
hash_append(h, e.getIssuerID());
|
||||
@@ -416,11 +404,11 @@ inline STPathElement::STPathElement(
|
||||
}
|
||||
|
||||
inline STPathElement::STPathElement(
|
||||
Type type,
|
||||
unsigned int uType,
|
||||
AccountID const& account,
|
||||
PathAsset const& asset,
|
||||
AccountID const& issuer)
|
||||
: type_(type)
|
||||
: type_(uType)
|
||||
, accountID_(account)
|
||||
, assetID_(asset)
|
||||
, issuerID_(issuer)
|
||||
@@ -435,56 +423,56 @@ inline STPathElement::STPathElement(
|
||||
hashValue_ = getHash(*this);
|
||||
}
|
||||
|
||||
inline STPathElement::Type
|
||||
STPathElement::getNodeType() const noexcept
|
||||
inline std::uint32_t
|
||||
STPathElement::getNodeType() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::isOffer() const noexcept
|
||||
STPathElement::isOffer() const
|
||||
{
|
||||
return isOffer_;
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::isAccount() const noexcept
|
||||
STPathElement::isAccount() const
|
||||
{
|
||||
return !isOffer();
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::isType(Type pe) const noexcept
|
||||
STPathElement::isType(Type const& pe) const
|
||||
{
|
||||
return (type_ & pe) != STPathElement::TypeNone;
|
||||
return (type_ & pe) != 0u;
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::hasIssuer() const noexcept
|
||||
STPathElement::hasIssuer() const
|
||||
{
|
||||
return isType(STPathElement::TypeIssuer);
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::hasCurrency() const noexcept
|
||||
STPathElement::hasCurrency() const
|
||||
{
|
||||
return isType(STPathElement::TypeCurrency);
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::hasMPT() const noexcept
|
||||
STPathElement::hasMPT() const
|
||||
{
|
||||
return isType(STPathElement::TypeMpt);
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::hasAsset() const noexcept
|
||||
STPathElement::hasAsset() const
|
||||
{
|
||||
return isType(STPathElement::TypeAsset);
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::isNone() const noexcept
|
||||
STPathElement::isNone() const
|
||||
{
|
||||
return getNodeType() == STPathElement::TypeNone;
|
||||
}
|
||||
@@ -492,13 +480,13 @@ STPathElement::isNone() const noexcept
|
||||
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
|
||||
// class of offers.
|
||||
inline AccountID const&
|
||||
STPathElement::getAccountID() const noexcept
|
||||
STPathElement::getAccountID() const
|
||||
{
|
||||
return accountID_;
|
||||
}
|
||||
|
||||
inline PathAsset const&
|
||||
STPathElement::getPathAsset() const noexcept
|
||||
STPathElement::getPathAsset() const
|
||||
{
|
||||
return assetID_;
|
||||
}
|
||||
@@ -516,11 +504,18 @@ STPathElement::getMPTID() const
|
||||
}
|
||||
|
||||
inline AccountID const&
|
||||
STPathElement::getIssuerID() const noexcept
|
||||
STPathElement::getIssuerID() const
|
||||
{
|
||||
return issuerID_;
|
||||
}
|
||||
|
||||
inline bool
|
||||
STPathElement::operator==(STPathElement const& t) const
|
||||
{
|
||||
return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hashValue_ == t.hashValue_ &&
|
||||
accountID_ == t.accountID_ && assetID_ == t.assetID_ && issuerID_ == t.issuerID_;
|
||||
}
|
||||
|
||||
// ------------ STPath ------------
|
||||
|
||||
inline STPath::STPath(std::vector<STPathElement> p) : path_(std::move(p))
|
||||
|
||||
@@ -596,7 +596,7 @@ template <std::size_t Bits, class Tag>
|
||||
BaseUInt<Bits, Tag>
|
||||
SerialIter::getBitString()
|
||||
{
|
||||
constexpr auto n = BaseUInt<Bits, Tag>::size();
|
||||
auto const n = Bits / 8;
|
||||
|
||||
if (remain_ < n)
|
||||
Throw<std::runtime_error>("invalid SerialIter getBitString");
|
||||
@@ -607,7 +607,7 @@ SerialIter::getBitString()
|
||||
used_ += n;
|
||||
remain_ -= n;
|
||||
|
||||
return BaseUInt<Bits, Tag>{std::span<std::uint8_t const, n>{x, n}};
|
||||
return BaseUInt<Bits, Tag>::fromVoid(x);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -14,7 +14,7 @@ namespace xrpl {
|
||||
// Various protocol and system specific constant globals.
|
||||
|
||||
/* The name of the system. */
|
||||
inline std::string const&
|
||||
static inline std::string const&
|
||||
systemName()
|
||||
{
|
||||
static std::string const kName = "xrpld";
|
||||
@@ -52,10 +52,11 @@ isLegalAmountSigned(XRPAmount const& amount)
|
||||
}
|
||||
|
||||
/* The currency code for the native currency. */
|
||||
inline std::string
|
||||
static inline std::string const&
|
||||
systemCurrencyCode()
|
||||
{
|
||||
return "XRP";
|
||||
static std::string const kCode = "XRP";
|
||||
return kCode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,40 +61,26 @@ using Domain = BaseUInt<256>;
|
||||
/**
|
||||
* XRP currency.
|
||||
*/
|
||||
constexpr inline Currency const&
|
||||
xrpCurrency() noexcept
|
||||
{
|
||||
static constexpr Currency const kCurrency(beast::kZero);
|
||||
return kCurrency;
|
||||
}
|
||||
Currency const&
|
||||
xrpCurrency();
|
||||
|
||||
/**
|
||||
* A placeholder for empty currencies.
|
||||
*/
|
||||
constexpr inline Currency const&
|
||||
noCurrency() noexcept
|
||||
{
|
||||
static constexpr Currency const kCurrency(xrpCurrency().next());
|
||||
return kCurrency;
|
||||
}
|
||||
Currency const&
|
||||
noCurrency();
|
||||
|
||||
/**
|
||||
* We deliberately disallow the currency that looks like "XRP" because too
|
||||
* many people were using it instead of the correct XRP currency.
|
||||
*
|
||||
* Note that this doesn't catch "xRP" or "xrp" or other case variations.
|
||||
*/
|
||||
constexpr inline Currency const&
|
||||
badCurrency() noexcept
|
||||
{
|
||||
static constexpr Currency kCurrency{"0000000000000000000000005852500000000000"};
|
||||
return kCurrency;
|
||||
}
|
||||
Currency const&
|
||||
badCurrency();
|
||||
|
||||
constexpr inline bool
|
||||
isXRP(Currency const& c) noexcept
|
||||
inline bool
|
||||
isXRP(Currency const& c)
|
||||
{
|
||||
return c == xrpCurrency();
|
||||
return c == beast::kZero;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +98,7 @@ to_string(Currency const& c);
|
||||
* to rewrite some unit test code.
|
||||
*/
|
||||
bool
|
||||
toCurrency(Currency&, std::string_view);
|
||||
toCurrency(Currency&, std::string const&);
|
||||
|
||||
/**
|
||||
* Tries to convert a string to a Currency, returns noCurrency() on failure.
|
||||
@@ -121,7 +107,8 @@ toCurrency(Currency&, std::string_view);
|
||||
* unfortunate; changing this will require very careful checking
|
||||
* everywhere and may mean having to rewrite some unit test code.
|
||||
*/
|
||||
Currency toCurrency(std::string_view);
|
||||
Currency
|
||||
toCurrency(std::string const&);
|
||||
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, Currency const& x)
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -183,7 +182,7 @@ public:
|
||||
operator result_type() noexcept
|
||||
{
|
||||
auto const digest = Sha512Hasher::result_type(h_);
|
||||
return result_type{std::span{digest}.first<result_type::size()>()};
|
||||
return result_type::fromVoid(digest.data());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -392,7 +392,6 @@ JSS(max_ledger); // in/out: LedgerCleaner
|
||||
JSS(max_queue_size); // out: TxQ
|
||||
JSS(max_spend_drops); // out: AccountInfo
|
||||
JSS(max_spend_drops_total); // out: AccountInfo
|
||||
JSS(maximum);
|
||||
JSS(mean); // out: get_aggregate_price
|
||||
JSS(median); // out: get_aggregate_price
|
||||
JSS(median_fee); // out: TxQ
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
|
||||
namespace xrpl::nft {
|
||||
|
||||
@@ -99,8 +98,7 @@ getTaxon(UInt256 const& id)
|
||||
inline AccountID
|
||||
getIssuer(UInt256 const& id)
|
||||
{
|
||||
return AccountID{
|
||||
std::span<unsigned char const, uint256::size()>{id}.subspan<4, AccountID::size()>()};
|
||||
return AccountID::fromVoid(id.data() + 4);
|
||||
}
|
||||
|
||||
} // namespace xrpl::nft
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl::nft {
|
||||
|
||||
// NFT directory pages order their contents based only on the low 96 bits of
|
||||
// the NFToken value. This mask provides easy access to the necessary mask.
|
||||
constexpr UInt256 kPageMask{"0000000000000000000000000000000000000000ffffffffffffffffffffffff"};
|
||||
constexpr UInt256 kPageMask(
|
||||
std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"));
|
||||
|
||||
} // namespace xrpl::nft
|
||||
@@ -51,8 +51,7 @@ public:
|
||||
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()})
|
||||
: ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
|
||||
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
std::reference_wrapper<ServiceRegistry> registry;
|
||||
|
||||
@@ -59,11 +59,10 @@ public:
|
||||
, parentBatchId(parentBatchId)
|
||||
, j(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
(flags & ApplyFlags::Batch) == ApplyFlags::Batch, "Batch apply flag should be set");
|
||||
XRPL_ASSERT((flags & TapBatch) == TapBatch, "Batch apply flag should be set");
|
||||
XRPL_ASSERT_IF(
|
||||
(flags & ApplyFlags::Proposal) != ApplyFlags::None,
|
||||
(flags & ApplyFlags::DryRun) != ApplyFlags::None,
|
||||
(flags & TapProposal) != TapNone,
|
||||
(flags & TapDryRun) != TapNone,
|
||||
"xrpl::PreflightContext : proposal preflight implies dry run");
|
||||
}
|
||||
|
||||
@@ -75,11 +74,10 @@ public:
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: registry(registry), tx(tx), rules(std::move(rules)), flags(flags), j(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
|
||||
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
|
||||
XRPL_ASSERT_IF(
|
||||
(flags & ApplyFlags::Proposal) != ApplyFlags::None,
|
||||
(flags & ApplyFlags::DryRun) != ApplyFlags::None,
|
||||
(flags & TapProposal) != TapNone,
|
||||
(flags & TapDryRun) != TapNone,
|
||||
"xrpl::PreflightContext : proposal preflight implies dry run");
|
||||
}
|
||||
|
||||
@@ -118,7 +116,7 @@ public:
|
||||
, j(j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
parentBatchId.has_value() == ((flags & ApplyFlags::Batch) == ApplyFlags::Batch),
|
||||
parentBatchId.has_value() == ((flags & TapBatch) == TapBatch),
|
||||
"Parent Batch ID should be set if batch apply flag is set");
|
||||
}
|
||||
|
||||
@@ -131,8 +129,7 @@ public:
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
: PreclaimContext(registry, view, preflightResult, tx, flags, std::nullopt, j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
|
||||
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
PreclaimContext&
|
||||
|
||||
@@ -41,7 +41,7 @@ struct ApplyResult
|
||||
inline bool
|
||||
isTecClaimHardFail(TER ter, ApplyFlags flags)
|
||||
{
|
||||
return isTecClaim(ter) && ((flags & ApplyFlags::Retry) == ApplyFlags::None);
|
||||
return isTecClaim(ter) && ((flags & TapRetry) == 0u);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,18 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Returns true if sleAcct is an AccountRoot carrying a pseudo-account field.
|
||||
*
|
||||
* isPseudoAccount() takes a view-bound AccountRootEntry, which is not
|
||||
* available while visiting entries, so invariant checks use this instead.
|
||||
*
|
||||
* @param sleAcct the SLE to check.
|
||||
* @return true if sleAcct is an AccountRoot carrying a pseudo-account field.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccountSle(SLE::ConstRef sleAcct);
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/**
|
||||
* @brief Prototype for invariant check implementations.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -25,7 +26,7 @@ checkFreeze(
|
||||
XRPL_ASSERT(src != dst, "xrpl::checkFreeze : unequal input accounts");
|
||||
|
||||
// check freeze
|
||||
if (auto sle = view.read(keylet::account(dst)))
|
||||
if (auto sle = AccountRootEntryR(dst, view))
|
||||
{
|
||||
if (sle->isFlag(lsfGlobalFreeze))
|
||||
{
|
||||
@@ -49,7 +50,7 @@ checkFreeze(
|
||||
|
||||
if (view.rules().enabled(fixFrozenLPTokenTransfer))
|
||||
{
|
||||
if (auto const sleDst = view.read(keylet::account(dst));
|
||||
if (auto const sleDst = AccountRootEntryR(dst, view);
|
||||
sleDst && sleDst->isFieldPresent(sfAMMID))
|
||||
{
|
||||
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -56,8 +58,8 @@ private:
|
||||
Keylet const& sponsorshipKeylet,
|
||||
AccountID const& sponsorID,
|
||||
AccountID const& sponseeID,
|
||||
SLE::Ref sponsorAccSle,
|
||||
SLE::Ref reserveSponsorAccSle);
|
||||
AccountRootEntryW& sponsorAccSle,
|
||||
std::optional<AccountRootEntryW>& reserveSponsorAccSle);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
+73
-73
@@ -2,144 +2,144 @@ Detected OS: macos (Darwin arm64)
|
||||
|
||||
Core build tools:
|
||||
✅ cmake
|
||||
cmake version 4.4.3
|
||||
/nix/store/q85csxf4s4shx89zif097h1ql4ax1rrz-cmake-4.4.3/bin/cmake
|
||||
cmake version 4.1.2
|
||||
/nix/store/gvabsb4yqb5xsqzqph54rijnn4zpihnp-cmake-4.1.2/bin/cmake
|
||||
✅ conan
|
||||
Conan version 2.32.0
|
||||
/nix/store/921jqsgbilixmr3xchj95si0jl8pi13z-conan-2.32.0/bin/conan
|
||||
Conan version 2.28.1
|
||||
/nix/store/9jiyxmkpwmn6dcqs0765s83riw3l5ail-conan-2.28.1/bin/conan
|
||||
✅ git
|
||||
git version 2.55.0
|
||||
/nix/store/gw7c7m0dwca5lg4152x9lpmxpj171bzw-git-2.55.0/bin/git
|
||||
git version 2.54.0
|
||||
/nix/store/a14yxcqvv9x2l9mllgpirzhvz93pgprg-git-2.54.0/bin/git
|
||||
✅ python3
|
||||
Python 3.14.7
|
||||
/nix/store/0kcws5blwbdyxqxjd1w8hjipz61xny3k-python3-3.14.7/bin/python3.14
|
||||
Python 3.13.13
|
||||
/nix/store/ygxqin6ydzjfawywqpp5pal8wv6sf5bh-python3-3.13.13/bin/python3.13
|
||||
|
||||
Development tooling:
|
||||
✅ ccache
|
||||
ccache version 4.13.6
|
||||
/nix/store/ydr5nlzxp1djb5256y0zzqa87vz55gsk-ccache-4.13.6/bin/ccache
|
||||
/nix/store/57davyvs6p6dkrl3svzwg1ph18wsy4cz-ccache-4.13.6/bin/ccache
|
||||
✅ clang
|
||||
clang version 22.1.8
|
||||
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang
|
||||
clang version 22.1.7
|
||||
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang
|
||||
✅ clang-22
|
||||
clang version 22.1.8
|
||||
/nix/store/w826sqb98nnym6kzackg829664iqdlxj-clang-22/bin/clang-22
|
||||
clang version 22.1.7
|
||||
/nix/store/rbap7zqq7mw00fyqa02p5rj7gqjp4w5i-clang-22/bin/clang-22
|
||||
✅ clang++
|
||||
clang version 22.1.8
|
||||
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang++
|
||||
clang version 22.1.7
|
||||
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang++
|
||||
✅ clang++-22
|
||||
clang version 22.1.8
|
||||
/nix/store/wnwyw3yfqrhpxij65bl3pa1x65q2zg3m-clang++-22/bin/clang++-22
|
||||
clang version 22.1.7
|
||||
/nix/store/v9haf787f7bcz0mq1sad4bpyx21pj6li-clang++-22/bin/clang++-22
|
||||
✅ ClangBuildAnalyzer
|
||||
ClangBuildAnalyzer 1.6.0
|
||||
/nix/store/c7jjnw29ra78jdlzjww2m374izhy05n8-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
|
||||
/nix/store/4l50ds9fa2mkvh7wg8qzrlbmjs12sb8l-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
|
||||
✅ curl
|
||||
curl 8.22.0 (aarch64-apple-darwin25.6.0) libcurl/8.22.0 OpenSSL/3.5.8 zlib/1.3.2 libssh2/1.11.1 nghttp2/1.70.0 mit-krb5/1.22.2
|
||||
/nix/store/pq1h17abqy6q5y6k5dn9qj0fwb89flba-curl-8.22.0-bin/bin/curl
|
||||
curl 8.20.0 (aarch64-apple-darwin25.3.0) libcurl/8.20.0 OpenSSL/3.6.2 zlib/1.3.2 libssh2/1.11.1 nghttp2/1.69.0 mit-krb5/1.22.1
|
||||
/nix/store/kclq0czaxvsgh4ym9ld7b6iwy50l1snk-curl-8.20.0-bin/bin/curl
|
||||
✅ file
|
||||
file-5.48
|
||||
/nix/store/y44vpziwbzfm8020dzlqqbybyyy7lb6g-file-5.48/bin/file
|
||||
file-5.47
|
||||
/nix/store/dax63li7wwcbqxxkkgzc4g2rx7d4w86x-file-5.47/bin/file
|
||||
✅ less
|
||||
less 710 (PCRE2 regular expressions)
|
||||
/nix/store/3yn3dx5fwg5i0inqqzwnxq7f6lwhpjqa-less-710/bin/less
|
||||
less 692 (PCRE2 regular expressions)
|
||||
/nix/store/lvr16y75r1pdxpdv0aph5ak2yd0hkvqm-less-692/bin/less
|
||||
✅ make
|
||||
GNU Make 4.4.1
|
||||
/nix/store/8l4wgxmhdxfvc9g18y76bnxl1ghgcp7n-gnumake-4.4.1/bin/make
|
||||
/nix/store/8wwiw8pwyhrkzyq28hqzxfl4z84lks81-gnumake-4.4.1/bin/make
|
||||
✅ netstat
|
||||
present
|
||||
/nix/store/57fqvgdav8lrmzndkwm7qrgksl01yj23-network_cmds-741.100.2/bin/netstat
|
||||
/nix/store/qsd1kzqb0ahrk433vmyl245gp623j19s-network_cmds-730.80.3/bin/netstat
|
||||
✅ ninja
|
||||
1.13.2
|
||||
/nix/store/infm4vlx71q83bqz0k559w1gn4b0rq4i-ninja-1.13.2/bin/ninja
|
||||
/nix/store/bqykhrblarkj4fl0hz2mf8ngwfv6x6bz-ninja-1.13.2/bin/ninja
|
||||
✅ perl
|
||||
v5.42.3
|
||||
/nix/store/7rvr6qjfzk2ybzn1imjw6c6qq1n1a9cr-perl-5.42.3/bin/perl
|
||||
v5.42.0
|
||||
/nix/store/js13ri9fvm0ajk1fpd3acigys2a9whdv-perl-5.42.0/bin/perl
|
||||
✅ pkg-config
|
||||
0.29.2
|
||||
/nix/store/nhgylvj98klqggy54h7gbc3b2ia4a00y-pkg-config-wrapper-0.29.2/bin/pkg-config
|
||||
/nix/store/lzrwr375jqhhbca116kja96xf1md83l8-pkg-config-wrapper-0.29.2/bin/pkg-config
|
||||
✅ vim
|
||||
VIM - Vi IMproved 9.2 (2026 Feb 14, compiled Jan 01 1980 00:00:00)
|
||||
/nix/store/akaa9kqm3b8rlkv61kvsqr2l2j70rvxb-vim-9.2.1001/bin/vim
|
||||
/nix/store/6vbkykg92w603c0sw3mkk7p7mfaawbns-vim-9.2.0389/bin/vim
|
||||
✅ zip
|
||||
Zip 3.0
|
||||
/nix/store/dvawr36npnad4xa6dxi2ss0fw44aa82r-zip-3.0/bin/zip
|
||||
/nix/store/z6ph729vcakbvz3wh8ln1wk6mi06w487-zip-3.0/bin/zip
|
||||
✅ clang-apply-replacements
|
||||
clang-apply-replacements version 22.1.8
|
||||
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-apply-replacements
|
||||
clang-apply-replacements version 22.1.7
|
||||
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-apply-replacements
|
||||
✅ clang-apply-replacements-22
|
||||
clang-apply-replacements version 22.1.8
|
||||
/nix/store/w4bz034k4l0w719rrmbwnqcgyjbswy81-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
||||
clang-apply-replacements version 22.1.7
|
||||
/nix/store/m3ii69rca4077lf4wlk7m3jcag1fs577-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
||||
✅ clang-format
|
||||
clang-format version 22.1.8
|
||||
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-format
|
||||
clang-format version 22.1.7
|
||||
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-format
|
||||
✅ clang-format-22
|
||||
clang-format version 22.1.8
|
||||
/nix/store/w7z346l0r8y36b6lc5i7jzj3w0581l2y-clang-format-22/bin/clang-format-22
|
||||
clang-format version 22.1.7
|
||||
/nix/store/4fawqy6ngqcsqd2ygyyzm93q0xy3f5gs-clang-format-22/bin/clang-format-22
|
||||
✅ clang-tidy
|
||||
LLVM version 22.1.8
|
||||
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-tidy
|
||||
LLVM version 22.1.7
|
||||
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-tidy
|
||||
✅ clang-tidy-22
|
||||
LLVM version 22.1.8
|
||||
/nix/store/0lv25qd3ddgjm3ndn70q61jfr6krlkl7-clang-tidy-22/bin/clang-tidy-22
|
||||
LLVM version 22.1.7
|
||||
/nix/store/jqw4280saixaxxihwdba9ldm2fsm6dr3-clang-tidy-22/bin/clang-tidy-22
|
||||
✅ dot
|
||||
dot - graphviz version 15.1.1 (0)
|
||||
/nix/store/mc99a6bpbk2waym2cnw6ifmxli4ndb4x-graphviz-15.1.1/bin/dot
|
||||
dot - graphviz version 12.2.1 (0)
|
||||
/nix/store/ijb4fbnqa6wzlpqnhb6q9knqpf7qqn5z-graphviz-12.2.1/bin/dot
|
||||
✅ doxygen
|
||||
1.17.0
|
||||
/nix/store/kz43yaj0wg6fx6cbrcmi2442z1ramy0c-doxygen-1.17.0/bin/doxygen
|
||||
1.16.1
|
||||
/nix/store/kbryjdpq9jizjb0ws0nzbf2h2ymbdiwm-doxygen-1.16.1/bin/doxygen
|
||||
✅ gcovr
|
||||
gcovr 8.4
|
||||
/nix/store/czj1vxsizrbba280gg22cys40qxkqjgr-python3.14-gcovr-8.4/bin/gcovr
|
||||
/nix/store/wn8jiyh9p0bybs96s4163qp3k8vfmczx-python3.13-gcovr-8.4/bin/gcovr
|
||||
✅ gh
|
||||
gh version 2.102.0 (2026-09-30)
|
||||
/nix/store/s8bahl6zssvry93ihivfmk1l6c58byrr-gh-2.102.0/bin/gh
|
||||
gh version 2.94.0 (nixpkgs)
|
||||
/nix/store/fhnpw0hs0gjms1ha6ap02jq7rx13gkbp-gh-2.94.0/bin/gh
|
||||
✅ git-cliff
|
||||
git-cliff 2.14.2
|
||||
/nix/store/k4m5yk0qb7hfslwsq1zjajz1d94j12bj-git-cliff-2.14.2/bin/git-cliff
|
||||
git-cliff 2.13.1
|
||||
/nix/store/cy0wwhgxa7yvrz97zydbq6sqmixc90fq-git-cliff-2.13.1/bin/git-cliff
|
||||
✅ git-lfs
|
||||
git-lfs/3.8.0 (3.8.0; darwin arm64; go 1.26.8)
|
||||
/nix/store/wzixjr3yb40a13m2q654hm3bdjn6sal3-git-lfs-3.8.0/bin/git-lfs
|
||||
git-lfs/3.7.1 (3.7.1; darwin arm64; go 1.26.3)
|
||||
/nix/store/k9r7zjfjplqa4d5s71cqvf2iv73jd9mc-git-lfs-3.7.1/bin/git-lfs
|
||||
✅ gpg
|
||||
gpg (GnuPG) 2.4.9
|
||||
/nix/store/0ldfwnwx385i5kh7cyvxx4mkvgjhs5rh-gnupg-2.4.9/bin/gpg
|
||||
/nix/store/cgh6iwzz5jgx9z5whka4vgj210i6npc6-gnupg-2.4.9/bin/gpg
|
||||
✅ pre-commit
|
||||
pre-commit 4.6.2
|
||||
/nix/store/ivh54xypg9qvd3ha9lis0i8p4nkf8n7a-pre-commit-4.6.2/bin/pre-commit
|
||||
pre-commit 4.5.1
|
||||
/nix/store/z3cca68620w0w10f090szgzdnmh1waf2-pre-commit-4.5.1/bin/pre-commit
|
||||
✅ run-clang-tidy
|
||||
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
|
||||
/nix/store/3xss1mm3mh1hzcmg182na6ki1p5rnxf1-run-clang-tidy/bin/run-clang-tidy
|
||||
/nix/store/4x28x911z2f9y7adqlh3qspp4a16dig7-run-clang-tidy/bin/run-clang-tidy
|
||||
✅ run-clang-tidy-22
|
||||
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
|
||||
/nix/store/w68r9z07hcq4fwbfyq3yvc7qxm8aqfbl-run-clang-tidy-22/bin/run-clang-tidy-22
|
||||
/nix/store/x8iymrh76sk5q91ryg5pa7i32s6gfh34-run-clang-tidy-22/bin/run-clang-tidy-22
|
||||
|
||||
Rust toolchain:
|
||||
✅ cargo
|
||||
cargo 1.97.1 (c980f4866 2026-06-30)
|
||||
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/cargo
|
||||
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/cargo
|
||||
✅ cargo-audit
|
||||
cargo-audit-audit 0.22.2
|
||||
/nix/store/7jgrwj557l91940aj0ag4i924ima1cqn-cargo-audit-0.22.2/bin/cargo-audit
|
||||
cargo-audit-audit 0.22.1
|
||||
/nix/store/snwkga2f5gyf404h7mmp9wriwxb8v65f-cargo-audit-0.22.1/bin/cargo-audit
|
||||
✅ cargo-llvm-cov
|
||||
cargo-llvm-cov 0.9.0
|
||||
/nix/store/5q1h285z8zzkw2qxig9a0b1avcsk1cf0-cargo-llvm-cov-0.9.0/bin/cargo-llvm-cov
|
||||
cargo-llvm-cov 0.8.5
|
||||
/nix/store/fpiqdh91gwyxalqp409ynm0s0g086w7w-cargo-llvm-cov-0.8.5/bin/cargo-llvm-cov
|
||||
✅ cargo-nextest
|
||||
cargo-nextest 0.9.146
|
||||
/nix/store/69v3hx3k597hcxd2zy7yad7fg0aw5yxd-cargo-nextest-0.9.146/bin/cargo-nextest
|
||||
cargo-nextest 0.9.137
|
||||
/nix/store/ylz7m947mhkgsp6i7611id3s3gcd58nq-cargo-nextest-0.9.137/bin/cargo-nextest
|
||||
✅ clippy-driver
|
||||
clippy 0.1.97 (8bab26f4f6 2026-07-14)
|
||||
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/clippy-driver
|
||||
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/clippy-driver
|
||||
✅ rust-analyzer
|
||||
rust-analyzer 1.97.1 (8bab26f4 2026-07-14)
|
||||
/nix/store/30rbyk8gx7v53fxqhs4pp0fvxfjycspr-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
|
||||
/nix/store/j6apc5pmd0giy15da9p650r8zklslmvi-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
|
||||
✅ rust-nightly
|
||||
rustc 1.101.0-nightly (282215592 2026-10-04)
|
||||
/nix/store/a9552rf3pylcnkijc1z0hf1lcnv50djq-rust-nightly/bin/rust-nightly
|
||||
rustc 1.99.0-nightly (87e5904f5 2026-07-20)
|
||||
/nix/store/fqpjz4l0nsnji8b2pz57mnj0akbp6hcl-rust-nightly/bin/rust-nightly
|
||||
✅ rustc
|
||||
rustc 1.97.1 (8bab26f4f 2026-07-14)
|
||||
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/rustc
|
||||
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/rustc
|
||||
✅ rustfmt
|
||||
rustfmt 1.9.0-stable (8bab26f4f6 2026-07-14)
|
||||
/nix/store/0jmdipyn8266j9kqx2xxm2qkxlrlrjfc-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
|
||||
/nix/store/5ymwgr9jqjz7zzbmj0j5vqbwcd3kp0vm-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
|
||||
|
||||
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
||||
|
||||
|
||||
+1
-2
@@ -21,8 +21,7 @@ let
|
||||
)
|
||||
);
|
||||
|
||||
toolchain =
|
||||
if pkgs.stdenv.hostPlatform.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
|
||||
toolchain = if pkgs.stdenv.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
|
||||
in
|
||||
{
|
||||
default = pkgs.buildEnv {
|
||||
|
||||
+6
-7
@@ -20,16 +20,15 @@ let
|
||||
|
||||
# Custom-glibc stdenvs, matching the CI environment. darwin has no custom
|
||||
# glibc, so there they fall back to the plain nixpkgs stdenvs.
|
||||
customGccStdenv = if pkgs.stdenv.hostPlatform.isLinux then linux.gccStdenv else plainGccStdenv;
|
||||
customClangStdenv =
|
||||
if pkgs.stdenv.hostPlatform.isLinux then linux.clangStdenv else plainClangStdenv;
|
||||
customGccStdenv = if pkgs.stdenv.isLinux then linux.gccStdenv else plainGccStdenv;
|
||||
customClangStdenv = if pkgs.stdenv.isLinux then linux.clangStdenv else plainClangStdenv;
|
||||
|
||||
# gcov matching each gcc shell, so `-Dcoverage=ON` builds work in the shell.
|
||||
plainGcov = mkGcov {
|
||||
name = "plain";
|
||||
cc = gccPackage.cc;
|
||||
};
|
||||
customGccGcov = if pkgs.stdenv.hostPlatform.isLinux then linux.gcov else plainGcov;
|
||||
customGccGcov = if pkgs.stdenv.isLinux then linux.gcov else plainGcov;
|
||||
|
||||
# Whole directory: init.sh locates the profiles relative to itself.
|
||||
conanDir = ../conan;
|
||||
@@ -52,7 +51,7 @@ let
|
||||
|
||||
# Not sdkEnv: a shell's stdenv already sets that up. Prepended so the stub
|
||||
# beats the nixpkgs libresolv this shell's tooling drags in.
|
||||
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin (
|
||||
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.isDarwin (
|
||||
pkgs.lib.concatLines (
|
||||
pkgs.lib.mapAttrsToList (
|
||||
name: value: ''export ${name}="${value} ''${${name}:-}"''
|
||||
@@ -127,7 +126,7 @@ let
|
||||
in
|
||||
rec {
|
||||
# macOS: Nix Clang. Linux: Nix GCC.
|
||||
default = if pkgs.stdenv.hostPlatform.isDarwin then clang else gcc;
|
||||
default = if pkgs.stdenv.isDarwin then clang else gcc;
|
||||
|
||||
# gcc/clang use the custom-glibc toolchain, matching CI. On darwin there is no
|
||||
# custom glibc, so they fall back to the plain nixpkgs toolchain.
|
||||
@@ -172,7 +171,7 @@ rec {
|
||||
# The *-plain shells (stock nixpkgs toolchain) exist only on Linux: on darwin
|
||||
# gcc/clang are already plain, so these would be redundant and are omitted, which
|
||||
# makes `nix develop .#gcc-plain` fail there rather than silently aliasing gcc.
|
||||
// pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
|
||||
// pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||
gcc-plain = makeShell {
|
||||
shellName = "gcc-plain";
|
||||
stdenv = plainGccStdenv;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
CountedObjects&
|
||||
CountedObjects::getInstance() noexcept
|
||||
{
|
||||
static CountedObjects kInstance;
|
||||
|
||||
return kInstance;
|
||||
}
|
||||
|
||||
CountedObjects::CountedObjects() noexcept : count_(0), head_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CountedObjects::List
|
||||
CountedObjects::getCounts(int minimumThreshold) const
|
||||
{
|
||||
List counts;
|
||||
|
||||
// When other operations are concurrent, the count
|
||||
// might be temporarily less than the actual count.
|
||||
counts.reserve(count_.load());
|
||||
|
||||
for (auto* ctr = head_.load(); ctr != nullptr; ctr = ctr->getNext())
|
||||
{
|
||||
if (ctr->getCount() >= minimumThreshold)
|
||||
counts.emplace_back(ctr->getName(), ctr->getCount());
|
||||
}
|
||||
|
||||
std::ranges::sort(counts);
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -80,10 +80,10 @@ BookDirs::const_iterator::operator++()
|
||||
XRPL_ASSERT(index_ != kZero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index");
|
||||
if (!cdirNext(*view_, curKey_, sle_, entry_, index_))
|
||||
{
|
||||
if (index_ == kZero)
|
||||
if (index_ == 0)
|
||||
curKey_ = view_->succ(++curKey_, nextQuality_).value_or(kZero);
|
||||
|
||||
if (index_ != kZero || curKey_ == kZero)
|
||||
if (index_ != 0 || curKey_ == kZero)
|
||||
{
|
||||
curKey_ = key_;
|
||||
entry_ = 0;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/LedgerTiming.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
@@ -262,7 +261,7 @@ Ledger::Ledger(Ledger const& prevLedger, NetClock::time_point closeTime)
|
||||
{
|
||||
header_.seq = prevLedger.header_.seq + 1;
|
||||
header_.parentCloseTime = prevLedger.header_.closeTime;
|
||||
header_.hash = prevLedger.header().hash.next();
|
||||
header_.hash = prevLedger.header().hash + UInt256(1);
|
||||
header_.drops = prevLedger.header().drops;
|
||||
header_.closeTimeResolution = prevLedger.header_.closeTimeResolution;
|
||||
header_.parentHash = prevLedger.header().hash;
|
||||
@@ -561,7 +560,7 @@ Ledger::setup()
|
||||
|
||||
try
|
||||
{
|
||||
if (auto const sle = FeeSettingsEntryR(*this))
|
||||
if (auto const sle = read(keylet::feeSettings()))
|
||||
{
|
||||
bool oldFees = false;
|
||||
bool newFees = false;
|
||||
|
||||
+28
-17
@@ -9,7 +9,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/LedgerHashesEntry.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -116,8 +116,8 @@ isVaultPseudoAccountFrozenForIssuance(
|
||||
view, {issuer, account}, assetOfHolding(issuanceSle, *sleHolding), depth + 1);
|
||||
}
|
||||
|
||||
auto const mptIssuer = view.read(keylet::account(issuer));
|
||||
if (mptIssuer == nullptr)
|
||||
auto const mptIssuer = AccountRootEntryR(issuer, view);
|
||||
if (!mptIssuer.exists())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::isVaultPseudoAccountFrozen : null MPToken issuer");
|
||||
@@ -190,7 +190,7 @@ canTransferLPToken(
|
||||
{
|
||||
// Only AMM-issued LPTokens are subject to this check. The LPToken's issuer
|
||||
// is the AMM account; if it is not an AMM, this is not an LPToken.
|
||||
auto const sleIssuer = view.read(keylet::account(lpTokenIssuer));
|
||||
auto const sleIssuer = AccountRootEntryR(lpTokenIssuer, view);
|
||||
if (!sleIssuer || !sleIssuer->isFieldPresent(sfAMMID))
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -362,16 +362,17 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
|
||||
if (int const diff = ledger.seq() - seq; diff <= 256)
|
||||
{
|
||||
// Within 256...
|
||||
LedgerHashesEntryR const hashIndex(ledger, journal);
|
||||
auto const hashIndex = ledger.read(keylet::skip());
|
||||
if (hashIndex)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.seq() - 1),
|
||||
"xrpl::hashOfSeq : matching ledger sequence");
|
||||
if (auto const hash = hashIndex.hashAt(diff - 1))
|
||||
return hash;
|
||||
STVector256 vec = hashIndex->getFieldV256(sfHashes);
|
||||
if (vec.size() >= diff)
|
||||
return vec[vec.size() - diff];
|
||||
JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " ("
|
||||
<< hashIndex->getFieldV256(sfHashes).size() << "," << diff << ")";
|
||||
<< vec.size() << "," << diff << ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -387,14 +388,16 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
|
||||
}
|
||||
|
||||
// in skiplist
|
||||
LedgerHashesEntryR const hashIndex(keylet::skip(seq), ledger, journal);
|
||||
auto const hashIndex = ledger.read(keylet::skip(seq));
|
||||
if (hashIndex)
|
||||
{
|
||||
auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
|
||||
XRPL_ASSERT(lastSeq >= seq, "xrpl::hashOfSeq : minimum last ledger");
|
||||
XRPL_ASSERT((lastSeq & 0xff) == 0, "xrpl::hashOfSeq : valid last ledger");
|
||||
if (auto const hash = hashIndex.hashAt((lastSeq - seq) >> 8))
|
||||
return hash;
|
||||
auto const diff = (lastSeq - seq) >> 8;
|
||||
STVector256 vec = hashIndex->getFieldV256(sfHashes);
|
||||
if (vec.size() > diff)
|
||||
return vec[vec.size() - diff - 1];
|
||||
}
|
||||
JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " error";
|
||||
return std::nullopt;
|
||||
@@ -464,7 +467,7 @@ canWithdraw(
|
||||
ReadView const& view,
|
||||
AccountID const& from,
|
||||
AccountID const& to,
|
||||
SLE::ConstRef toSle,
|
||||
AccountRootEntryR const& toSle,
|
||||
STAmount const& amount,
|
||||
bool hasDestinationTag,
|
||||
std::optional<std::vector<UInt256>> const& credentialIDs)
|
||||
@@ -514,7 +517,7 @@ canWithdraw(
|
||||
bool hasDestinationTag,
|
||||
std::optional<std::vector<UInt256>> const& credentialIDs)
|
||||
{
|
||||
auto const toSle = view.read(keylet::account(to));
|
||||
auto const toSle = AccountRootEntryR(to, view);
|
||||
|
||||
return canWithdraw(view, from, to, toSle, amount, hasDestinationTag, credentialIDs);
|
||||
}
|
||||
@@ -539,7 +542,7 @@ doWithdraw(
|
||||
STAmount const& amount,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const dstSle = ctx.view.read(keylet::account(dstAcct));
|
||||
auto dstSle = AccountRootEntryW(dstAcct, ctx.view);
|
||||
|
||||
// Create a trust line or MPToken for a self-destination only when there
|
||||
// is a payout to credit. Post-fixCleanup3_4_0, a zero-value withdraw
|
||||
@@ -557,7 +560,8 @@ doWithdraw(
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto err = verifyDepositPreauth(ctx.tx, ctx.view, senderAcct, dstAcct, dstSle, j))
|
||||
if (auto err =
|
||||
verifyDepositPreauth(ctx.tx, ctx.view, senderAcct, dstAcct, dstSle.rawSle(), j))
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -580,14 +584,21 @@ doWithdraw(
|
||||
// sponsor against the destination. accountSend can auto-create a holding
|
||||
// for dstAcct; keying on the destination ensures a third-party destination's
|
||||
// holding is never stamped with the tx's reserve sponsor.
|
||||
auto const sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle);
|
||||
auto sponsorSle = getEffectiveTxReserveSponsor(ctx, dstSle);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
// Move the funds directly from the broker's pseudo-account to the
|
||||
// dstAcct
|
||||
auto& sponsor = *sponsorSle;
|
||||
return accountSend(
|
||||
ctx.view, sourceAcct, dstAcct, amount, j, *sponsorSle, WaiveTransferFee::Yes);
|
||||
ctx.view,
|
||||
sourceAcct,
|
||||
dstAcct,
|
||||
amount,
|
||||
j,
|
||||
sponsor ? sponsor->mutableRawSle() : SLE::pointer{},
|
||||
WaiveTransferFee::Yes);
|
||||
}
|
||||
|
||||
TER
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/OwnerCounts.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <typename ViewT>
|
||||
XRPAmount
|
||||
AccountRootEntry<ViewT>::reserve(Adjustment adj) const
|
||||
{
|
||||
auto const& view = this->readView();
|
||||
auto const j = this->journal();
|
||||
|
||||
XRPL_ASSERT(
|
||||
this->exists() && (*this)->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle");
|
||||
|
||||
if (!view.rules().enabled(featureSponsor))
|
||||
{
|
||||
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::accountReserve : no account count delta");
|
||||
return view.fees().accountReserve(
|
||||
(*this)->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
|
||||
}
|
||||
std::uint32_t const currentOwnerCount = ownerCount(adj.ownerCountDelta);
|
||||
std::uint32_t const currentAccountCount =
|
||||
detail::accountCountImpl(*this, adj.accountCountDelta, j);
|
||||
|
||||
return view.fees().accountReserve(currentOwnerCount, currentAccountCount);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
std::uint32_t
|
||||
AccountRootEntry<ViewT>::ownerCount(std::int32_t ownerCountAdj) const
|
||||
{
|
||||
auto const j = this->journal();
|
||||
|
||||
XRPL_ASSERT(
|
||||
this->exists() && (*this)->getType() == ltACCOUNT_ROOT,
|
||||
"xrpl::ownerCount : sle is account root");
|
||||
|
||||
AccountID const id = (*this)->getAccountID(sfAccount);
|
||||
std::uint32_t const currentOwnerCount = (*this)->at(sfOwnerCount);
|
||||
std::uint32_t const sponsoredOwnerCount = (*this)->at(sfSponsoredOwnerCount);
|
||||
std::uint32_t const sponsoringOwnerCount = (*this)->at(sfSponsoringOwnerCount);
|
||||
|
||||
XRPL_ASSERT(
|
||||
currentOwnerCount >= sponsoredOwnerCount,
|
||||
"xrpl::ownerCount : OwnerCount must be greater than or equal to SponsoredOwnerCount");
|
||||
|
||||
std::int64_t deltaCount =
|
||||
static_cast<std::int64_t>(ownerCountAdj) - sponsoredOwnerCount + sponsoringOwnerCount;
|
||||
|
||||
if (deltaCount > std::numeric_limits<std::int32_t>::max())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
deltaCount = std::numeric_limits<std::int32_t>::max();
|
||||
JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, "
|
||||
<< "adjustment: " << ownerCountAdj
|
||||
<< ", sponsoredCount: " << sponsoredOwnerCount
|
||||
<< ", sponsoringOwnerCount: " << sponsoringOwnerCount;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
else if (deltaCount < std::numeric_limits<std::int32_t>::min())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
deltaCount = std::numeric_limits<std::int32_t>::min();
|
||||
JLOG(j.fatal()) << "Account " << id << " delta count is below min, "
|
||||
<< "adjustment: " << ownerCountAdj
|
||||
<< ", sponsoredCount: " << sponsoredOwnerCount
|
||||
<< ", sponsoringCount: " << sponsoringOwnerCount;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return detail::confineOwnerCount(currentOwnerCount, deltaCount);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
TER
|
||||
AccountRootEntry<ViewT>::checkReserve(
|
||||
ApplyViewContext ctx,
|
||||
XRPAmount accBalance,
|
||||
std::optional<AccountRootEntry<ReadView>> const& sponsorSle,
|
||||
Adjustment adj,
|
||||
TER insufReserveCode) const
|
||||
{
|
||||
// TODO: swap to assert after fixCleanup3_2_0 is retired
|
||||
if (!this->exists() || (*this)->getType() != ltACCOUNT_ROOT)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
XRPL_ASSERT(
|
||||
!isTesSuccess(insufReserveCode), "xrpl::checkReserve : insufReserveCode is not tesSUCCESS");
|
||||
if (ctx.view.rules().enabled(featureSponsor))
|
||||
{
|
||||
if (sponsorSle)
|
||||
{
|
||||
if ((*sponsorSle)->getType() != ltACCOUNT_ROOT)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sle = ctx.view.read(
|
||||
keylet::sponsorship(
|
||||
(*sponsorSle)->getAccountID(sfAccount), (*this)->getAccountID(sfAccount)));
|
||||
|
||||
// A reserve-sponsored tx must carry a sponsor signature
|
||||
// (cosigning path) and/or have a pre-existing sponsorship SLE
|
||||
// (prefunded path). Absence of both is an internal invariant break.
|
||||
if (isReserveSponsored(ctx.tx) && !sle && !ctx.tx.isFieldPresent(sfSponsorSignature))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (sle)
|
||||
{
|
||||
auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount);
|
||||
if (adj.ownerCountDelta > 0 &&
|
||||
ownerCountAllowed < static_cast<std::uint32_t>(adj.ownerCountDelta))
|
||||
return insufReserveCode;
|
||||
}
|
||||
|
||||
auto const sponsorBalance = (*sponsorSle)->getFieldAmount(sfBalance).xrp();
|
||||
XRPAmount const sponsorReserve = sponsorSle->reserve(adj);
|
||||
|
||||
if (sponsorBalance < sponsorReserve)
|
||||
return insufReserveCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
XRPAmount const reserve = this->reserve(adj);
|
||||
if (accBalance < reserve)
|
||||
return insufReserveCode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!sponsorSle,
|
||||
"xrpl::checkReserve : featureSponsor disabled and sponsorSle not provided");
|
||||
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::checkReserve : accountCountDelta is 0");
|
||||
auto const reserve = ctx.view.fees().accountReserve(
|
||||
(*this)->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
|
||||
if (accBalance < reserve)
|
||||
return insufReserveCode;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
TER
|
||||
AccountRootEntry<ViewT>::checkReserve(ApplyViewContext ctx, XRPAmount accBalance, Adjustment adj)
|
||||
const
|
||||
{
|
||||
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, *this);
|
||||
if (!sponsorExp)
|
||||
return sponsorExp.error(); // LCOV_EXCL_LINE
|
||||
|
||||
std::optional<AccountRootEntry<ReadView>> sponsorSle;
|
||||
if (*sponsorExp)
|
||||
sponsorSle.emplace(**sponsorExp);
|
||||
return checkReserve(ctx, accBalance, sponsorSle, adj);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
void
|
||||
AccountRootEntry<ViewT>::adjustOwnerCountSigned(
|
||||
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
|
||||
std::int32_t adjustment)
|
||||
requires Base::kIsWritable
|
||||
{
|
||||
auto& view = this->applyView();
|
||||
auto const j = this->journal();
|
||||
|
||||
if (view.rules().enabled(featureSponsor))
|
||||
{
|
||||
XRPL_ASSERT(this->exists(), "xrpl::adjustOwnerCountSigned : valid account sle");
|
||||
if (!this->exists())
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
auto const accountID = (*this)->getAccountID(sfAccount);
|
||||
bool const validType = (*this)->getType() == ltACCOUNT_ROOT;
|
||||
XRPL_ASSERT(validType, "xrpl::adjustOwnerCountSigned : valid account sle type");
|
||||
if (!validType)
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCountSigned : nonzero adjustment input");
|
||||
|
||||
OwnerCounts const currentOwnerCount(this->rawSle());
|
||||
OwnerCounts totalOwnerCount(currentOwnerCount);
|
||||
|
||||
if (sponsorSle)
|
||||
{
|
||||
bool const validSponsorType = (*sponsorSle)->getType() == ltACCOUNT_ROOT;
|
||||
XRPL_ASSERT(validSponsorType, "xrpl::adjustOwnerCountSigned : valid sponsor sle type");
|
||||
if (!validSponsorType)
|
||||
return; // LCOV_EXCL_LINE
|
||||
auto const sponsorID = (*sponsorSle)->getAccountID(sfAccount);
|
||||
|
||||
totalOwnerCount.sponsored = detail::adjustOwnerCountImpl(
|
||||
view, this->mutableRawSle(), sfSponsoredOwnerCount, accountID, adjustment, j);
|
||||
|
||||
{
|
||||
OwnerCounts const sponsorCurrent(sponsorSle->rawSle());
|
||||
OwnerCounts sponsorAdjustment(sponsorCurrent);
|
||||
sponsorAdjustment.sponsoring = detail::adjustOwnerCountImpl(
|
||||
view,
|
||||
sponsorSle->mutableRawSle(),
|
||||
sfSponsoringOwnerCount,
|
||||
sponsorID,
|
||||
adjustment,
|
||||
j);
|
||||
view.adjustOwnerCountHook(sponsorID, sponsorCurrent, sponsorAdjustment);
|
||||
}
|
||||
|
||||
auto sponsorshipSle = view.peek(keylet::sponsorship(sponsorID, accountID));
|
||||
if (sponsorshipSle && adjustment > 0)
|
||||
{
|
||||
// Only decrease the pre-funded ReserveCount on Sponsorship if we assign new
|
||||
// objects. Removing/reassigning ownership of the object doesn't increase
|
||||
// RemainingOwnerCount back. Don't call hook because this counter is not something
|
||||
// that requires reserve (like other sf...OwnerCounts do).
|
||||
detail::adjustOwnerCountImpl(
|
||||
view, sponsorshipSle, sfRemainingOwnerCount, sponsorID, -adjustment, j);
|
||||
}
|
||||
}
|
||||
|
||||
totalOwnerCount.owner = detail::adjustOwnerCountImpl(
|
||||
view, this->mutableRawSle(), sfOwnerCount, accountID, adjustment, j);
|
||||
view.adjustOwnerCountHook(accountID, currentOwnerCount, totalOwnerCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
XRPL_ASSERT(this->exists(), "xrpl::adjustOwnerCountSigned : valid account sle");
|
||||
if (!this->exists())
|
||||
return;
|
||||
// the remaining are only asserts to preserve existing behavior
|
||||
XRPL_ASSERT(!sponsorSle, "xrpl::adjustOwnerCountSigned : sponsor not enabled");
|
||||
XRPL_ASSERT(
|
||||
(*this)->getType() == ltACCOUNT_ROOT,
|
||||
"xrpl::adjustOwnerCountSigned : valid account sle type");
|
||||
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input");
|
||||
std::uint32_t const current{(*this)->getFieldU32(sfOwnerCount)};
|
||||
AccountID const id = (**this)[sfAccount];
|
||||
std::uint32_t const adjusted = detail::confineOwnerCount(current, adjustment, id, j);
|
||||
|
||||
OwnerCounts const currentOwnerCount(this->rawSle());
|
||||
OwnerCounts finalOwnerCount(currentOwnerCount);
|
||||
finalOwnerCount.owner = adjusted;
|
||||
|
||||
view.adjustOwnerCountHook(id, currentOwnerCount, finalOwnerCount);
|
||||
(*this)->at(sfOwnerCount) = adjusted;
|
||||
this->update();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
void
|
||||
AccountRootEntry<ViewT>::increaseOwnerCount(
|
||||
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
|
||||
std::uint32_t count)
|
||||
requires Base::kIsWritable
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
|
||||
"xrpl::increaseOwnerCount : count in signed delta range");
|
||||
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCountSigned(sponsorSle, static_cast<std::int32_t>(count));
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
void
|
||||
AccountRootEntry<ViewT>::increaseOwnerCount(ApplyViewContext ctx, std::uint32_t count)
|
||||
requires Base::kIsWritable
|
||||
{
|
||||
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, *this);
|
||||
|
||||
// The sponsor's existence is validated by checkReserve/checkSponsor before
|
||||
// any owner-count mutation, so loading it here cannot fail.
|
||||
XRPL_ASSERT(
|
||||
sponsorExp.has_value(), "xrpl::increaseOwnerCount : sponsor validated before mutation");
|
||||
|
||||
std::optional<AccountRootEntry<ApplyView>> sponsorSle;
|
||||
if (sponsorExp && *sponsorExp)
|
||||
sponsorSle.emplace(std::move(**sponsorExp));
|
||||
|
||||
increaseOwnerCount(sponsorSle, count);
|
||||
}
|
||||
|
||||
template <typename ViewT>
|
||||
void
|
||||
AccountRootEntry<ViewT>::decreaseOwnerCount(
|
||||
std::optional<AccountRootEntry<ApplyView>>& sponsorSle,
|
||||
std::uint32_t count)
|
||||
requires Base::kIsWritable
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
|
||||
"xrpl::decreaseOwnerCount : count in signed delta range");
|
||||
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCountSigned(sponsorSle, -static_cast<std::int32_t>(count));
|
||||
}
|
||||
|
||||
template class AccountRootEntry<ReadView>;
|
||||
template class AccountRootEntry<ApplyView>;
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -641,7 +642,7 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
|
||||
[&](Issue const& issue) {
|
||||
if (isXRP(issue))
|
||||
{
|
||||
if (auto const sle = view.read(keylet::account(ammAccountID)))
|
||||
if (auto const sle = AccountRootEntryR(ammAccountID, view))
|
||||
return (*sle)[sfBalance];
|
||||
}
|
||||
else if (
|
||||
@@ -757,7 +758,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
|
||||
}
|
||||
|
||||
auto const ammAccountID = (*ammSle)[sfAccount];
|
||||
auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
|
||||
auto sleAMMRoot = AccountRootEntryW(ammAccountID, sb);
|
||||
if (!sleAMMRoot)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -795,7 +796,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
|
||||
}
|
||||
|
||||
sb.erase(ammSle);
|
||||
sb.erase(sleAMMRoot);
|
||||
sleAMMRoot.erase();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/OwnerCounts.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include <xrpl/protocol/Rate.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
@@ -38,25 +38,19 @@ isGlobalFrozen(ReadView const& view, AccountID const& issuer)
|
||||
{
|
||||
if (isXRP(issuer))
|
||||
return false;
|
||||
if (auto const sle = view.read(keylet::account(issuer)))
|
||||
if (auto const sle = AccountRootEntryR(issuer, view))
|
||||
return sle->isFlag(lsfGlobalFreeze);
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
namespace detail {
|
||||
|
||||
// An owner count cannot be negative. If adjustment would cause a negative
|
||||
// owner count, clamp the owner count at 0. Similarly for overflow. This
|
||||
// adjustment allows the ownerCount to be adjusted up or down in multiple steps.
|
||||
// If id != std::nullopt, then do error reporting.
|
||||
//
|
||||
// Returns adjusted owner count.
|
||||
std::uint32_t
|
||||
confineOwnerCount(
|
||||
std::uint32_t currentOwnerCount,
|
||||
std::int32_t ownerCountAdj,
|
||||
std::optional<AccountID> const& id = std::nullopt,
|
||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||
std::optional<AccountID> const& id,
|
||||
beast::Journal j)
|
||||
{
|
||||
std::uint32_t totalOwnerCount{currentOwnerCount + ownerCountAdj};
|
||||
if (ownerCountAdj > 0)
|
||||
@@ -91,10 +85,8 @@ confineOwnerCount(
|
||||
return totalOwnerCount;
|
||||
}
|
||||
|
||||
// Returns the number of account reserves funded by this account: 1 for itself (0 if sponsored by
|
||||
// another account) plus the count of accounts it sponsors.
|
||||
std::uint32_t
|
||||
accountCountImpl(SLE::ConstRef sle, std::int32_t accountCountAdj, beast::Journal j)
|
||||
accountCountImpl(AccountRootEntryR const& sle, std::int32_t accountCountAdj, beast::Journal j)
|
||||
{
|
||||
bool const isSponsored = sle->isFieldPresent(sfSponsor);
|
||||
std::int64_t const sponsoringAccountCount = sle->getFieldU32(sfSponsoringAccountCount);
|
||||
@@ -131,151 +123,25 @@ adjustOwnerCountImpl(
|
||||
{
|
||||
std::uint32_t const currentOwnerCount = sle->at(sfield);
|
||||
std::uint32_t const totalOwnerCount =
|
||||
confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j);
|
||||
detail::confineOwnerCount(currentOwnerCount, ownerCountAdj, accID, j);
|
||||
sle->at(sfield) = totalOwnerCount;
|
||||
view.update(sle);
|
||||
return totalOwnerCount;
|
||||
}
|
||||
|
||||
void
|
||||
adjustOwnerCountSigned(
|
||||
ApplyView& view,
|
||||
SLE::Ref accountSle,
|
||||
SLE::Ref sponsorSle,
|
||||
std::int32_t adjustment,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (view.rules().enabled(featureSponsor))
|
||||
{
|
||||
XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCountSigned : valid account sle");
|
||||
if (!accountSle)
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
auto const accountID = accountSle->getAccountID(sfAccount);
|
||||
bool const validType = accountSle->getType() == ltACCOUNT_ROOT;
|
||||
XRPL_ASSERT(validType, "xrpl::adjustOwnerCountSigned : valid account sle type");
|
||||
if (!validType)
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCountSigned : nonzero adjustment input");
|
||||
|
||||
OwnerCounts const currentOwnerCount(accountSle);
|
||||
OwnerCounts totalOwnerCount(currentOwnerCount);
|
||||
|
||||
if (sponsorSle)
|
||||
{
|
||||
bool const validSponsorType = sponsorSle->getType() == ltACCOUNT_ROOT;
|
||||
XRPL_ASSERT(validSponsorType, "xrpl::adjustOwnerCountSigned : valid sponsor sle type");
|
||||
if (!validSponsorType)
|
||||
return; // LCOV_EXCL_LINE
|
||||
auto const sponsorID = sponsorSle->getAccountID(sfAccount);
|
||||
|
||||
totalOwnerCount.sponsored = adjustOwnerCountImpl(
|
||||
view, accountSle, sfSponsoredOwnerCount, accountID, adjustment, j);
|
||||
|
||||
{
|
||||
OwnerCounts const sponsorCurrent(sponsorSle);
|
||||
OwnerCounts sponsorAdjustment(sponsorCurrent);
|
||||
sponsorAdjustment.sponsoring = adjustOwnerCountImpl(
|
||||
view, sponsorSle, sfSponsoringOwnerCount, sponsorID, adjustment, j);
|
||||
view.adjustOwnerCountHook(sponsorID, sponsorCurrent, sponsorAdjustment);
|
||||
}
|
||||
|
||||
auto sponsorshipSle = view.peek(keylet::sponsorship(sponsorID, accountID));
|
||||
if (sponsorshipSle && adjustment > 0)
|
||||
{
|
||||
// Only decrease the pre-funded ReserveCount on Sponsorship if we assign new
|
||||
// objects. Removing/reassigning ownership of the object doesn't increase
|
||||
// RemainingOwnerCount back. Don't call hook because this counter is not something
|
||||
// that requires reserve (like other sf...OwnerCounts do).
|
||||
adjustOwnerCountImpl(
|
||||
view, sponsorshipSle, sfRemainingOwnerCount, sponsorID, -adjustment, j);
|
||||
}
|
||||
}
|
||||
|
||||
totalOwnerCount.owner =
|
||||
adjustOwnerCountImpl(view, accountSle, sfOwnerCount, accountID, adjustment, j);
|
||||
view.adjustOwnerCountHook(accountID, currentOwnerCount, totalOwnerCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
XRPL_ASSERT(accountSle, "xrpl::adjustOwnerCountSigned : valid account sle");
|
||||
if (!accountSle)
|
||||
return;
|
||||
// the remaining are only asserts to preserve existing behavior
|
||||
XRPL_ASSERT(sponsorSle == nullptr, "xrpl::adjustOwnerCountSigned : sponsor not enabled");
|
||||
XRPL_ASSERT(
|
||||
accountSle->getType() == ltACCOUNT_ROOT,
|
||||
"xrpl::adjustOwnerCountSigned : valid account sle type");
|
||||
XRPL_ASSERT(adjustment, "xrpl::adjustOwnerCount : nonzero adjustment input");
|
||||
std::uint32_t const current{accountSle->getFieldU32(sfOwnerCount)};
|
||||
AccountID const id = (*accountSle)[sfAccount];
|
||||
std::uint32_t const adjusted = confineOwnerCount(current, adjustment, id, j);
|
||||
|
||||
OwnerCounts const currentOwnerCount(accountSle);
|
||||
OwnerCounts finalOwnerCount(currentOwnerCount);
|
||||
finalOwnerCount.owner = adjusted;
|
||||
|
||||
view.adjustOwnerCountHook(id, currentOwnerCount, finalOwnerCount);
|
||||
accountSle->at(sfOwnerCount) = adjusted;
|
||||
view.update(accountSle);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::uint32_t
|
||||
ownerCount(SLE::ConstRef sle, beast::Journal j, std::int32_t ownerCountAdj)
|
||||
{
|
||||
XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::ownerCount : sle is account root");
|
||||
|
||||
AccountID const id = sle->getAccountID(sfAccount);
|
||||
std::uint32_t const currentOwnerCount = sle->at(sfOwnerCount);
|
||||
std::uint32_t const sponsoredOwnerCount = sle->at(sfSponsoredOwnerCount);
|
||||
std::uint32_t const sponsoringOwnerCount = sle->at(sfSponsoringOwnerCount);
|
||||
|
||||
XRPL_ASSERT(
|
||||
currentOwnerCount >= sponsoredOwnerCount,
|
||||
"xrpl::ownerCount : OwnerCount must be greater than or equal to SponsoredOwnerCount");
|
||||
|
||||
std::int64_t deltaCount =
|
||||
static_cast<std::int64_t>(ownerCountAdj) - sponsoredOwnerCount + sponsoringOwnerCount;
|
||||
|
||||
if (deltaCount > std::numeric_limits<std::int32_t>::max())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
deltaCount = std::numeric_limits<std::int32_t>::max();
|
||||
JLOG(j.fatal()) << "Account " << id << " delta count exceeds max, "
|
||||
<< "adjustment: " << ownerCountAdj
|
||||
<< ", sponsoredCount: " << sponsoredOwnerCount
|
||||
<< ", sponsoringOwnerCount: " << sponsoringOwnerCount;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
else if (deltaCount < std::numeric_limits<std::int32_t>::min())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
deltaCount = std::numeric_limits<std::int32_t>::min();
|
||||
JLOG(j.fatal()) << "Account " << id << " delta count is below min, "
|
||||
<< "adjustment: " << ownerCountAdj
|
||||
<< ", sponsoredCount: " << sponsoredOwnerCount
|
||||
<< ", sponsoringCount: " << sponsoringOwnerCount;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return confineOwnerCount(currentOwnerCount, deltaCount);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
XRPAmount
|
||||
xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj, beast::Journal j)
|
||||
{
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
if (sle == nullptr)
|
||||
auto const sle = AccountRootEntryR(id, view);
|
||||
if (!sle.exists())
|
||||
return beast::kZero;
|
||||
|
||||
// Return balance minus reserve
|
||||
std::uint32_t const currentOwnerCount =
|
||||
confineOwnerCount(view.ownerCountHook(id, OwnerCounts(sle)).count(), ownerCountAdj);
|
||||
std::uint32_t const currentAccountCount = accountCountImpl(sle, 0, j);
|
||||
std::uint32_t const currentOwnerCount = detail::confineOwnerCount(
|
||||
view.ownerCountHook(id, OwnerCounts(sle.rawSle())).count(), ownerCountAdj);
|
||||
std::uint32_t const currentAccountCount = detail::accountCountImpl(sle, 0, j);
|
||||
|
||||
// Pseudo-accounts have no reserve requirement
|
||||
auto const reserve = isPseudoAccount(sle)
|
||||
@@ -300,7 +166,7 @@ xrpLiquid(ReadView const& view, AccountID const& id, std::int32_t ownerCountAdj,
|
||||
Rate
|
||||
transferRate(ReadView const& view, AccountID const& issuer)
|
||||
{
|
||||
auto const sle = view.read(keylet::account(issuer));
|
||||
auto const sle = AccountRootEntryR(issuer, view);
|
||||
|
||||
if (sle && sle->isFieldPresent(sfTransferRate))
|
||||
return Rate{sle->getFieldU32(sfTransferRate)};
|
||||
@@ -308,57 +174,10 @@ transferRate(ReadView const& view, AccountID const& issuer)
|
||||
return kParityRate;
|
||||
}
|
||||
|
||||
void
|
||||
increaseOwnerCount(
|
||||
ApplyView& view,
|
||||
SLE::Ref accountSle,
|
||||
SLE::Ref sponsorSle,
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
|
||||
"xrpl::increaseOwnerCount : count in signed delta range");
|
||||
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCountSigned(view, accountSle, sponsorSle, static_cast<std::int32_t>(count), j);
|
||||
}
|
||||
|
||||
void
|
||||
increaseOwnerCount(ApplyViewContext ctx, SLE::Ref accountSle, std::uint32_t count, beast::Journal j)
|
||||
{
|
||||
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, accountSle);
|
||||
|
||||
// The sponsor's existence is validated by checkReserve/checkSponsor before
|
||||
// any owner-count mutation, so loading it here cannot fail.
|
||||
XRPL_ASSERT(
|
||||
sponsorExp.has_value(), "xrpl::increaseOwnerCount : sponsor validated before mutation");
|
||||
|
||||
increaseOwnerCount(ctx.view, accountSle, sponsorExp ? *sponsorExp : SLE::pointer(), count, j);
|
||||
}
|
||||
|
||||
void
|
||||
decreaseOwnerCount(
|
||||
ApplyView& view,
|
||||
SLE::Ref accountSle,
|
||||
SLE::Ref sponsorSle,
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
count != 0 && count <= std::numeric_limits<std::int32_t>::max(),
|
||||
"xrpl::decreaseOwnerCount : count in signed delta range");
|
||||
if (count == 0 || count > std::numeric_limits<std::int32_t>::max())
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCountSigned(view, accountSle, sponsorSle, -static_cast<std::int32_t>(count), j);
|
||||
}
|
||||
|
||||
void
|
||||
decreaseOwnerCountForObject(
|
||||
ApplyView& view,
|
||||
SLE::Ref accountSle,
|
||||
AccountRootEntryW& accountSle,
|
||||
SLE::Ref objectSle,
|
||||
std::uint32_t count,
|
||||
beast::Journal j)
|
||||
@@ -372,8 +191,8 @@ decreaseOwnerCountForObject(
|
||||
if (!validObjectType)
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
SLE::Ref sponsorSle = getLedgerEntryReserveSponsor(view, objectSle);
|
||||
decreaseOwnerCount(view, accountSle, sponsorSle, count, j);
|
||||
auto sponsorSle = getLedgerEntryReserveSponsor(view, objectSle);
|
||||
accountSle.decreaseOwnerCount(sponsorSle, count);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -393,107 +212,10 @@ adjustLoanBrokerOwnerCount(
|
||||
if (delta == 0)
|
||||
return; // LCOV_EXCL_LINE
|
||||
|
||||
adjustOwnerCountImpl(
|
||||
detail::adjustOwnerCountImpl(
|
||||
view, brokerSle, sfOwnerCount, brokerSle->getAccountID(sfAccount), delta, j);
|
||||
}
|
||||
|
||||
XRPAmount
|
||||
accountReserve(ReadView const& view, SLE::ConstRef sle, beast::Journal j, Adjustment adj)
|
||||
{
|
||||
XRPL_ASSERT(sle && sle->getType() == ltACCOUNT_ROOT, "xrpl::accountReserve : valid sle");
|
||||
|
||||
if (!view.rules().enabled(featureSponsor))
|
||||
{
|
||||
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::accountReserve : no account count delta");
|
||||
return view.fees().accountReserve(sle->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
|
||||
}
|
||||
std::uint32_t const currentOwnerCount = ownerCount(sle, j, adj.ownerCountDelta);
|
||||
std::uint32_t const currentAccountCount = accountCountImpl(sle, adj.accountCountDelta, j);
|
||||
|
||||
return view.fees().accountReserve(currentOwnerCount, currentAccountCount);
|
||||
}
|
||||
|
||||
TER
|
||||
checkReserve(
|
||||
ApplyViewContext ctx,
|
||||
SLE::ConstRef accSle,
|
||||
XRPAmount accBalance,
|
||||
SLE::ConstRef sponsorSle,
|
||||
Adjustment adj,
|
||||
beast::Journal j,
|
||||
TER insufReserveCode)
|
||||
{
|
||||
// TODO: swap to assert after fixCleanup3_2_0 is retired
|
||||
if (!accSle || accSle->getType() != ltACCOUNT_ROOT)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
XRPL_ASSERT(
|
||||
!isTesSuccess(insufReserveCode), "xrpl::checkReserve : insufReserveCode is not tesSUCCESS");
|
||||
if (ctx.view.rules().enabled(featureSponsor))
|
||||
{
|
||||
if (sponsorSle)
|
||||
{
|
||||
if (sponsorSle->getType() != ltACCOUNT_ROOT)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sle = ctx.view.read(
|
||||
keylet::sponsorship(
|
||||
sponsorSle->getAccountID(sfAccount), accSle->getAccountID(sfAccount)));
|
||||
|
||||
// A reserve-sponsored tx must carry a sponsor signature
|
||||
// (cosigning path) and/or have a pre-existing sponsorship SLE
|
||||
// (prefunded path). Absence of both is an internal invariant break.
|
||||
if (isReserveSponsored(ctx.tx) && !sle && !ctx.tx.isFieldPresent(sfSponsorSignature))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (sle)
|
||||
{
|
||||
auto const ownerCountAllowed = sle->getFieldU32(sfRemainingOwnerCount);
|
||||
if (adj.ownerCountDelta > 0 &&
|
||||
ownerCountAllowed < static_cast<std::uint32_t>(adj.ownerCountDelta))
|
||||
return insufReserveCode;
|
||||
}
|
||||
|
||||
auto const sponsorBalance = sponsorSle->getFieldAmount(sfBalance).xrp();
|
||||
XRPAmount const sponsorReserve = accountReserve(ctx.view, sponsorSle, j, adj);
|
||||
|
||||
if (sponsorBalance < sponsorReserve)
|
||||
return insufReserveCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
XRPAmount const reserve = accountReserve(ctx.view, accSle, j, adj);
|
||||
if (accBalance < reserve)
|
||||
return insufReserveCode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!sponsorSle,
|
||||
"xrpl::checkReserve : featureSponsor disabled and sponsorSle not provided");
|
||||
XRPL_ASSERT(adj.accountCountDelta == 0, "xrpl::checkReserve : accountCountDelta is 0");
|
||||
auto const reserve = ctx.view.fees().accountReserve(
|
||||
accSle->getFieldU32(sfOwnerCount) + adj.ownerCountDelta, 1);
|
||||
if (accBalance < reserve)
|
||||
return insufReserveCode;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
checkReserve(
|
||||
ApplyViewContext ctx,
|
||||
SLE::ConstRef accSle,
|
||||
XRPAmount accBalance,
|
||||
Adjustment adj,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, accSle);
|
||||
if (!sponsorExp)
|
||||
return sponsorExp.error(); // LCOV_EXCL_LINE
|
||||
return checkReserve(ctx, accSle, accBalance, *sponsorExp, adj, j);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
AccountID
|
||||
@@ -506,8 +228,8 @@ pseudoAccountAddress(ReadView const& view, UInt256 const& pseudoOwnerKey)
|
||||
RipeshaHasher rsh;
|
||||
auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey);
|
||||
rsh(hash.data(), hash.size());
|
||||
AccountID const ret{static_cast<RipeshaHasher::result_type>(rsh)};
|
||||
if (!view.read(keylet::account(ret)))
|
||||
AccountID const ret = AccountID::fromRaw(static_cast<RipeshaHasher::result_type>(rsh));
|
||||
if (!AccountRootEntryR(ret, view).exists())
|
||||
return ret;
|
||||
}
|
||||
return beast::kZero;
|
||||
@@ -546,7 +268,7 @@ getPseudoAccountFields()
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isPseudoAccount(SLE::const_pointer sleAcct)
|
||||
isPseudoAccount(AccountRootEntryR const& sleAcct)
|
||||
{
|
||||
// Intentionally use defensive coding here because it's cheap and makes the
|
||||
// semantics of true return value clean.
|
||||
@@ -599,9 +321,9 @@ createPseudoAccount(ApplyView& view, UInt256 const& pseudoOwnerKey, SField const
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
checkDestinationAndTag(SLE::ConstRef toSle, bool hasDestinationTag)
|
||||
checkDestinationAndTag(AccountRootEntryR const& toSle, bool hasDestinationTag)
|
||||
{
|
||||
if (toSle == nullptr)
|
||||
if (!toSle.exists())
|
||||
return tecNO_DST;
|
||||
|
||||
// The tag is basically account-specific information we don't
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -81,7 +82,7 @@ deleteSLE(ApplyView& view, SLE::Ref sleCredential, beast::Journal j)
|
||||
|
||||
auto delSLE = [&view, &sleCredential, j](
|
||||
AccountID const& account, SField const& node, bool isOwner) -> TER {
|
||||
auto const sleAccount = view.peek(keylet::account(account));
|
||||
auto sleAccount = AccountRootEntryW(account, view);
|
||||
if (!sleAccount)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -206,7 +207,7 @@ authorizeMPToken(
|
||||
std::uint32_t flags,
|
||||
std::optional<AccountID> holderID)
|
||||
{
|
||||
auto const sleAcct = ctx.view.peek(keylet::account(account));
|
||||
auto sleAcct = AccountRootEntryW(account, ctx.view, journal);
|
||||
if (!sleAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -242,10 +243,10 @@ authorizeMPToken(
|
||||
// - create the MPToken object for the holder
|
||||
|
||||
// A reserve sponsor only covers tx.Account's own objects.
|
||||
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct);
|
||||
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, sleAcct);
|
||||
if (!sponsorExp)
|
||||
return sponsorExp.error(); // LCOV_EXCL_LINE
|
||||
auto const sponsorSle = *sponsorExp;
|
||||
auto& sponsorSle = *sponsorExp;
|
||||
|
||||
// The reserve that is required to create the MPToken. Note
|
||||
// that although the reserve increases with every item
|
||||
@@ -255,10 +256,10 @@ authorizeMPToken(
|
||||
// The "free-tier" shortcut (ownerCount < 2) does not apply once a sponsor is on
|
||||
// the tx — the sponsor must always cover the reserve (via balance or prefunded
|
||||
// budget), so this check always runs for sponsored transactions.
|
||||
if (sponsorSle || ownerCount(sleAcct, journal) >= 2)
|
||||
if (sponsorSle || sleAcct.ownerCount() >= 2)
|
||||
{
|
||||
if (auto const ret = checkReserve(
|
||||
ctx, sleAcct, priorBalance, sponsorSle, {.ownerCountDelta = 1}, journal);
|
||||
if (auto const ret =
|
||||
sleAcct.checkReserve(ctx, priorBalance, sponsorSle, {.ownerCountDelta = 1});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
}
|
||||
@@ -285,8 +286,8 @@ authorizeMPToken(
|
||||
ctx.view.insert(mptoken);
|
||||
|
||||
// Update owner count.
|
||||
increaseOwnerCount(ctx.view, sleAcct, sponsorSle, 1, journal);
|
||||
addSponsorToLedgerEntry(mptoken, sponsorSle);
|
||||
sleAcct.increaseOwnerCount(sponsorSle, 1);
|
||||
addSponsorToLedgerEntry(mptoken, sponsorSle ? sponsorSle->rawSle() : SLE::const_pointer{});
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -415,7 +416,7 @@ requireAuth(
|
||||
}
|
||||
|
||||
// requireAuth is recursive if the issuer is a vault pseudo-account
|
||||
auto const sleIssuer = view.read(keylet::account(mptIssuer));
|
||||
auto const sleIssuer = AccountRootEntryR(mptIssuer, view);
|
||||
if (!sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -1020,13 +1021,16 @@ checkCreateMPT(
|
||||
{
|
||||
return err;
|
||||
}
|
||||
auto const sleAcct = view.peek(keylet::account(holder));
|
||||
auto sleAcct = AccountRootEntryW(holder, view, j);
|
||||
if (!sleAcct)
|
||||
{
|
||||
return tecINTERNAL;
|
||||
}
|
||||
|
||||
increaseOwnerCount(view, sleAcct, sponsorSle, 1, j);
|
||||
std::optional<AccountRootEntryW> sponsorAccSle;
|
||||
if (sponsorSle)
|
||||
sponsorAccSle.emplace(sponsorSle->getAccountID(sfAccount), view);
|
||||
sleAcct.increaseOwnerCount(sponsorAccSle, 1);
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
@@ -855,7 +856,7 @@ tokenOfferCreatePreclaim(
|
||||
|
||||
if (nftIssuer != acctID && ((nftFlags & nft::kFlagTransferable) == 0))
|
||||
{
|
||||
auto const root = view.read(keylet::account(nftIssuer));
|
||||
auto const root = AccountRootEntryR(nftIssuer, view);
|
||||
XRPL_ASSERT(root, "xrpl::nft::tokenOfferCreatePreclaim : non-null account");
|
||||
|
||||
if (auto minter = (*root)[~sfNFTokenMinter]; minter != acctID)
|
||||
@@ -886,7 +887,7 @@ tokenOfferCreatePreclaim(
|
||||
{
|
||||
// If a destination is specified, the destination must already be in
|
||||
// the ledger.
|
||||
auto const sleDst = view.read(keylet::account(*dest));
|
||||
auto const sleDst = AccountRootEntryR(*dest, view);
|
||||
|
||||
if (!sleDst)
|
||||
return tecNO_DST;
|
||||
@@ -898,7 +899,7 @@ tokenOfferCreatePreclaim(
|
||||
|
||||
if (owner)
|
||||
{
|
||||
auto const sleOwner = view.read(keylet::account(*owner));
|
||||
auto const sleOwner = AccountRootEntryR(*owner, view);
|
||||
|
||||
// defensively check
|
||||
// it should not be possible to specify owner that doesn't exist
|
||||
@@ -937,9 +938,8 @@ tokenOfferCreateApply(
|
||||
beast::Journal j,
|
||||
std::uint32_t txFlags)
|
||||
{
|
||||
Keylet const acctKeylet = keylet::account(acctID);
|
||||
if (auto const acct = view.read(acctKeylet);
|
||||
priorBalance < accountReserve(view, acct, j, {.ownerCountDelta = 1}))
|
||||
if (auto const acct = AccountRootEntryW(acctID, view, j);
|
||||
priorBalance < acct.reserve({.ownerCountDelta = 1}))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto const offerID = keylet::nftokenOffer(acctID, seqProxy);
|
||||
@@ -1008,7 +1008,7 @@ checkTrustlineAuthorized(
|
||||
|
||||
if (view.rules().enabled(fixEnforceNFTokenTrustlineV2))
|
||||
{
|
||||
auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
auto const issuerAccount = AccountRootEntryR(issue.account, view);
|
||||
if (!issuerAccount)
|
||||
{
|
||||
JLOG(j.debug()) << "xrpl::nft::checkTrustlineAuthorized: can't "
|
||||
@@ -1060,7 +1060,7 @@ checkTrustlineDeepFrozen(
|
||||
|
||||
if (view.rules().enabled(featureDeepFreeze))
|
||||
{
|
||||
auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
auto const issuerAccount = AccountRootEntryR(issue.account, view);
|
||||
if (!issuerAccount)
|
||||
{
|
||||
JLOG(j.debug()) << "xrpl::nft::checkTrustlineDeepFrozen: can't "
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -51,7 +52,7 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
|
||||
}
|
||||
|
||||
// Transfer amount back to owner, decrement owner count
|
||||
auto const sle = view.peek(keylet::account(src));
|
||||
auto sle = AccountRootEntryW(src, view);
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -59,7 +60,7 @@ closeChannel(SLE::Ref slep, ApplyView& view, UInt256 const& key, beast::Journal
|
||||
(*slep)[sfAmount] >= (*slep)[sfBalance], "xrpl::closeChannel : minimum channel amount");
|
||||
(*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount] - (*slep)[sfBalance];
|
||||
decreaseOwnerCountForObject(view, sle, slep, 1, j);
|
||||
view.update(sle);
|
||||
sle.update();
|
||||
|
||||
// Remove PayChan from ledger
|
||||
view.erase(slep);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
@@ -133,14 +134,14 @@ isFrozen(
|
||||
{
|
||||
if (isXRP(currency))
|
||||
return false;
|
||||
auto sle = view.read(keylet::account(issuer));
|
||||
if (sle && sle->isFlag(lsfGlobalFreeze))
|
||||
auto const sleIssuer = AccountRootEntryR(issuer, view);
|
||||
if (sleIssuer && sleIssuer->isFlag(lsfGlobalFreeze))
|
||||
return true;
|
||||
if (issuer != account)
|
||||
{
|
||||
// Check if the issuer froze the line
|
||||
sle = view.read(keylet::trustLine(account, issuer, currency));
|
||||
if (sle && sle->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
|
||||
auto const sleLine = view.read(keylet::trustLine(account, issuer, currency));
|
||||
if (sleLine && sleLine->isFlag((issuer > account) ? lsfHighFreeze : lsfLowFreeze))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -238,7 +239,7 @@ trustCreate(
|
||||
XRPL_ASSERT(
|
||||
sleAccount->getAccountID(sfAccount) == (bSetHigh ? uHighAccountID : uLowAccountID),
|
||||
"xrpl::trustCreate : matching account ID");
|
||||
auto const slePeer = view.peek(keylet::account(bSetHigh ? uLowAccountID : uHighAccountID));
|
||||
auto const slePeer = AccountRootEntryW(bSetHigh ? uLowAccountID : uHighAccountID, view);
|
||||
if (!slePeer)
|
||||
return tecNO_TARGET;
|
||||
|
||||
@@ -283,7 +284,11 @@ trustCreate(
|
||||
}
|
||||
|
||||
sleRippleState->setFieldU32(sfFlags, uFlags);
|
||||
increaseOwnerCount(view, sleAccount, sponsorSle, 1, j);
|
||||
AccountRootEntryW accountSle(bSetHigh ? uHighAccountID : uLowAccountID, view, j);
|
||||
std::optional<AccountRootEntryW> sponsorAccSle;
|
||||
if (sponsorSle)
|
||||
sponsorAccSle.emplace(sponsorSle->getAccountID(sfAccount), view);
|
||||
accountSle.increaseOwnerCount(sponsorAccSle, 1);
|
||||
|
||||
addSponsorToLedgerEntry(sleRippleState, sponsorSle, bSetHigh ? sfHighSponsor : sfLowSponsor);
|
||||
|
||||
@@ -349,7 +354,7 @@ updateTrustLine(
|
||||
if (!state)
|
||||
return false;
|
||||
|
||||
auto sle = view.peek(keylet::account(sender));
|
||||
auto sle = AccountRootEntryW(sender, view, j);
|
||||
if (!sle)
|
||||
return false;
|
||||
|
||||
@@ -376,9 +381,9 @@ updateTrustLine(
|
||||
{
|
||||
// VFALCO Where is the line being deleted?
|
||||
// Clear the reserve of the sender, possibly delete the line!
|
||||
auto const currentSponsor =
|
||||
auto currentSponsor =
|
||||
getLedgerEntryReserveSponsor(view, state, bSenderHigh ? sfHighSponsor : sfLowSponsor);
|
||||
decreaseOwnerCount(view, sle, currentSponsor, 1, j);
|
||||
sle.decreaseOwnerCount(currentSponsor, 1);
|
||||
|
||||
// Clear reserve flag.
|
||||
state->clearFlag(senderReserveFlag);
|
||||
@@ -464,7 +469,7 @@ issueIOU(
|
||||
|
||||
finalBalance.get<Issue>().account = noAccount();
|
||||
|
||||
auto const receiverAccount = view.peek(keylet::account(account));
|
||||
auto receiverAccount = AccountRootEntryW(account, view);
|
||||
if (!receiverAccount)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -476,7 +481,7 @@ issueIOU(
|
||||
issue.account,
|
||||
account,
|
||||
index.key,
|
||||
receiverAccount,
|
||||
receiverAccount.mutableRawSle(),
|
||||
false,
|
||||
noRipple,
|
||||
false,
|
||||
@@ -579,7 +584,7 @@ requireAuth(ReadView const& view, Issue const& issue, AccountID const& account,
|
||||
|
||||
// If this is a weak or legacy check, or if the account has a line, fail if
|
||||
// auth is required and not set on the line
|
||||
if (auto const issuerAccount = view.read(keylet::account(issue.account));
|
||||
if (auto const issuerAccount = AccountRootEntryR(issue.account, view);
|
||||
issuerAccount && issuerAccount->isFlag(lsfRequireAuth))
|
||||
{
|
||||
if (trustLine)
|
||||
@@ -609,8 +614,8 @@ canTransfer(ReadView const& view, Issue const& issue, AccountID const& from, Acc
|
||||
auto const& issuerId = issue.getIssuer();
|
||||
if (issuerId == from || issuerId == to)
|
||||
return tesSUCCESS;
|
||||
auto const sleIssuer = view.read(keylet::account(issuerId));
|
||||
if (sleIssuer == nullptr)
|
||||
auto const sleIssuer = AccountRootEntryR(issuerId, view);
|
||||
if (!sleIssuer.exists())
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const isRippleDisabled = [&](AccountID account) -> bool {
|
||||
@@ -665,8 +670,8 @@ addEmptyHolding(
|
||||
if (isGlobalFrozen(ctx.view, issuerId))
|
||||
return tecFROZEN; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleSrc = ctx.view.peek(keylet::account(srcId));
|
||||
auto const sleDst = ctx.view.peek(keylet::account(dstId));
|
||||
auto const sleSrc = AccountRootEntryW(srcId, ctx.view);
|
||||
auto sleDst = AccountRootEntryW(dstId, ctx.view, journal);
|
||||
if (!sleDst || !sleSrc)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
// Create path: DefaultRipple is still required. terNO_RIPPLE is
|
||||
@@ -681,20 +686,14 @@ addEmptyHolding(
|
||||
return tecDUPLICATE;
|
||||
|
||||
// A reserve sponsor only covers tx.Account's own objects.
|
||||
auto const sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst);
|
||||
auto sponsorExp = getEffectiveTxReserveSponsor(ctx, sleDst);
|
||||
if (!sponsorExp)
|
||||
return sponsorExp.error(); // LCOV_EXCL_LINE
|
||||
auto const sponsorSle = *sponsorExp;
|
||||
auto& sponsorSle = *sponsorExp;
|
||||
|
||||
// Can the account cover the trust line reserve ?
|
||||
if (auto const ret = checkReserve(
|
||||
ctx,
|
||||
sleDst,
|
||||
priorBalance,
|
||||
sponsorSle,
|
||||
{.ownerCountDelta = 1},
|
||||
journal,
|
||||
tecNO_LINE_INSUF_RESERVE);
|
||||
if (auto const ret = sleDst.checkReserve(
|
||||
ctx, priorBalance, sponsorSle, {.ownerCountDelta = 1}, tecNO_LINE_INSUF_RESERVE);
|
||||
!isTesSuccess(ret))
|
||||
{
|
||||
return ret;
|
||||
@@ -706,7 +705,7 @@ addEmptyHolding(
|
||||
srcId,
|
||||
dstId,
|
||||
index.key,
|
||||
sleDst,
|
||||
sleDst.mutableRawSle(),
|
||||
/*bAuth=*/false,
|
||||
/*bNoRipple=*/true,
|
||||
/*bFreeze=*/false,
|
||||
@@ -715,7 +714,7 @@ addEmptyHolding(
|
||||
/*saLimit=*/STAmount{Issue{currency, dstId}},
|
||||
/*uQualityIn=*/0,
|
||||
/*uQualityOut=*/0,
|
||||
sponsorSle,
|
||||
sponsorSle ? sponsorSle->mutableRawSle() : SLE::pointer{},
|
||||
journal);
|
||||
}
|
||||
|
||||
@@ -728,7 +727,7 @@ removeEmptyHolding(
|
||||
{
|
||||
if (issue.native())
|
||||
{
|
||||
auto const sle = ctx.view.read(keylet::account(accountID));
|
||||
auto const sle = AccountRootEntryW(accountID, ctx.view);
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -753,13 +752,14 @@ removeEmptyHolding(
|
||||
if (line->isFlag(lsfLowReserve))
|
||||
{
|
||||
// Clear reserve for low account.
|
||||
auto sleLowAccount = ctx.view.peek(keylet::account(line->at(sfLowLimit)->getIssuer()));
|
||||
auto sleLowAccount =
|
||||
AccountRootEntryW(line->at(sfLowLimit)->getIssuer(), ctx.view, journal);
|
||||
if (!sleLowAccount)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor);
|
||||
auto currentLowSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfLowSponsor);
|
||||
|
||||
decreaseOwnerCount(ctx.view, sleLowAccount, currentLowSponsor, 1, journal);
|
||||
sleLowAccount.decreaseOwnerCount(currentLowSponsor, 1);
|
||||
// It's not really necessary to clear the reserve flag, since the line
|
||||
// is about to be deleted, but this will make the metadata reflect an
|
||||
// accurate state at the time of deletion.
|
||||
@@ -770,13 +770,14 @@ removeEmptyHolding(
|
||||
if (line->isFlag(lsfHighReserve))
|
||||
{
|
||||
// Clear reserve for high account.
|
||||
auto sleHighAccount = ctx.view.peek(keylet::account(line->at(sfHighLimit)->getIssuer()));
|
||||
auto sleHighAccount =
|
||||
AccountRootEntryW(line->at(sfHighLimit)->getIssuer(), ctx.view, journal);
|
||||
if (!sleHighAccount)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor);
|
||||
auto currentHighSponsor = getLedgerEntryReserveSponsor(ctx.view, line, sfHighSponsor);
|
||||
|
||||
decreaseOwnerCount(ctx.view, sleHighAccount, currentHighSponsor, 1, journal);
|
||||
sleHighAccount.decreaseOwnerCount(currentHighSponsor, 1);
|
||||
// It's not really necessary to clear the reserve flag, since the line
|
||||
// is about to be deleted, but this will make the metadata reflect an
|
||||
// accurate state at the time of deletion.
|
||||
@@ -805,8 +806,8 @@ deleteAMMTrustLine(
|
||||
auto const& [low, high] = std::minmax(
|
||||
sleState->getFieldAmount(sfLowLimit).getIssuer(),
|
||||
sleState->getFieldAmount(sfHighLimit).getIssuer());
|
||||
auto sleLow = view.peek(keylet::account(low));
|
||||
auto sleHigh = view.peek(keylet::account(high));
|
||||
auto sleLow = AccountRootEntryW(low, view, j);
|
||||
auto sleHigh = AccountRootEntryW(high, view, j);
|
||||
if (!sleLow || !sleHigh)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -825,7 +826,7 @@ deleteAMMTrustLine(
|
||||
if (ammAccountID && (low != *ammAccountID && high != *ammAccountID))
|
||||
return terNO_AMM;
|
||||
|
||||
auto const sponsorSle =
|
||||
auto sponsorSle =
|
||||
getLedgerEntryReserveSponsor(view, sleState, !ammLow ? sfLowSponsor : sfHighSponsor);
|
||||
|
||||
if (auto const ter = trustDelete(view, sleState, low, high, j); !isTesSuccess(ter))
|
||||
@@ -838,7 +839,7 @@ deleteAMMTrustLine(
|
||||
if (!sleState->isFlag(uFlags))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
decreaseOwnerCount(view, !ammLow ? sleLow : sleHigh, sponsorSle, 1, j);
|
||||
(!ammLow ? sleLow : sleHigh).decreaseOwnerCount(sponsorSle, 1);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/OracleHelpers.h>
|
||||
#include <xrpl/ledger/helpers/ProposalHelpers.h>
|
||||
@@ -76,7 +77,7 @@ getTxReserveSponsorID(STTx const& tx)
|
||||
return {};
|
||||
}
|
||||
|
||||
std::expected<SLE::pointer, TER>
|
||||
std::expected<std::optional<AccountRootEntryW>, TER>
|
||||
getTxReserveSponsor(ApplyViewContext ctx)
|
||||
{
|
||||
auto const sponsorID = getTxReserveSponsorID(ctx.tx);
|
||||
@@ -85,17 +86,17 @@ getTxReserveSponsor(ApplyViewContext ctx)
|
||||
XRPL_ASSERT(
|
||||
ctx.view.rules().enabled(featureSponsor),
|
||||
"xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
|
||||
auto sle = ctx.view.peek(keylet::account(*sponsorID));
|
||||
auto sle = AccountRootEntryW(*sponsorID, ctx.view);
|
||||
|
||||
// already checked in Transactor::checkSponsor
|
||||
if (!sle)
|
||||
if (!sle.exists())
|
||||
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
return sle;
|
||||
}
|
||||
return SLE::pointer();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::expected<SLE::const_pointer, TER>
|
||||
std::expected<std::optional<AccountRootEntryR>, TER>
|
||||
getTxReserveSponsor(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
auto const sponsorID = getTxReserveSponsorID(tx);
|
||||
@@ -104,18 +105,18 @@ getTxReserveSponsor(ReadView const& view, STTx const& tx)
|
||||
XRPL_ASSERT(
|
||||
view.rules().enabled(featureSponsor),
|
||||
"xrpl::getTxReserveSponsor : sponsor exists + Sponsor enabled");
|
||||
auto sle = view.read(keylet::account(*sponsorID));
|
||||
auto sle = AccountRootEntryR(*sponsorID, view);
|
||||
|
||||
// already checked in Transactor::checkSponsor
|
||||
if (!sle)
|
||||
if (!sle.exists())
|
||||
return std::unexpected(tecINTERNAL); // LCOV_EXCL_LINE
|
||||
return sle;
|
||||
}
|
||||
return SLE::pointer();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::expected<SLE::pointer, TER>
|
||||
getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle)
|
||||
std::expected<std::optional<AccountRootEntryW>, TER>
|
||||
getEffectiveTxReserveSponsor(ApplyViewContext ctx, AccountRootEntryR const& accountSle)
|
||||
{
|
||||
// A reserve sponsor only covers tx.Account's own objects.
|
||||
if (ctx.view.rules().enabled(fixCleanup3_2_0))
|
||||
@@ -133,7 +134,7 @@ getEffectiveTxReserveSponsor(ApplyViewContext ctx, SLE::ConstRef accountSle)
|
||||
}
|
||||
|
||||
if (isPseudoAccount(accountSle) || accountSle->getAccountID(sfAccount) != ctx.tx[sfAccount])
|
||||
return SLE::pointer();
|
||||
return std::nullopt;
|
||||
return getTxReserveSponsor(ctx);
|
||||
}
|
||||
|
||||
@@ -151,7 +152,7 @@ getLedgerEntryReserveSponsorID(SLE::ConstRef sle, SF_ACCOUNT const& field)
|
||||
return {};
|
||||
}
|
||||
|
||||
SLE::pointer
|
||||
std::optional<AccountRootEntryW>
|
||||
getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT const& field)
|
||||
{
|
||||
auto const sponsorID = getLedgerEntryReserveSponsorID(sle, field);
|
||||
@@ -160,9 +161,9 @@ getLedgerEntryReserveSponsor(ApplyView& view, SLE::ConstRef sle, SF_ACCOUNT cons
|
||||
XRPL_ASSERT(
|
||||
view.rules().enabled(featureSponsor),
|
||||
"xrpl::getLedgerEntryReserveSponsor : sponsor exists + Sponsor enabled");
|
||||
return view.peek(keylet::account(*sponsorID));
|
||||
return AccountRootEntryW(*sponsorID, view);
|
||||
}
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -185,16 +186,16 @@ addSponsorToLedgerEntry(SLE::Ref sle, SLE::ConstRef sponsorSle, SF_ACCOUNT const
|
||||
void
|
||||
addSponsorToLedgerEntry(ApplyViewContext ctx, SLE::Ref sle, SF_ACCOUNT const& field)
|
||||
{
|
||||
// getTxReserveSponsor yields a null pointer when the tx is not
|
||||
// reserve-sponsored, so addSponsorToLedgerEntry becomes a no-op then. The
|
||||
// error case (tecINTERNAL) is an already-checked invariant; skip stamping.
|
||||
// getTxReserveSponsor yields nullopt when the tx is not reserve-sponsored,
|
||||
// so addSponsorToLedgerEntry becomes a no-op then. The error case
|
||||
// (tecINTERNAL) is an already-checked invariant; skip stamping.
|
||||
auto const sponsorSle = getTxReserveSponsor(ctx);
|
||||
if (sponsorSle && *sponsorSle)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
ctx.view.rules().enabled(featureSponsor),
|
||||
"xrpl::addSponsorToLedgerEntry : sponsor exists + Sponsor enabled");
|
||||
addSponsorToLedgerEntry(sle, *sponsorSle, field);
|
||||
addSponsorToLedgerEntry(sle, (**sponsorSle).rawSle(), field);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
@@ -293,7 +294,7 @@ getLineIfUsable(
|
||||
// we need to check if the associated assets have been frozen
|
||||
if (view.rules().enabled(fixFrozenLPTokenTransfer))
|
||||
{
|
||||
auto const sleIssuer = view.read(keylet::account(issuer));
|
||||
auto const sleIssuer = AccountRootEntryR(issuer, view);
|
||||
if (!sleIssuer)
|
||||
{
|
||||
return nullptr; // LCOV_EXCL_LINE
|
||||
@@ -562,7 +563,7 @@ canAddHolding(ReadView const& view, Issue const& issue)
|
||||
return tesSUCCESS; // No special checks for XRP
|
||||
}
|
||||
|
||||
auto const issuer = view.read(keylet::account(issue.getIssuer()));
|
||||
auto const issuer = AccountRootEntryR(issue.getIssuer(), view);
|
||||
if (!issuer)
|
||||
{
|
||||
return terNO_ACCOUNT;
|
||||
@@ -738,6 +739,8 @@ directSendNoFeeIOU(
|
||||
auto const senderFreezeFlag = bSenderHigh ? lsfHighFreeze : lsfLowFreeze;
|
||||
auto const receiverReserveFlag = bSenderHigh ? lsfLowReserve : lsfHighReserve;
|
||||
|
||||
AccountRootEntryW senderSle(uSenderID, view, j);
|
||||
|
||||
// FIXME This NEEDS to be cleaned up and simplified. It's impossible
|
||||
// for anyone to understand.
|
||||
if (saBefore > beast::kZero
|
||||
@@ -746,8 +749,7 @@ directSendNoFeeIOU(
|
||||
// Sender is zero or negative.
|
||||
&& sleRippleState->isFlag(senderReserveFlag)
|
||||
// Sender reserve is set.
|
||||
&& sleRippleState->isFlag(senderNoRippleFlag) !=
|
||||
view.read(keylet::account(uSenderID))->isFlag(lsfDefaultRipple) &&
|
||||
&& sleRippleState->isFlag(senderNoRippleFlag) != senderSle->isFlag(lsfDefaultRipple) &&
|
||||
!sleRippleState->isFlag(senderFreezeFlag) &&
|
||||
!sleRippleState->getFieldAmount(bSenderHigh ? sfHighLimit : sfLowLimit)
|
||||
// Sender trust limit is 0.
|
||||
@@ -758,9 +760,9 @@ directSendNoFeeIOU(
|
||||
// Sender quality out is 0.
|
||||
{
|
||||
// Clear the reserve of the sender, possibly delete the line!
|
||||
auto const currentSponsor = getLedgerEntryReserveSponsor(
|
||||
auto currentSponsor = getLedgerEntryReserveSponsor(
|
||||
view, sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor);
|
||||
decreaseOwnerCount(view, view.peek(keylet::account(uSenderID)), currentSponsor, 1, j);
|
||||
senderSle.decreaseOwnerCount(currentSponsor, 1);
|
||||
|
||||
removeSponsorFromLedgerEntry(
|
||||
sleRippleState, !bSenderHigh ? sfLowSponsor : sfHighSponsor);
|
||||
@@ -805,7 +807,7 @@ directSendNoFeeIOU(
|
||||
<< to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : "
|
||||
<< saAmount.getFullText();
|
||||
|
||||
auto const sleAccount = view.peek(keylet::account(uReceiverID));
|
||||
auto sleAccount = AccountRootEntryW(uReceiverID, view);
|
||||
if (!sleAccount)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -817,7 +819,7 @@ directSendNoFeeIOU(
|
||||
uSenderID,
|
||||
uReceiverID,
|
||||
index.key,
|
||||
sleAccount,
|
||||
sleAccount.mutableRawSle(),
|
||||
false,
|
||||
noRipple,
|
||||
false,
|
||||
@@ -1010,10 +1012,12 @@ accountSendIOU(
|
||||
*/
|
||||
TER terResult(tesSUCCESS);
|
||||
|
||||
SLE::pointer const sender =
|
||||
uSenderID != beast::kZero ? view.peek(keylet::account(uSenderID)) : SLE::pointer();
|
||||
SLE::pointer const receiver =
|
||||
uReceiverID != beast::kZero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer();
|
||||
SLE::pointer const sender = uSenderID != beast::kZero
|
||||
? AccountRootEntryW(uSenderID, view).mutableRawSle()
|
||||
: SLE::pointer();
|
||||
SLE::pointer const receiver = uReceiverID != beast::kZero
|
||||
? AccountRootEntryW(uReceiverID, view).mutableRawSle()
|
||||
: SLE::pointer();
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
@@ -1106,8 +1110,9 @@ accountSendMultiIOU(
|
||||
* ensure that transfers are balanced.
|
||||
*/
|
||||
|
||||
SLE::pointer const sender =
|
||||
senderID != beast::kZero ? view.peek(keylet::account(senderID)) : SLE::pointer();
|
||||
SLE::pointer const sender = senderID != beast::kZero
|
||||
? AccountRootEntryW(senderID, view).mutableRawSle()
|
||||
: SLE::pointer();
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
@@ -1138,8 +1143,9 @@ accountSendMultiIOU(
|
||||
if (!amount || (senderID == receiverID))
|
||||
continue;
|
||||
|
||||
SLE::pointer const receiver =
|
||||
receiverID != beast::kZero ? view.peek(keylet::account(receiverID)) : SLE::pointer();
|
||||
SLE::pointer const receiver = receiverID != beast::kZero
|
||||
? AccountRootEntryW(receiverID, view).mutableRawSle()
|
||||
: SLE::pointer();
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
@@ -1591,8 +1597,8 @@ transferXRP(
|
||||
XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver");
|
||||
XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP");
|
||||
|
||||
SLE::pointer const sender = view.peek(keylet::account(from));
|
||||
SLE::pointer const receiver = view.peek(keylet::account(to));
|
||||
SLE::pointer const sender = AccountRootEntryW(from, view).mutableRawSle();
|
||||
SLE::pointer const receiver = AccountRootEntryW(to, view).mutableRawSle();
|
||||
if (!sender || !receiver)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/nodestore/detail/DecodedBlob.h>
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
@@ -11,13 +12,8 @@
|
||||
|
||||
namespace xrpl::node_store {
|
||||
|
||||
DecodedBlob::DecodedBlob(std::optional<UInt256> key, void const* value, int valueBytes)
|
||||
DecodedBlob::DecodedBlob(void const* key, void const* value, int valueBytes) : key_(key)
|
||||
{
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
key_ = key.value();
|
||||
|
||||
/* Data format:
|
||||
|
||||
Bytes
|
||||
@@ -67,7 +63,7 @@ DecodedBlob::createObject()
|
||||
{
|
||||
Blob data(objectData_, objectData_ + dataBytes_);
|
||||
|
||||
object = NodeObject::createObject(objectType_, std::move(data), key_);
|
||||
object = NodeObject::createObject(objectType_, std::move(data), UInt256::fromVoid(key_));
|
||||
}
|
||||
|
||||
return object;
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -217,7 +216,7 @@ public:
|
||||
[&hash, pno, &status](void const* data, std::size_t size) {
|
||||
nudb::detail::buffer bf;
|
||||
auto const result = nodeobjectDecompress(data, size, bf);
|
||||
DecodedBlob decoded(hash, result.first, result.second);
|
||||
DecodedBlob decoded(hash.data(), result.first, result.second);
|
||||
if (!decoded.wasOk())
|
||||
{
|
||||
status = Status::DataCorrupt;
|
||||
@@ -241,7 +240,7 @@ public:
|
||||
nudb::error_code ec;
|
||||
nudb::detail::buffer bf;
|
||||
auto const result = nodeobjectCompress(e.getData(), e.getSize(), bf);
|
||||
db.insert(e.getKey().data(), result.first, result.second, ec);
|
||||
db.insert(e.getKey(), result.first, result.second, ec);
|
||||
if (ec && ec != nudb::error::key_exists)
|
||||
Throw<nudb::system_error>(ec);
|
||||
}
|
||||
@@ -289,25 +288,14 @@ public:
|
||||
Throw<nudb::system_error>(ec);
|
||||
nudb::visit(
|
||||
dp,
|
||||
[&](void const* keyData,
|
||||
[&](void const* key,
|
||||
std::size_t keyBytes,
|
||||
void const* data,
|
||||
std::size_t size,
|
||||
nudb::error_code&) {
|
||||
auto key = uint256::fromRaw(
|
||||
std::span{static_cast<unsigned char const*>(keyData), keyBytes});
|
||||
|
||||
if (!key) [[unlikely]]
|
||||
{
|
||||
ec = make_error_code(nudb::error::invalid_key_size);
|
||||
return;
|
||||
}
|
||||
|
||||
nudb::detail::buffer bf;
|
||||
auto const result = nodeobjectDecompress(data, size, bf);
|
||||
|
||||
DecodedBlob decoded(key, result.first, result.second);
|
||||
|
||||
if (!decoded.wasOk())
|
||||
{
|
||||
ec = make_error_code(nudb::error::missing_value);
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
@@ -112,9 +111,6 @@ public:
|
||||
if (!getIfExists(keyValues, Keys::kPath, name))
|
||||
Throw<std::runtime_error>("Missing path in RocksDBFactory backend");
|
||||
|
||||
if (keyBytes != uint256::size())
|
||||
Throw<std::runtime_error>("Incorrect key size: expected 32 bytes");
|
||||
|
||||
rocksdb::BlockBasedTableOptions tableOptions;
|
||||
options.env = env;
|
||||
|
||||
@@ -296,7 +292,7 @@ public:
|
||||
|
||||
if (getStatus.ok())
|
||||
{
|
||||
DecodedBlob decoded(hash, string.data(), string.size());
|
||||
DecodedBlob decoded(hash.data(), string.data(), string.size());
|
||||
|
||||
if (decoded.wasOk())
|
||||
{
|
||||
@@ -321,8 +317,8 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
status = checkedCast<Status>(
|
||||
safeCast<int>(Status::CustomCode) + safeCast<int>(getStatus.code()));
|
||||
status = static_cast<Status>(
|
||||
static_cast<int>(Status::CustomCode) + unsafeCast<int>(getStatus.code()));
|
||||
|
||||
JLOG(journal.error()) << getStatus.ToString();
|
||||
}
|
||||
@@ -351,7 +347,7 @@ public:
|
||||
EncodedBlob const encoded(e);
|
||||
|
||||
wb.Put(
|
||||
rocksdb::Slice(reinterpret_cast<char const*>(encoded.getKey().data()), keyBytes),
|
||||
rocksdb::Slice(reinterpret_cast<char const*>(encoded.getKey()), keyBytes),
|
||||
rocksdb::Slice(
|
||||
reinterpret_cast<char const*>(encoded.getData()), encoded.getSize()));
|
||||
}
|
||||
@@ -379,12 +375,9 @@ public:
|
||||
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next())
|
||||
{
|
||||
if (it->key().size() == keyBytes && it->key().size() == uint256::size())
|
||||
if (it->key().size() == keyBytes)
|
||||
{
|
||||
DecodedBlob decoded(
|
||||
uint256::fromRaw(std::span{it->key().data(), it->key().size()}),
|
||||
it->value().data(),
|
||||
it->value().size());
|
||||
DecodedBlob decoded(it->key().data(), it->value().data(), it->value().size());
|
||||
|
||||
if (decoded.wasOk())
|
||||
{
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <xrpl/protocol/tokens.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@@ -21,143 +19,95 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace {
|
||||
namespace detail {
|
||||
|
||||
/** If true, we implement a cache for account ID lookups.
|
||||
|
||||
This is generally a good idea and can improve performance, especially
|
||||
when serving client requests.
|
||||
|
||||
The cache may generate warnings under some sanitizers; if you want to
|
||||
disable caching, set this variable to false.
|
||||
/**
|
||||
* Caches the base58 representations of AccountIDs
|
||||
*/
|
||||
constexpr bool cacheAccountIDs = true;
|
||||
|
||||
/** Represents a cached entry for the given AccountID.
|
||||
|
||||
We keep the size and alignment of the structure at 64 bytes to
|
||||
make sure it fits within a cache line and avoid false sharing.
|
||||
*/
|
||||
struct alignas(64) CachedAccountID
|
||||
class AccountIdCache
|
||||
{
|
||||
/** The sequence lock for the entry.
|
||||
private:
|
||||
struct CachedAccountID
|
||||
{
|
||||
AccountID id;
|
||||
char encoding[40] = {0};
|
||||
};
|
||||
|
||||
An even value means that the data is stable, whereas an odd
|
||||
value indicates the data is being actively modified.
|
||||
// The actual cache
|
||||
std::vector<CachedAccountID> cache_;
|
||||
|
||||
We use a 32-bit value to ensure that the counter will never
|
||||
return to an observed value during a read window.
|
||||
*/
|
||||
std::atomic<std::uint32_t> seq{0};
|
||||
// We use a hash function designed to resist algorithmic complexity attacks
|
||||
HardenedHash<> hasher_;
|
||||
|
||||
/** The account id. */
|
||||
AccountID id{};
|
||||
// 64 spinlocks, packed into a single 64-bit value
|
||||
std::atomic<std::uint64_t> locks_ = 0;
|
||||
|
||||
/** The length of the cached encoding.
|
||||
public:
|
||||
AccountIdCache(std::size_t count) : cache_(count)
|
||||
{
|
||||
// This is non-binding, but we try to avoid wasting memory that
|
||||
// is caused by overallocation.
|
||||
cache_.shrink_to_fit();
|
||||
}
|
||||
|
||||
This field disambiguates collisions between ACCOUNT_ZERO and
|
||||
never-written-to slots, since they would otherwise share the
|
||||
same encoded bits.
|
||||
*/
|
||||
std::uint8_t len = 0;
|
||||
std::string
|
||||
toBase58(AccountID const& id)
|
||||
{
|
||||
auto const index = hasher_(id) % cache_.size();
|
||||
|
||||
/** The actual string encoding. */
|
||||
char encoding[39] = {0};
|
||||
PackedSpinlock sl(locks_, index % 64);
|
||||
|
||||
{
|
||||
std::scoped_lock const lock(sl);
|
||||
|
||||
// The check against the first character of the encoding ensures
|
||||
// that we don't mishandle the case of the all-zero account:
|
||||
if (cache_[index].encoding[0] != 0 && cache_[index].id == id)
|
||||
return cache_[index].encoding;
|
||||
}
|
||||
|
||||
auto ret = encodeBase58Token(TokenType::AccountID, id.data(), id.size());
|
||||
|
||||
XRPL_ASSERT(ret.size() <= 38, "xrpl::detail::AccountIdCache : maximum result size");
|
||||
|
||||
{
|
||||
std::scoped_lock const lock(sl);
|
||||
cache_[index].id = id;
|
||||
std::strcpy(cache_[index].encoding, ret.c_str());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(CachedAccountID) == 64 && alignof(CachedAccountID) == 64);
|
||||
} // namespace detail
|
||||
|
||||
// The actual cache to use: a simple, direct-mapped, best-effort construct.
|
||||
//
|
||||
// We do not use a hardened hash for this because an AccountID is already
|
||||
// just cryptographically generated (i.e. uniformly distributed) bits and
|
||||
// there is no amplification, since collisions do not carry an additional
|
||||
// cost.
|
||||
//
|
||||
// The size is a "nice" power of 2 because it makes the calculation of an
|
||||
// index easier.
|
||||
constinit std::array<CachedAccountID, 65536> gAccountIdCache{};
|
||||
static std::unique_ptr<detail::AccountIdCache> gAccountIdCache;
|
||||
|
||||
} // namespace
|
||||
void
|
||||
initAccountIdCache(std::size_t count)
|
||||
{
|
||||
if (!gAccountIdCache && count != 0)
|
||||
gAccountIdCache = std::make_unique<detail::AccountIdCache>(count);
|
||||
}
|
||||
|
||||
std::string
|
||||
toBase58(AccountID const& v)
|
||||
{
|
||||
// Since the input bits are generated by a cryptographically secure
|
||||
// hash construct, we can just pick any subset of them, no muss, no
|
||||
// fuss.
|
||||
auto const index = [&v]() -> std::uint16_t {
|
||||
auto const data = v.data();
|
||||
if (gAccountIdCache)
|
||||
return gAccountIdCache->toBase58(v);
|
||||
|
||||
return static_cast<std::uint16_t>(data[0]) | (static_cast<std::uint16_t>(data[1]) << 8);
|
||||
}();
|
||||
|
||||
if constexpr (cacheAccountIDs)
|
||||
{
|
||||
auto& slot = gAccountIdCache[index];
|
||||
|
||||
// This is the optimistic, store-free read path: a collision with a
|
||||
// concurrent writer will be detected because the seq recheck fails
|
||||
// and we fall through to the slow path.
|
||||
//
|
||||
// These unsynchronized reads of the slot fields are, formally, data
|
||||
// races and, technically speaking, result in undefined behavior and
|
||||
// a tool like TSAN might flag them. These should not have an impact
|
||||
// on the correctness of the returned data: the sequence lock checks
|
||||
// ensure we discard any data that was in flux.
|
||||
if (auto const s0 = slot.seq.load(std::memory_order::acquire);
|
||||
(s0 & 1) == 0 && slot.len != 0 && slot.id == v)
|
||||
{
|
||||
char buf[38];
|
||||
auto const len = std::min<std::size_t>(slot.len, sizeof(buf));
|
||||
std::memcpy(buf, slot.encoding, len);
|
||||
|
||||
// Prevents the loads above from sinking below the recheck; the
|
||||
// acquire on s0 constrains later operations, not earlier ones.
|
||||
std::atomic_thread_fence(std::memory_order::acquire);
|
||||
|
||||
// Everything read from the slot — the gates above and the bytes in
|
||||
// buf — precedes this recheck and is validated by it. No slot access
|
||||
// may follow it.
|
||||
if (slot.seq.load(std::memory_order::relaxed) == s0)
|
||||
return std::string(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = encodeBase58Token(TokenType::AccountID, v.data(), v.size());
|
||||
|
||||
XRPL_ASSERT(!ret.empty() && ret.size() <= 38, "xrpl::toBase58 : maximum result size");
|
||||
|
||||
if constexpr (cacheAccountIDs)
|
||||
{
|
||||
auto& slot = gAccountIdCache[index];
|
||||
|
||||
// The CAS to an odd value excludes other writers; if it fails, another
|
||||
// thread wrote or is writing into this slot. That is not a problem: we
|
||||
// just don't store our result into the cache this time.
|
||||
if (auto s0 = slot.seq.load(std::memory_order::relaxed); (s0 & 1) == 0 &&
|
||||
slot.seq.compare_exchange_strong(s0, s0 + 1, std::memory_order::acquire))
|
||||
{
|
||||
slot.id = v;
|
||||
slot.len = static_cast<std::uint8_t>(ret.size());
|
||||
std::copy_n(ret.data(), ret.size(), slot.encoding);
|
||||
|
||||
// The acquire on the CAS above keeps the writes from becoming visible
|
||||
// before the seqlock transitions to an odd value, which means readers
|
||||
// can detect an in-progress write. This release publishes them before
|
||||
// the seqlock transitions to an even value.
|
||||
slot.seq.store(s0 + 2, std::memory_order::release);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return encodeBase58Token(TokenType::AccountID, v.data(), v.size());
|
||||
}
|
||||
|
||||
template <>
|
||||
std::optional<AccountID>
|
||||
parseBase58(std::string const& s)
|
||||
{
|
||||
return AccountID::fromRaw(decodeBase58Token(s, TokenType::AccountID));
|
||||
auto const result = decodeBase58Token(s, TokenType::AccountID);
|
||||
if (result.size() != AccountID::kBytes)
|
||||
return std::nullopt;
|
||||
return AccountID::fromRaw(result);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -198,9 +148,25 @@ parseBase58(std::string const& s)
|
||||
AccountID
|
||||
calcAccountID(PublicKey const& pk)
|
||||
{
|
||||
static_assert(AccountID::kBytes == sizeof(RipeshaHasher::result_type));
|
||||
|
||||
RipeshaHasher rsh;
|
||||
rsh(pk.data(), pk.size());
|
||||
return AccountID{static_cast<RipeshaHasher::result_type>(rsh)};
|
||||
return AccountID::fromRaw(static_cast<RipeshaHasher::result_type>(rsh));
|
||||
}
|
||||
|
||||
AccountID const&
|
||||
xrpAccount()
|
||||
{
|
||||
static AccountID const kAccount(beast::kZero);
|
||||
return kAccount;
|
||||
}
|
||||
|
||||
AccountID const&
|
||||
noAccount()
|
||||
{
|
||||
static AccountID const kAccount(1);
|
||||
return kAccount;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -185,9 +185,8 @@ getBookBase(Book const& book)
|
||||
UInt256
|
||||
getQualityNext(UInt256 const& uBase)
|
||||
{
|
||||
static constexpr UInt256 kNextQuality{
|
||||
"0000000000000000000000000000000000000000000000010000000000000000"};
|
||||
|
||||
static constexpr UInt256 kNextQuality(
|
||||
"0000000000000000000000000000000000000000000000010000000000000000");
|
||||
return uBase + kNextQuality;
|
||||
}
|
||||
|
||||
@@ -430,9 +429,9 @@ payChannel(AccountID const& src, AccountID const& dst, SeqProxy const& seq) noex
|
||||
Keylet
|
||||
nftokenPageMin(AccountID const& owner)
|
||||
{
|
||||
std::array<std::uint8_t, UInt256::size()> buf{};
|
||||
std::ranges::copy(owner, buf.begin());
|
||||
return {ltNFTOKEN_PAGE, UInt256{buf}};
|
||||
std::array<std::uint8_t, 32> buf{};
|
||||
std::memcpy(buf.data(), owner.data(), owner.size());
|
||||
return {ltNFTOKEN_PAGE, UInt256::fromRaw(buf)};
|
||||
}
|
||||
|
||||
Keylet
|
||||
|
||||
@@ -295,9 +295,11 @@ verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept
|
||||
NodeID
|
||||
calcNodeID(PublicKey const& pk)
|
||||
{
|
||||
static_assert(NodeID::kBytes == sizeof(RipeshaHasher::result_type));
|
||||
|
||||
RipeshaHasher h;
|
||||
h(pk.data(), pk.size());
|
||||
return NodeID{static_cast<RipeshaHasher::result_type>(h)};
|
||||
return NodeID::fromRaw(static_cast<RipeshaHasher::result_type>(h));
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -148,12 +148,12 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
|
||||
}
|
||||
|
||||
Issue issue;
|
||||
issue.currency = Currency{sit.get160()};
|
||||
issue.currency = sit.get160();
|
||||
|
||||
if (isXRP(issue.currency))
|
||||
Throw<std::runtime_error>("invalid native currency");
|
||||
|
||||
issue.account = AccountID{sit.get160()};
|
||||
issue.account = sit.get160();
|
||||
|
||||
if (isXRP(issue.account))
|
||||
Throw<std::runtime_error>("invalid native account");
|
||||
|
||||
@@ -19,13 +19,13 @@ STCurrency::STCurrency(SField const& name) : STBase{name}
|
||||
{
|
||||
}
|
||||
|
||||
STCurrency::STCurrency(SField const& name, Currency const& currency)
|
||||
: STBase{name}, currency_{currency}
|
||||
STCurrency::STCurrency(SerialIter& sit, SField const& name) : STBase{name}
|
||||
{
|
||||
currency_ = sit.get160();
|
||||
}
|
||||
|
||||
STCurrency::STCurrency(SerialIter& sit, SField const& name)
|
||||
: STCurrency{name, Currency{sit.get160()}}
|
||||
STCurrency::STCurrency(SField const& name, Currency const& currency)
|
||||
: STBase{name}, currency_{currency}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -28,38 +28,48 @@ STIssue::STIssue(SField const& name) : STBase{name}
|
||||
|
||||
STIssue::STIssue(SerialIter& sit, SField const& name) : STBase{name}
|
||||
{
|
||||
// Either the currency of an Issue or the issuer of an MPT; the
|
||||
// following 160 bits disambiguate.
|
||||
auto const currencyOrAccount = sit.get160();
|
||||
|
||||
if (isXRP(Currency{currencyOrAccount}))
|
||||
if (isXRP(Currency::fromRaw(currencyOrAccount)))
|
||||
{
|
||||
asset_ = xrpIssue();
|
||||
return;
|
||||
}
|
||||
|
||||
// MPT is serialized as:
|
||||
// - 160 bits MPT issuer account
|
||||
// - 160 bits black hole account
|
||||
// - 32 bits sequence
|
||||
AccountID const account{sit.get160()};
|
||||
|
||||
// MPT
|
||||
if (noAccount() == account)
|
||||
// Check if MPT
|
||||
else
|
||||
{
|
||||
// MPT: issuer (160), noAccount() (160), sequence (32). The sequence
|
||||
// is stored in little-endian and get32() reads big-endian, so
|
||||
// reverse it before building the canonical ID.
|
||||
asset_ = MPTIssue{std::byteswap(sit.get32()), AccountID{currencyOrAccount}};
|
||||
return;
|
||||
// MPT is serialized as:
|
||||
// - 160 bits MPT issuer account
|
||||
// - 160 bits black hole account
|
||||
// - 32 bits sequence
|
||||
AccountID const account = AccountID::fromRaw(sit.get160());
|
||||
// MPT
|
||||
if (noAccount() == account)
|
||||
{
|
||||
MPTID mptID;
|
||||
std::uint32_t sequence = sit.get32();
|
||||
// MPTID stores the sequence in canonical big-endian bytes. STIssue
|
||||
// ledger bytes are the legacy LE-host encoding, so convert the
|
||||
// native get32() value to LE bytes before copying into the MPTID.
|
||||
sequence = boost::endian::native_to_little(sequence);
|
||||
static_assert(MPTID::size() == sizeof(sequence) + sizeof(currencyOrAccount));
|
||||
memcpy(mptID.data(), &sequence, sizeof(sequence));
|
||||
memcpy(
|
||||
mptID.data() + sizeof(sequence),
|
||||
currencyOrAccount.data(),
|
||||
sizeof(currencyOrAccount));
|
||||
MPTIssue const issue{mptID};
|
||||
asset_ = issue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Issue issue;
|
||||
issue.currency = currencyOrAccount;
|
||||
issue.account = account;
|
||||
if (!isConsistent(issue))
|
||||
Throw<std::runtime_error>("invalid issue: currency and account native mismatch");
|
||||
asset_ = issue;
|
||||
}
|
||||
}
|
||||
|
||||
Issue const issue{Currency{currencyOrAccount}, account};
|
||||
|
||||
if (!isConsistent(issue))
|
||||
Throw<std::runtime_error>("invalid issue: currency and account native mismatch");
|
||||
|
||||
asset_ = issue;
|
||||
}
|
||||
|
||||
SerializedTypeID
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/hash/uhash.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
@@ -18,13 +17,9 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -41,10 +36,10 @@ STPathElement::getHash(STPathElement const& element)
|
||||
// important. We don't even really need to fully hash the whole
|
||||
// base_uint here, as a few bytes would do for our use.
|
||||
//
|
||||
// The note above is only true because the result of this function is only
|
||||
// used by the STPathElement equality operator as a fast-reject prefilter,
|
||||
// ahead of the field comparisons that then decide the answer. Do not use
|
||||
// it to key a container.
|
||||
// The note above is only true because the result of this function reaches
|
||||
// nothing but STPathElement::operator==, where it is a fast-reject
|
||||
// prefilter ahead of the field comparisons that decide the answer. Do not
|
||||
// use it to key a container.
|
||||
|
||||
for (auto const x : element.getAccountID())
|
||||
hashAccount += (hashAccount * 257) ^ x;
|
||||
@@ -111,38 +106,12 @@ STPathSet::operator=(STPathSet const& other)
|
||||
|
||||
STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
|
||||
{
|
||||
using enum STPathElement::Type;
|
||||
|
||||
auto parsePathElementType = [](std::underlying_type_t<STPathElement::Type> byte) noexcept
|
||||
-> std::optional<STPathElement::Type> {
|
||||
if (byte == std::to_underlying(TypeBoundary))
|
||||
return TypeBoundary;
|
||||
|
||||
constexpr auto knownBits = static_cast<std::underlying_type_t<STPathElement::Type>>(
|
||||
std::to_underlying(TypeAccount) | std::to_underlying(TypeCurrency) |
|
||||
std::to_underlying(TypeIssuer) | std::to_underlying(TypeMpt));
|
||||
|
||||
if ((byte & ~knownBits) != 0)
|
||||
return std::nullopt;
|
||||
|
||||
return static_cast<STPathElement::Type>(byte);
|
||||
};
|
||||
|
||||
std::vector<STPathElement> path;
|
||||
|
||||
std::optional<STPathElement::Type> type;
|
||||
|
||||
do
|
||||
for (;;)
|
||||
{
|
||||
type = parsePathElementType(sit.get8());
|
||||
int const iType = sit.get8();
|
||||
|
||||
if (!type)
|
||||
{
|
||||
JLOG(debugLog().error()) << "Bad path element in pathset";
|
||||
Throw<std::runtime_error>("bad path element");
|
||||
}
|
||||
|
||||
if (type == TypeNone || type == TypeBoundary)
|
||||
if (iType == STPathElement::TypeNone || iType == STPathElement::TypeBoundary)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
@@ -153,11 +122,28 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
|
||||
// Move rather than converting the vector to an STPath by copy.
|
||||
value_.emplace_back(std::move(path));
|
||||
path.clear();
|
||||
|
||||
if (iType == STPathElement::TypeNone)
|
||||
return;
|
||||
}
|
||||
else if ((iType & ~STPathElement::TypeAll) != 0)
|
||||
{
|
||||
JLOG(debugLog().error()) << "Bad path element " << iType << " in pathset";
|
||||
Throw<std::runtime_error>("bad path element");
|
||||
}
|
||||
else
|
||||
{
|
||||
bool const hasCurrency = ((*type & TypeCurrency) == TypeCurrency);
|
||||
bool const hasMPT = ((*type & TypeMpt) == TypeMpt);
|
||||
auto const hasAccount = (iType & STPathElement::TypeAccount) != 0u;
|
||||
auto const hasCurrency = (iType & STPathElement::TypeCurrency) != 0u;
|
||||
auto const hasIssuer = (iType & STPathElement::TypeIssuer) != 0u;
|
||||
auto const hasMPT = (iType & STPathElement::TypeMpt) != 0u;
|
||||
|
||||
AccountID account;
|
||||
PathAsset asset;
|
||||
AccountID issuer;
|
||||
|
||||
if (hasAccount)
|
||||
account = sit.get160();
|
||||
|
||||
if (hasCurrency && hasMPT)
|
||||
{
|
||||
@@ -165,26 +151,18 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
|
||||
Throw<std::runtime_error>("bad path element: MPT and Currency");
|
||||
}
|
||||
|
||||
AccountID account;
|
||||
AccountID issuer;
|
||||
|
||||
if ((*type & TypeAccount) == TypeAccount)
|
||||
account = AccountID{sit.get160()};
|
||||
|
||||
PathAsset asset;
|
||||
|
||||
if (hasCurrency)
|
||||
asset = Currency{sit.get160()};
|
||||
asset = Currency::fromRaw(sit.get160());
|
||||
|
||||
if (hasMPT)
|
||||
asset = sit.get192();
|
||||
|
||||
if ((*type & TypeIssuer) == TypeIssuer)
|
||||
issuer = AccountID{sit.get160()};
|
||||
if (hasIssuer)
|
||||
issuer = sit.get160();
|
||||
|
||||
path.emplace_back(account, asset, issuer, hasCurrency || hasMPT);
|
||||
}
|
||||
} while (type != TypeNone);
|
||||
}
|
||||
}
|
||||
|
||||
STBase*
|
||||
@@ -238,22 +216,24 @@ STPath::getJson(JsonOptions) const
|
||||
for (auto const& it : path_)
|
||||
{
|
||||
json::Value elem(json::ValueType::Object);
|
||||
auto const iType = it.getNodeType();
|
||||
|
||||
elem[jss::type] = safeCast<std::uint32_t>(it.getNodeType());
|
||||
elem[jss::type] = iType;
|
||||
|
||||
if (it.isType(STPathElement::TypeAccount))
|
||||
if ((iType & STPathElement::TypeAccount) != 0u)
|
||||
elem[jss::account] = to_string(it.getAccountID());
|
||||
|
||||
XRPL_ASSERT(
|
||||
!(it.hasCurrency() && it.hasMPT()),
|
||||
((iType & STPathElement::TypeCurrency) == 0u) ||
|
||||
((iType & STPathElement::TypeMpt) == 0u),
|
||||
"xrpl::STPath::getJson : not type Currency and MPT");
|
||||
if (it.hasCurrency())
|
||||
if ((iType & STPathElement::TypeCurrency) != 0u)
|
||||
elem[jss::currency] = to_string(it.getCurrency());
|
||||
|
||||
if (it.hasMPT())
|
||||
if ((iType & STPathElement::TypeMpt) != 0u)
|
||||
elem[jss::mpt_issuance_id] = to_string(it.getMPTID());
|
||||
|
||||
if (it.hasIssuer())
|
||||
if ((iType & STPathElement::TypeIssuer) != 0u)
|
||||
elem[jss::issuer] = to_string(it.getIssuerID());
|
||||
|
||||
ret.append(elem);
|
||||
@@ -285,42 +265,34 @@ STPathSet::add(Serializer& s) const
|
||||
XRPL_ASSERT(getFName().fieldType == STI_PATHSET, "xrpl::STPathSet::add : valid field type");
|
||||
bool first = true;
|
||||
|
||||
// Workaround, since `Serializer` does not accept strongly-typed enums
|
||||
// yet. When support is added, the static_assert will trigger, and the
|
||||
// lambda can be removed entirely.
|
||||
auto const toByte = [](std::same_as<STPathElement::Type> auto t) noexcept {
|
||||
static_assert(
|
||||
!requires(Serializer& ser) { ser.add8(t); },
|
||||
"Serializer::add8 now accepts STPathElement::Type directly; remove toByte.");
|
||||
return std::to_underlying(t);
|
||||
};
|
||||
|
||||
for (auto const& spPath : value_)
|
||||
{
|
||||
if (!first)
|
||||
s.add8(toByte(STPathElement::TypeBoundary));
|
||||
s.add8(STPathElement::TypeBoundary);
|
||||
|
||||
for (auto const& speElement : spPath)
|
||||
{
|
||||
s.add8(toByte(speElement.getNodeType()));
|
||||
int const iType = speElement.getNodeType();
|
||||
|
||||
if (speElement.isType(STPathElement::TypeAccount))
|
||||
s.add8(iType);
|
||||
|
||||
if ((iType & STPathElement::TypeAccount) != 0u)
|
||||
s.addBitString(speElement.getAccountID());
|
||||
|
||||
if (speElement.hasMPT())
|
||||
if ((iType & STPathElement::TypeMpt) != 0u)
|
||||
s.addBitString(speElement.getMPTID());
|
||||
|
||||
if (speElement.hasCurrency())
|
||||
if ((iType & STPathElement::TypeCurrency) != 0u)
|
||||
s.addBitString(speElement.getCurrency());
|
||||
|
||||
if (speElement.hasIssuer())
|
||||
if ((iType & STPathElement::TypeIssuer) != 0u)
|
||||
s.addBitString(speElement.getIssuerID());
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
s.add8(toByte(STPathElement::TypeNone));
|
||||
s.add8(STPathElement::TypeNone);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -9,9 +9,7 @@
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
@@ -19,7 +17,7 @@ namespace xrpl {
|
||||
|
||||
STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name)
|
||||
{
|
||||
auto slice = sit.getSlice(sit.getVLDataLength());
|
||||
auto const slice = sit.getSlice(sit.getVLDataLength());
|
||||
|
||||
if (slice.size() % UInt256::size() != 0)
|
||||
{
|
||||
@@ -27,14 +25,12 @@ STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name)
|
||||
std::format("Bad serialization for STVector256: {}", slice.size()));
|
||||
}
|
||||
|
||||
value_.reserve(slice.size() / UInt256::size());
|
||||
auto const cnt = slice.size() / UInt256::size();
|
||||
|
||||
while (!slice.empty())
|
||||
{
|
||||
value_.emplace_back(
|
||||
std::span<std::uint8_t const, UInt256::size()>{slice.data(), UInt256::size()});
|
||||
slice += UInt256::size();
|
||||
}
|
||||
value_.reserve(cnt);
|
||||
|
||||
for (std::size_t i = 0; i != cnt; ++i)
|
||||
value_.push_back(UInt256::fromRaw(slice.substr(i * UInt256::size(), UInt256::size())));
|
||||
}
|
||||
|
||||
STBase*
|
||||
|
||||
@@ -104,9 +104,8 @@ parseGenericSeed(std::string const& str, bool rfc1751)
|
||||
std::string key;
|
||||
if (RFC1751::getKeyFromEnglish(key, str) == 1)
|
||||
{
|
||||
std::array<std::uint8_t, UInt128::size()> bytes{};
|
||||
std::ranges::reverse_copy(key, bytes.begin());
|
||||
return Seed{UInt128{bytes}};
|
||||
Blob const blob(key.rbegin(), key.rend());
|
||||
return Seed{UInt128::fromRaw(blob)};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ constexpr std::size_t kIsoCodeLength = 3;
|
||||
std::string
|
||||
to_string(Currency const& currency)
|
||||
{
|
||||
if (currency == xrpCurrency())
|
||||
if (currency == beast::kZero)
|
||||
return systemCurrencyCode();
|
||||
|
||||
if (currency == noCurrency())
|
||||
@@ -62,7 +62,7 @@ to_string(Currency const& currency)
|
||||
}
|
||||
|
||||
bool
|
||||
toCurrency(Currency& currency, std::string_view code)
|
||||
toCurrency(Currency& currency, std::string const& code)
|
||||
{
|
||||
if (code.empty() || code == systemCurrencyCode())
|
||||
{
|
||||
@@ -87,7 +87,7 @@ toCurrency(Currency& currency, std::string_view code)
|
||||
}
|
||||
|
||||
Currency
|
||||
toCurrency(std::string_view code)
|
||||
toCurrency(std::string const& code)
|
||||
{
|
||||
Currency currency;
|
||||
if (!toCurrency(currency, code))
|
||||
@@ -95,4 +95,25 @@ toCurrency(std::string_view code)
|
||||
return currency;
|
||||
}
|
||||
|
||||
Currency const&
|
||||
xrpCurrency()
|
||||
{
|
||||
static Currency const kCurrency(beast::kZero);
|
||||
return kCurrency;
|
||||
}
|
||||
|
||||
Currency const&
|
||||
noCurrency()
|
||||
{
|
||||
static Currency const kCurrency(1);
|
||||
return kCurrency;
|
||||
}
|
||||
|
||||
Currency const&
|
||||
badCurrency()
|
||||
{
|
||||
static Currency const kCurrency(0x5852500000000000);
|
||||
return kCurrency;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
@@ -130,15 +129,14 @@ deserializeSHAMapNodeID(void const* data, std::size_t size)
|
||||
{
|
||||
std::optional<SHAMapNodeID> ret;
|
||||
|
||||
if (size == UInt256::size() + 1)
|
||||
if (size == 33)
|
||||
{
|
||||
std::span const bytes{static_cast<unsigned char const*>(data), size};
|
||||
|
||||
if (unsigned int const depth = bytes[uint256::size()]; depth <= SHAMap::kLeafDepth)
|
||||
unsigned int const depth = *(static_cast<unsigned char const*>(data) + 32);
|
||||
if (depth <= SHAMap::kLeafDepth)
|
||||
{
|
||||
// Reject a serialized ID carrying bits below its own depth. Checked before
|
||||
// constructing the node, since the constructor asserts that same property.
|
||||
if (uint256 const id{bytes.first<uint256::size()>()}; isPrefixOfAtDepth(id, depth, id))
|
||||
// constructing, since the constructor asserts that same property.
|
||||
if (auto const id = UInt256::fromVoid(data); isPrefixOfAtDepth(id, depth, id))
|
||||
ret.emplace(depth, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ ApplyContext::ApplyContext(
|
||||
, parentBatchId_(parentBatchId)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
parentBatchId.has_value() == ((flags_ & ApplyFlags::Batch) == ApplyFlags::Batch),
|
||||
parentBatchId.has_value() == ((flags_ & TapBatch) == TapBatch),
|
||||
"Parent Batch ID should be set if batch apply flag is set");
|
||||
view_.emplace(&base_, flags_);
|
||||
}
|
||||
@@ -53,14 +53,13 @@ std::optional<TxMeta>
|
||||
ApplyContext::apply(TER ter)
|
||||
{
|
||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
|
||||
return view_->apply(
|
||||
base_, tx, ter, parentBatchId_, (flags_ & ApplyFlags::DryRun) != ApplyFlags::None, journal);
|
||||
return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & TapDryRun) != 0u, journal);
|
||||
}
|
||||
|
||||
void
|
||||
ApplyContext::addOrderBook(Book const& book)
|
||||
{
|
||||
if ((flags_ & ApplyFlags::DryRun) == ApplyFlags::None)
|
||||
if ((flags_ & TapDryRun) == TapNone)
|
||||
registry.get().getOrderBookDB().addOrderBook(book);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <xrpl/json/to_string.h> // IWYU pragma: keep
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DelegateHelpers.h>
|
||||
@@ -134,7 +135,7 @@ preflightCheckSigningKey(STObject const& sigObject, beast::Journal j)
|
||||
std::optional<NotTEC>
|
||||
preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j)
|
||||
{
|
||||
if ((flags & ApplyFlags::DryRun) != ApplyFlags::None) // simulation
|
||||
if ((flags & TapDryRun) != 0u) // simulation
|
||||
{
|
||||
std::optional<Slice> const signature = sigObject[~sfTxnSignature];
|
||||
if (signature && !signature->empty())
|
||||
@@ -510,8 +511,7 @@ Transactor::minimumFee(
|
||||
Fees const& fees,
|
||||
ApplyFlags flags)
|
||||
{
|
||||
return scaleFeeLoad(
|
||||
baseFee, registry.getFeeTrack(), fees, (flags & ApplyFlags::Unlimited) != ApplyFlags::None);
|
||||
return scaleFeeLoad(baseFee, registry.getFeeTrack(), fees, (flags & TapUnlimited) != 0u);
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -522,7 +522,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
|
||||
|
||||
auto const feePaid = ctx.tx[sfFee].xrp();
|
||||
|
||||
if ((ctx.flags & ApplyFlags::Batch) != ApplyFlags::None)
|
||||
if ((ctx.flags & TapBatch) != 0u)
|
||||
{
|
||||
if (feePaid == beast::kZero)
|
||||
return tesSUCCESS;
|
||||
@@ -587,7 +587,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
|
||||
|
||||
if (feePayer.type == FeePayerType::SponsorCoSigned)
|
||||
{
|
||||
auto const sponsorReserve = accountReserve(ctx.view, payerSle, ctx.j);
|
||||
auto const sponsorReserve = accountReserve(ctx.view, feePayer.id, ctx.j);
|
||||
maxSpendable = payerSle->getFieldAmount(sfBalance).xrp() - sponsorReserve;
|
||||
}
|
||||
else
|
||||
@@ -653,7 +653,7 @@ Transactor::payFee()
|
||||
XRPAmount spendable = balance;
|
||||
if (feePayer.type == FeePayerType::SponsorCoSigned)
|
||||
{
|
||||
auto const sponsorReserve = accountReserve(view(), sle, j_);
|
||||
auto const sponsorReserve = accountReserve(view(), feePayer.id, j_);
|
||||
// max(balance - reserve, 0) with overflow handling
|
||||
spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero;
|
||||
}
|
||||
@@ -701,7 +701,7 @@ Transactor::checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j
|
||||
{
|
||||
auto const id = tx.getAccountID(sfAccount);
|
||||
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
auto const sle = AccountRootEntryR(id, view);
|
||||
|
||||
if (!sle)
|
||||
{
|
||||
@@ -766,7 +766,7 @@ Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const id = ctx.tx.getAccountID(sfAccount);
|
||||
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
auto const sle = AccountRootEntryR(id, ctx.view);
|
||||
|
||||
if (!sle)
|
||||
{
|
||||
@@ -836,7 +836,7 @@ Transactor::ticketDelete(
|
||||
|
||||
// Update the account root's TicketCount. If the ticket count drops to
|
||||
// zero remove the (optional) field.
|
||||
auto sleAccount = view.peek(keylet::account(account));
|
||||
auto sleAccount = AccountRootEntryW(account, view);
|
||||
if (!sleAccount)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
@@ -886,19 +886,19 @@ Transactor::apply()
|
||||
|
||||
// If the transactor requires a valid account and the transaction doesn't
|
||||
// list one, preflight will have already a flagged a failure.
|
||||
auto const sle = view().peek(keylet::account(accountID_));
|
||||
auto sle = AccountRootEntryW(accountID_, view());
|
||||
|
||||
// sle must exist except for transactions
|
||||
// that allow zero account.
|
||||
XRPL_ASSERT(
|
||||
sle != nullptr || accountID_ == beast::kZero,
|
||||
sle.exists() || accountID_ == beast::kZero,
|
||||
"xrpl::Transactor::apply : non-null SLE or zero account");
|
||||
|
||||
if (sle)
|
||||
{
|
||||
preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp();
|
||||
|
||||
TER result = consumeSeqProxy(sle);
|
||||
TER result = consumeSeqProxy(sle.mutableRawSle());
|
||||
if (!isTesSuccess(result))
|
||||
return result;
|
||||
|
||||
@@ -909,7 +909,7 @@ Transactor::apply()
|
||||
if (sle->isFieldPresent(sfAccountTxnID))
|
||||
sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID());
|
||||
|
||||
view().update(sle);
|
||||
sle.update();
|
||||
}
|
||||
|
||||
return doApply();
|
||||
@@ -926,7 +926,7 @@ Transactor::checkSign(
|
||||
bool permitUncreatedAccount)
|
||||
{
|
||||
{
|
||||
auto const sle = view.read(keylet::account(idAccount));
|
||||
auto const sle = AccountRootEntryR(idAccount, view);
|
||||
|
||||
if ((view.rules().enabled(featureLendingProtocol) ||
|
||||
view.rules().enabled(featureBatchV1_1) || view.rules().enabled(fixCleanup3_3_0)) &&
|
||||
@@ -952,8 +952,7 @@ Transactor::checkSign(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
if (((flags & ApplyFlags::DryRun) != ApplyFlags::None) && pkSigner.empty() &&
|
||||
!sigObject.isFieldPresent(sfSigners))
|
||||
if (((flags & TapDryRun) != 0u) && pkSigner.empty() && !sigObject.isFieldPresent(sfSigners))
|
||||
{
|
||||
// simulate: skip signature validation when neither SigningPubKey nor
|
||||
// Signers are provided
|
||||
@@ -993,7 +992,7 @@ Transactor::checkSign(
|
||||
|
||||
// Look up the account.
|
||||
auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
|
||||
auto const sleAccount = view.read(keylet::account(idAccount));
|
||||
auto const sleAccount = AccountRootEntryR(idAccount, view);
|
||||
if (!sleAccount)
|
||||
{
|
||||
// An account that does not exist yet can only be authorized by its own
|
||||
@@ -1006,7 +1005,7 @@ Transactor::checkSign(
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
return checkSingleSign(view, idSigner, idAccount, sleAccount, j);
|
||||
return checkSingleSign(view, idSigner, idAccount, sleAccount.rawSle(), j);
|
||||
}
|
||||
|
||||
NotTEC
|
||||
@@ -1124,7 +1123,7 @@ Transactor::checkMultiSign(
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
(flags & ApplyFlags::DryRun) != ApplyFlags::None || !spk.empty(),
|
||||
(flags & TapDryRun) || !spk.empty(),
|
||||
"xrpl::Transactor::checkMultiSign : non-empty signer or "
|
||||
"simulation");
|
||||
AccountID const signingAcctIDFromPubKey =
|
||||
@@ -1155,7 +1154,7 @@ Transactor::checkMultiSign(
|
||||
|
||||
// In any of these cases we need to know whether the account is in
|
||||
// the ledger. Determine that now.
|
||||
auto const sleTxSignerRoot = view.read(keylet::account(txSignerAcctID));
|
||||
auto const sleTxSignerRoot = AccountRootEntryR(txSignerAcctID, view);
|
||||
|
||||
if (signingAcctIDFromPubKey == txSignerAcctID)
|
||||
{
|
||||
@@ -1298,7 +1297,7 @@ Transactor::reset(XRPAmount fee)
|
||||
{
|
||||
ctx_.discard();
|
||||
|
||||
auto const txnAcct = view().peek(keylet::account(ctx_.tx.getAccountID(sfAccount)));
|
||||
auto txnAcct = AccountRootEntryW(ctx_.tx.getAccountID(sfAccount), view());
|
||||
|
||||
// The account should never be missing from the ledger. But if it
|
||||
// is missing then we can't very well charge it a fee, can we?
|
||||
@@ -1333,7 +1332,7 @@ Transactor::reset(XRPAmount fee)
|
||||
XRPAmount spendable = balance;
|
||||
if (feePayer.type == FeePayerType::SponsorCoSigned)
|
||||
{
|
||||
auto const sponsorReserve = accountReserve(view(), payerSle, j_);
|
||||
auto const sponsorReserve = accountReserve(view(), feePayer.id, j_);
|
||||
// max(balance - reserve, 0) with overflow handling
|
||||
spendable = balance > sponsorReserve ? balance - sponsorReserve : beast::kZero;
|
||||
}
|
||||
@@ -1366,13 +1365,13 @@ Transactor::reset(XRPAmount fee)
|
||||
payerSle->setFieldAmount(feePayer.balanceField, feeAmountAfter);
|
||||
}
|
||||
|
||||
TER const ter{consumeSeqProxy(txnAcct)};
|
||||
TER const ter{consumeSeqProxy(txnAcct.mutableRawSle())};
|
||||
XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS");
|
||||
|
||||
if (isTesSuccess(ter))
|
||||
{
|
||||
view().update(txnAcct);
|
||||
if (payerSle != txnAcct)
|
||||
txnAcct.update();
|
||||
if (payerSle != txnAcct.mutableRawSle())
|
||||
view().update(payerSle);
|
||||
}
|
||||
|
||||
@@ -1606,7 +1605,7 @@ Transactor::operator()()
|
||||
if (ctx_.size() > kOversizeMetaDataCap)
|
||||
result = tecOVERSIZE;
|
||||
|
||||
if (isTecClaim(result) && ((view().flags() & ApplyFlags::FailHard) != ApplyFlags::None))
|
||||
if (isTecClaim(result) && ((view().flags() & TapFailHard) != 0u))
|
||||
{
|
||||
// If the TapFailHard flag is set, a tec result
|
||||
// must not do anything
|
||||
@@ -1686,7 +1685,7 @@ Transactor::operator()()
|
||||
// Once we call apply, we will no longer be able to look at view()
|
||||
metadata = ctx_.apply(result);
|
||||
|
||||
if ((ctx_.flags() & ApplyFlags::DryRun) != ApplyFlags::None)
|
||||
if ((ctx_.flags() & TapDryRun) != 0u)
|
||||
return logger(result, false, std::move(metadata));
|
||||
|
||||
return logger(result, canApply, std::move(metadata));
|
||||
|
||||
@@ -215,7 +215,7 @@ applyBatchTransactions(
|
||||
auto applyOneTransaction = [®istry, &j, &parentBatchId, &batchView](STTx const& tx) {
|
||||
OpenView perTxBatchView(kBatchView, batchView);
|
||||
|
||||
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, ApplyFlags::Batch, j);
|
||||
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, TapBatch, j);
|
||||
XRPL_ASSERT(
|
||||
ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)),
|
||||
"Inner transaction should not be applied");
|
||||
@@ -274,7 +274,7 @@ applyTransaction(
|
||||
{
|
||||
// Returns false if the transaction has need not be retried.
|
||||
if (retryAssured)
|
||||
flags = flags | ApplyFlags::Retry;
|
||||
flags = flags | TapRetry;
|
||||
|
||||
JLOG(j.debug()) << "TXN " << txn.getTransactionID() << (retryAssured ? "/retry" : "/final");
|
||||
|
||||
|
||||
@@ -346,8 +346,7 @@ preflight(
|
||||
// builds; re-check the same invariant here so a release build can't
|
||||
// silently skip a proposed transaction's signature-presence checks
|
||||
// outside of a dry run.
|
||||
if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
|
||||
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
|
||||
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
|
||||
@@ -378,8 +377,7 @@ preflight(
|
||||
PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
|
||||
|
||||
// See the comment in the other preflight() overload above.
|
||||
if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
|
||||
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
|
||||
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
|
||||
|
||||
@@ -735,6 +735,15 @@ NoDeepFreezeTrustLinesWithoutFreeze::finalize(
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
isPseudoAccountSle(SLE::ConstRef sleAcct)
|
||||
{
|
||||
auto const& fields = getPseudoAccountFields();
|
||||
return sleAcct && sleAcct->getType() == ltACCOUNT_ROOT &&
|
||||
std::ranges::any_of(
|
||||
fields, [&sleAcct](SField const* sf) { return sleAcct->isFieldPresent(*sf); });
|
||||
}
|
||||
|
||||
void
|
||||
ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after)
|
||||
{
|
||||
@@ -742,7 +751,7 @@ ValidNewAccountRoot::visitEntry(bool, SLE::ConstRef before, SLE::ConstRef after)
|
||||
{
|
||||
accountsCreated_++;
|
||||
accountSeq_ = (*after)[sfSequence];
|
||||
pseudoAccount_ = isPseudoAccount(after);
|
||||
pseudoAccount_ = isPseudoAccountSle(after);
|
||||
flags_ = after->getFlags();
|
||||
}
|
||||
}
|
||||
@@ -1020,9 +1029,9 @@ ValidPseudoAccounts::visitEntry(bool isDelete, SLE::ConstRef before, SLE::ConstR
|
||||
if (after && after->getType() == ltACCOUNT_ROOT)
|
||||
{
|
||||
bool const isPseudo = [&]() {
|
||||
// isPseudoAccount checks that any of the pseudo-account fields are
|
||||
// set.
|
||||
if (isPseudoAccount(after))
|
||||
// isPseudoAccountSle checks that any of the pseudo-account fields
|
||||
// are set.
|
||||
if (isPseudoAccountSle(after))
|
||||
return true;
|
||||
// Not all pseudo-accounts have a zero sequence, but all accounts
|
||||
// with a zero sequence had better be pseudo-accounts.
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheck.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -893,7 +894,7 @@ ValidMPTTransfer::visitEntry(
|
||||
// no trace of its pseudo-account status in the post-transaction view
|
||||
// isAuthorized() sees at finalize() time.
|
||||
if (before && before->getType() == ltACCOUNT_ROOT)
|
||||
pseudoAccountsBefore_[before->at(sfAccount)] = isPseudoAccount(before);
|
||||
pseudoAccountsBefore_[before->at(sfAccount)] = isPseudoAccountSle(before);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -740,7 +740,7 @@ ValidVault::finalize(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isPseudoAccount(sleSharesIssuer))
|
||||
if (!isPseudoAccount(AccountRootEntryR(sleSharesIssuer, view)))
|
||||
{
|
||||
JLOG(j.fatal()) //
|
||||
<< "Invariant failed: shares issuer must be a "
|
||||
|
||||
@@ -985,7 +985,7 @@ std::optional<std::variant<Quality, AMMOffer<TIn, TOut>>>
|
||||
BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
|
||||
{
|
||||
// This can be simplified (and sped up) if directories are never empty.
|
||||
Sandbox sb(&view, ApplyFlags::None);
|
||||
Sandbox sb(&view, TapNone);
|
||||
BookTip bt(sb, book_);
|
||||
auto const lobQuality = bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
|
||||
// Multi-path offer generates an offer with the quality
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -836,7 +837,7 @@ DirectStepI<TDerived>::check(StrandContext const& ctx) const
|
||||
return temBAD_PATH;
|
||||
}
|
||||
|
||||
auto const sleSrc = ctx.view.read(keylet::account(src_));
|
||||
auto const sleSrc = AccountRootEntryR(src_, ctx.view);
|
||||
if (!sleSrc)
|
||||
{
|
||||
JLOG(j_.warn()) << "DirectStepI: can't receive IOUs from non-existent issuer: " << src_;
|
||||
@@ -902,7 +903,7 @@ DirectStepI<TDerived>::check(StrandContext const& ctx) const
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<TDerived const*>(this)->check(ctx, sleSrc);
|
||||
return static_cast<TDerived const*>(this)->check(ctx, sleSrc.rawSle());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -865,7 +866,7 @@ MPTEndpointStep<TDerived>::check(StrandContext const& ctx) const
|
||||
return temBAD_PATH;
|
||||
}
|
||||
|
||||
auto const sleSrc = ctx.view.read(keylet::account(src_));
|
||||
auto const sleSrc = AccountRootEntryR(src_, ctx.view);
|
||||
if (!sleSrc)
|
||||
{
|
||||
JLOG(j_.warn()) << "MPTEndpointStep: can't receive MPT from non-existent issuer: " << src_;
|
||||
@@ -928,7 +929,7 @@ MPTEndpointStep<TDerived>::check(StrandContext const& ctx) const
|
||||
return temBAD_PATH;
|
||||
}
|
||||
|
||||
return static_cast<TDerived const*>(this)->check(ctx, sleSrc);
|
||||
return static_cast<TDerived const*>(this)->check(ctx, sleSrc.rawSle());
|
||||
}
|
||||
|
||||
template <class TDerived>
|
||||
|
||||
@@ -57,8 +57,10 @@ checkNear(IOUAmount const& expected, IOUAmount const& actual)
|
||||
static bool
|
||||
isXRPAccount(STPathElement const& pe)
|
||||
{
|
||||
return pe.getNodeType() == STPathElement::TypeAccount && isXRP(pe.getAccountID());
|
||||
}
|
||||
if (pe.getNodeType() != STPathElement::TypeAccount)
|
||||
return false;
|
||||
return isXRP(pe.getAccountID());
|
||||
};
|
||||
|
||||
static std::pair<TER, std::unique_ptr<Step>>
|
||||
toStep(
|
||||
@@ -69,8 +71,11 @@ toStep(
|
||||
{
|
||||
auto& j = ctx.j;
|
||||
|
||||
if (ctx.isFirst && e1->isAccount() && e1->hasCurrency() && e1->getPathAsset().isXRP())
|
||||
if (ctx.isFirst && e1->isAccount() &&
|
||||
((e1->getNodeType() & STPathElement::TypeCurrency) != 0u) && e1->getPathAsset().isXRP())
|
||||
{
|
||||
return makeXrpEndpointStep(ctx, e1->getAccountID());
|
||||
}
|
||||
|
||||
if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
|
||||
return makeXrpEndpointStep(ctx, e2->getAccountID());
|
||||
@@ -112,9 +117,15 @@ toStep(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
XRPL_ASSERT(e2->hasAsset() || e2->hasIssuer(), "xrpl::toStep : currency or issuer");
|
||||
PathAsset const outAsset = e2->hasAsset() ? e2->getPathAsset() : curAsset;
|
||||
auto const outIssuer = e2->hasIssuer() ? e2->getIssuerID() : curAsset.getIssuer();
|
||||
XRPL_ASSERT(
|
||||
(e2->getNodeType() & STPathElement::TypeAsset) ||
|
||||
(e2->getNodeType() & STPathElement::TypeIssuer),
|
||||
"xrpl::toStep : currency or issuer");
|
||||
PathAsset const outAsset =
|
||||
((e2->getNodeType() & STPathElement::TypeAsset) != 0u) ? e2->getPathAsset() : curAsset;
|
||||
auto const outIssuer = ((e2->getNodeType() & STPathElement::TypeIssuer) != 0u)
|
||||
? e2->getIssuerID()
|
||||
: curAsset.getIssuer();
|
||||
|
||||
if (isXRP(curAsset) && outAsset.isXRP())
|
||||
{
|
||||
@@ -186,15 +197,16 @@ toStrand(
|
||||
for (std::size_t i = 0; i < path.size(); ++i)
|
||||
{
|
||||
auto const& pe = path[i];
|
||||
auto const t = pe.getNodeType();
|
||||
|
||||
if ((pe.getNodeType() & ~STPathElement::TypeAll) != STPathElement::TypeNone || pe.isNone())
|
||||
if (((t & ~STPathElement::TypeAll) != 0u) || (t == 0u))
|
||||
return {temBAD_PATH, Strand{}};
|
||||
|
||||
bool const hasAccount = pe.isType(STPathElement::TypeAccount);
|
||||
bool const hasIssuer = pe.hasIssuer();
|
||||
bool const hasCurrency = pe.hasCurrency();
|
||||
bool const hasMPT = pe.hasMPT();
|
||||
bool const hasAsset = pe.hasAsset();
|
||||
bool const hasAccount = (t & STPathElement::TypeAccount) != 0u;
|
||||
bool const hasIssuer = (t & STPathElement::TypeIssuer) != 0u;
|
||||
bool const hasCurrency = (t & STPathElement::TypeCurrency) != 0u;
|
||||
bool const hasMPT = (t & STPathElement::TypeMpt) != 0u;
|
||||
bool const hasAsset = (t & STPathElement::TypeAsset) != 0u;
|
||||
|
||||
if (hasAccount && (hasIssuer || hasCurrency))
|
||||
return {temBAD_PATH, Strand{}};
|
||||
@@ -238,7 +250,9 @@ toStrand(
|
||||
}();
|
||||
|
||||
// Currency or MPT
|
||||
auto hasAsset = [](STPathElement const& pe) { return pe.hasAsset(); };
|
||||
auto hasAsset = [](STPathElement const pe) {
|
||||
return pe.getNodeType() & STPathElement::TypeAsset;
|
||||
};
|
||||
|
||||
std::vector<STPathElement> normPath;
|
||||
// reserve enough for the path, the implied source, destination,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -342,7 +343,7 @@ XRPEndpointStep<TDerived>::check(StrandContext const& ctx) const
|
||||
return temBAD_PATH;
|
||||
}
|
||||
|
||||
auto sleAcc = ctx.view.read(keylet::account(acc_));
|
||||
auto sleAcc = AccountRootEntryR(acc_, ctx.view);
|
||||
if (!sleAcc)
|
||||
{
|
||||
JLOG(j_.warn()) << "XRPEndpointStep: can't send or receive XRP from "
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
|
||||
@@ -224,7 +225,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
AccountID const account{ctx.tx[sfAccount]};
|
||||
AccountID const dst{ctx.tx[sfDestination]};
|
||||
|
||||
auto sleDst = ctx.view.read(keylet::account(dst));
|
||||
auto sleDst = AccountRootEntryR(dst, ctx.view);
|
||||
|
||||
if (!sleDst)
|
||||
return tecNO_DST;
|
||||
@@ -250,7 +251,7 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
auto sleAccount = ctx.view.read(keylet::account(account));
|
||||
auto sleAccount = AccountRootEntryR(account, ctx.view);
|
||||
XRPL_ASSERT(sleAccount, "xrpl::AccountDelete::preclaim : non-null account");
|
||||
if (!sleAccount)
|
||||
return terNO_ACCOUNT;
|
||||
@@ -353,11 +354,11 @@ AccountDelete::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
AccountDelete::doApply()
|
||||
{
|
||||
auto src = view().peek(keylet::account(accountID_));
|
||||
auto src = AccountRootEntryW(accountID_, view());
|
||||
XRPL_ASSERT(src, "xrpl::AccountDelete::doApply : non-null source account");
|
||||
|
||||
auto const dstID = ctx_.tx[sfDestination];
|
||||
auto dst = view().peek(keylet::account(dstID));
|
||||
auto dst = AccountRootEntryW(dstID, view());
|
||||
XRPL_ASSERT(dst, "xrpl::AccountDelete::doApply : non-null destination account");
|
||||
|
||||
if (!src || !dst)
|
||||
@@ -365,8 +366,8 @@ AccountDelete::doApply()
|
||||
|
||||
if (ctx_.tx.isFieldPresent(sfCredentialIDs))
|
||||
{
|
||||
if (auto err =
|
||||
verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, dstID, dst, ctx_.journal);
|
||||
if (auto err = verifyDepositPreauth(
|
||||
ctx_.tx, ctx_.view(), accountID_, dstID, dst.rawSle(), ctx_.journal);
|
||||
!isTesSuccess(err))
|
||||
return err;
|
||||
}
|
||||
@@ -407,7 +408,7 @@ AccountDelete::doApply()
|
||||
if (src->isFieldPresent(sfSponsor))
|
||||
{
|
||||
auto const sponsorID = src->getAccountID(sfSponsor);
|
||||
auto sponsorSle = view().peek(keylet::account(sponsorID));
|
||||
auto sponsorSle = AccountRootEntryW(sponsorID, view());
|
||||
|
||||
if (!sponsorSle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -425,7 +426,7 @@ AccountDelete::doApply()
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
sponsorSle->at(sfSponsoringAccountCount) = sponsoringAccountCount - 1;
|
||||
view().update(sponsorSle);
|
||||
sponsorSle.update();
|
||||
|
||||
// Following line might look redundant, but without it, sfSponsor
|
||||
// would end up remaining in after-ltAccountRoot during the
|
||||
@@ -448,8 +449,8 @@ AccountDelete::doApply()
|
||||
if (remainingBalance > XRPAmount(0) && dst->isFlag(lsfPasswordSpent))
|
||||
dst->clearFlag(lsfPasswordSpent);
|
||||
|
||||
view().update(dst);
|
||||
view().erase(src);
|
||||
dst.update();
|
||||
src.erase();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -170,7 +171,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const id = ctx.tx[sfAccount];
|
||||
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
auto const sle = AccountRootEntryR(id, ctx.view);
|
||||
if (!sle)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
@@ -187,8 +188,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
||||
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "Retry: Owner directory not empty.";
|
||||
return ((ctx.flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terOWNERS}
|
||||
: TER{tecOWNERS};
|
||||
return ((ctx.flags & TapRetry) != 0u) ? TER{terOWNERS} : TER{tecOWNERS};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
AccountSet::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(accountID_));
|
||||
auto sle = AccountRootEntryW(accountID_, view());
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -583,7 +583,7 @@ AccountSet::doApply()
|
||||
if (uFlagsIn != uFlagsOut)
|
||||
sle->setFieldU32(sfFlags, uFlagsOut);
|
||||
|
||||
ctx_.view().update(sle);
|
||||
sle.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
@@ -25,7 +26,7 @@ SetRegularKey::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
if (calcAccountID(PublicKey(makeSlice(spk))) == id)
|
||||
{
|
||||
auto const sle = view.read(keylet::account(id));
|
||||
auto const sle = AccountRootEntryR(id, view);
|
||||
|
||||
if (sle && !sle->isFlag(lsfPasswordSpent))
|
||||
{
|
||||
@@ -53,7 +54,7 @@ SetRegularKey::preflight(PreflightContext const& ctx)
|
||||
TER
|
||||
SetRegularKey::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(accountID_));
|
||||
auto sle = AccountRootEntryW(accountID_, view());
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -73,7 +74,7 @@ SetRegularKey::doApply()
|
||||
sle->makeFieldAbsent(sfRegularKey);
|
||||
}
|
||||
|
||||
ctx_.view().update(sle);
|
||||
sle.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
@@ -213,8 +214,9 @@ removeSignersFromLedger(
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
AccountRootEntryW accountSle(accountKeylet, view);
|
||||
decreaseOwnerCountForObject(
|
||||
view, view.peek(accountKeylet), signers, removeFromOwnerCount, registry.getJournal("View"));
|
||||
view, accountSle, signers, removeFromOwnerCount, registry.getJournal("View"));
|
||||
|
||||
view.erase(signers);
|
||||
|
||||
@@ -309,7 +311,7 @@ SignerListSet::replaceSignerList()
|
||||
ctx_.registry, view(), accountKeylet, ownerDirKeylet, signerListKeylet, j_))
|
||||
return ter;
|
||||
|
||||
auto const sle = view().peek(accountKeylet);
|
||||
auto sle = AccountRootEntryW(accountKeylet, view(), ctx_.journal);
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -319,12 +321,8 @@ SignerListSet::replaceSignerList()
|
||||
// We check the reserve against the starting balance because we want to
|
||||
// allow dipping into the reserve to pay fees. This behavior is consistent
|
||||
// with TicketCreate.
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(),
|
||||
sle,
|
||||
preFeeBalance_,
|
||||
{.ownerCountDelta = kAddedOwnerCount},
|
||||
ctx_.journal);
|
||||
if (auto const ret = sle.checkReserve(
|
||||
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = kAddedOwnerCount});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
@@ -333,7 +331,6 @@ SignerListSet::replaceSignerList()
|
||||
view().insert(signerList);
|
||||
writeSignersToSLE(signerList, flags);
|
||||
|
||||
auto viewJ = ctx_.registry.get().getJournal("View");
|
||||
// Add the signer list to the account's directory.
|
||||
auto const page =
|
||||
ctx_.view().dirInsert(ownerDirKeylet, signerListKeylet, describeOwnerDir(accountID_));
|
||||
@@ -348,7 +345,7 @@ SignerListSet::replaceSignerList()
|
||||
|
||||
// If we succeeded, the new entry counts against the
|
||||
// creator's reserve.
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, kAddedOwnerCount, viewJ);
|
||||
sle.increaseOwnerCount(ctx_.getApplyViewContext(), kAddedOwnerCount);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), signerList);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -359,7 +356,7 @@ SignerListSet::destroySignerList()
|
||||
auto const accountKeylet = keylet::account(accountID_);
|
||||
// Destroying the signer list is only allowed if either the master key
|
||||
// is enabled or there is a regular key.
|
||||
SLE::pointer const ledgerEntry = view().peek(accountKeylet);
|
||||
auto const ledgerEntry = AccountRootEntryW(accountKeylet, view());
|
||||
if (!ledgerEntry)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/RawView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -125,8 +126,7 @@ checkAttestationPublicKey(
|
||||
|
||||
AccountID const accountFromPK = calcAccountID(pk);
|
||||
|
||||
if (auto const sleAttestationSigningAccount =
|
||||
view.read(keylet::account(attestationSignerAccount)))
|
||||
if (auto const sleAttestationSigningAccount = AccountRootEntryR(attestationSignerAccount, view))
|
||||
{
|
||||
if (accountFromPK == attestationSignerAccount)
|
||||
{
|
||||
@@ -404,7 +404,7 @@ transferHelper(
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const dstK = keylet::account(dst);
|
||||
if (auto sleDst = psb.read(dstK))
|
||||
if (auto sleDst = AccountRootEntryR(dstK, psb))
|
||||
{
|
||||
// Check dst tag and deposit auth
|
||||
|
||||
@@ -430,13 +430,13 @@ transferHelper(
|
||||
|
||||
if (amt.native())
|
||||
{
|
||||
auto const sleSrc = psb.peek(keylet::account(src));
|
||||
auto sleSrc = AccountRootEntryW(src, psb, j);
|
||||
XRPL_ASSERT(sleSrc, "xrpl::transferHelper : non-null source account");
|
||||
if (!sleSrc)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
{
|
||||
auto const reserve = accountReserve(psb, sleSrc, j);
|
||||
auto const reserve = sleSrc.reserve();
|
||||
|
||||
auto const availableBalance = [&]() -> STAmount {
|
||||
STAmount curBal = (*sleSrc)[sfBalance];
|
||||
@@ -455,7 +455,7 @@ transferHelper(
|
||||
}
|
||||
}
|
||||
|
||||
auto sleDst = psb.peek(dstK);
|
||||
auto sleDst = AccountRootEntryW(dstK, psb);
|
||||
if (!sleDst)
|
||||
{
|
||||
if (canCreate == CanCreateDstPolicy::No)
|
||||
@@ -470,17 +470,17 @@ transferHelper(
|
||||
}
|
||||
|
||||
// Create the account.
|
||||
sleDst = std::make_shared<SLE>(dstK);
|
||||
sleDst.newSLE();
|
||||
sleDst->setAccountID(sfAccount, dst);
|
||||
sleDst->setFieldU32(sfSequence, psb.seq());
|
||||
|
||||
psb.insert(sleDst);
|
||||
sleDst.insert();
|
||||
}
|
||||
|
||||
(*sleSrc)[sfBalance] = (*sleSrc)[sfBalance] - amt;
|
||||
(*sleDst)[sfBalance] = (*sleDst)[sfBalance] + amt;
|
||||
psb.update(sleSrc);
|
||||
psb.update(sleDst);
|
||||
sleSrc.update();
|
||||
sleDst.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -724,7 +724,7 @@ finalizeClaimHelper(
|
||||
auto const cidOwner = (*sleClaimID)[sfAccount];
|
||||
{
|
||||
// Remove the claim id
|
||||
auto const sleOwner = outerSb.peek(keylet::account(cidOwner));
|
||||
auto sleOwner = AccountRootEntryW(cidOwner, outerSb);
|
||||
auto const page = (*sleClaimID)[sfOwnerNode];
|
||||
if (!outerSb.dirRemove(keylet::ownerDir(cidOwner), page, sleClaimID->key(), true))
|
||||
{
|
||||
@@ -759,7 +759,7 @@ getSignersListAndQuorum(ReadView const& view, SLE const& sleBridge, beast::Journ
|
||||
std::uint32_t q = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
AccountID const thisDoor = sleBridge[sfAccount];
|
||||
auto const sleDoor = [&] { return view.read(keylet::account(thisDoor)); }();
|
||||
auto const sleDoor = [&] { return AccountRootEntryR(thisDoor, view); }();
|
||||
|
||||
if (!sleDoor)
|
||||
{
|
||||
@@ -1036,7 +1036,7 @@ applyCreateAccountAttestations(
|
||||
|
||||
// Check reserve
|
||||
auto const balance = (*sleDoor)[sfBalance];
|
||||
auto const reserve = accountReserve(psb, sleDoor, j, {.ownerCountDelta = 1});
|
||||
auto const reserve = AccountRootEntryR(sleDoor, psb, j).reserve({.ownerCountDelta = 1});
|
||||
|
||||
if (balance < reserve)
|
||||
return std::unexpected(tecINSUFFICIENT_RESERVE);
|
||||
@@ -1145,7 +1145,9 @@ applyCreateAccountAttestations(
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Reserve was already checked
|
||||
increaseOwnerCount(psb, sleDoor, {}, 1, j);
|
||||
AccountRootEntryW doorSle(doorK, psb, j);
|
||||
std::optional<AccountRootEntryW> noSponsor;
|
||||
doorSle.increaseOwnerCount(noSponsor, 1);
|
||||
psb.insert(createdSleClaimID);
|
||||
psb.update(sleDoor);
|
||||
}
|
||||
@@ -1429,7 +1431,7 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
if (!isXRP(bridgeSpec.issue(chainType)))
|
||||
{
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(bridgeSpec.issue(chainType).account));
|
||||
auto const sleIssuer = AccountRootEntryR(bridgeSpec.issue(chainType).account, ctx.view);
|
||||
|
||||
if (!sleIssuer)
|
||||
return tecNO_ISSUER;
|
||||
@@ -1442,12 +1444,12 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
{
|
||||
// Check reserve
|
||||
auto const sleAcc = ctx.view.read(keylet::account(account));
|
||||
auto const sleAcc = AccountRootEntryR(account, ctx.view, ctx.j);
|
||||
if (!sleAcc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const balance = (*sleAcc)[sfBalance];
|
||||
auto const reserve = accountReserve(ctx.view, sleAcc, ctx.j, {.ownerCountDelta = 1});
|
||||
auto const reserve = sleAcc.reserve({.ownerCountDelta = 1});
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -1464,7 +1466,7 @@ XChainCreateBridge::doApply()
|
||||
auto const reward = ctx_.tx[sfSignatureReward];
|
||||
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
|
||||
|
||||
auto const sleAcct = ctx_.view().peek(keylet::account(account));
|
||||
auto sleAcct = AccountRootEntryW(account, ctx_.view(), ctx_.journal);
|
||||
if (!sleAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -1492,10 +1494,11 @@ XChainCreateBridge::doApply()
|
||||
(*sleBridge)[sfOwnerNode] = *page;
|
||||
}
|
||||
|
||||
increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal);
|
||||
std::optional<AccountRootEntryW> noSponsor;
|
||||
sleAcct.increaseOwnerCount(noSponsor, 1);
|
||||
|
||||
ctx_.view().insert(sleBridge);
|
||||
ctx_.view().update(sleAcct);
|
||||
sleAcct.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -1575,7 +1578,7 @@ BridgeModify::doApply()
|
||||
auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount];
|
||||
bool const clearAccountCreate = ctx_.tx.isFlag(tfClearAccountCreateAmount);
|
||||
|
||||
auto const sleAcct = ctx_.view().peek(keylet::account(account));
|
||||
auto const sleAcct = AccountRootEntryW(account, ctx_.view());
|
||||
if (!sleAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -1633,7 +1636,7 @@ XChainClaim::preclaim(PreclaimContext const& ctx)
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
|
||||
if (!ctx.view.read(keylet::account(ctx.tx[sfDestination])))
|
||||
if (!AccountRootEntryR(ctx.tx[sfDestination], ctx.view))
|
||||
{
|
||||
return tecNO_DST;
|
||||
}
|
||||
@@ -1738,7 +1741,7 @@ XChainClaim::doApply()
|
||||
// `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child
|
||||
// views, it's important that the sle's lifetime doesn't overlap.
|
||||
|
||||
auto const sleAcct = psb.peek(keylet::account(account));
|
||||
auto const sleAcct = AccountRootEntryW(account, psb);
|
||||
auto const sleBridge = peekBridge(psb, bridgeSpec);
|
||||
auto const sleClaimID = psb.peek(claimIDKeylet);
|
||||
|
||||
@@ -1919,7 +1922,7 @@ XChainCommit::doApply()
|
||||
auto const amount = ctx_.tx[sfAmount];
|
||||
auto const bridgeSpec = ctx_.tx[sfXChainBridge];
|
||||
|
||||
auto const sleAccount = psb.read(keylet::account(account));
|
||||
auto const sleAccount = AccountRootEntryR(account, psb);
|
||||
if (!sleAccount)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -1990,12 +1993,12 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
{
|
||||
// Check reserve
|
||||
auto const sleAcc = ctx.view.read(keylet::account(account));
|
||||
auto const sleAcc = AccountRootEntryR(account, ctx.view, ctx.j);
|
||||
if (!sleAcc)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const balance = (*sleAcc)[sfBalance];
|
||||
auto const reserve = accountReserve(ctx.view, sleAcc, ctx.j, {.ownerCountDelta = 1});
|
||||
auto const reserve = sleAcc.reserve({.ownerCountDelta = 1});
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
}
|
||||
@@ -2011,7 +2014,7 @@ XChainCreateClaimID::doApply()
|
||||
auto const reward = ctx_.tx[sfSignatureReward];
|
||||
auto const otherChainSrc = ctx_.tx[sfOtherChainSource];
|
||||
|
||||
auto const sleAcct = ctx_.view().peek(keylet::account(account));
|
||||
auto sleAcct = AccountRootEntryW(account, ctx_.view(), ctx_.journal);
|
||||
if (!sleAcct)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -2053,11 +2056,12 @@ XChainCreateClaimID::doApply()
|
||||
(*sleClaimID)[sfOwnerNode] = *page;
|
||||
}
|
||||
|
||||
increaseOwnerCount(ctx_.view(), sleAcct, {}, 1, ctx_.journal);
|
||||
std::optional<AccountRootEntryW> noSponsor;
|
||||
sleAcct.increaseOwnerCount(noSponsor, 1);
|
||||
|
||||
ctx_.view().insert(sleClaimID);
|
||||
ctx_.view().update(sleBridge);
|
||||
ctx_.view().update(sleAcct);
|
||||
sleAcct.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -2195,7 +2199,7 @@ XChainCreateAccountCommit::doApply()
|
||||
STAmount const reward = ctx_.tx[sfSignatureReward];
|
||||
STXChainBridge const bridge = ctx_.tx[sfXChainBridge];
|
||||
|
||||
auto const sle = psb.peek(keylet::account(account));
|
||||
auto const sle = AccountRootEntryW(account, psb);
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
@@ -112,8 +113,8 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
{
|
||||
auto const sleSrc = ctx.view.read(keylet::account(srcId));
|
||||
auto const sleDst = ctx.view.read(keylet::account(dstId));
|
||||
auto const sleSrc = AccountRootEntryR(srcId, ctx.view);
|
||||
auto const sleDst = AccountRootEntryR(dstId, ctx.view);
|
||||
if (!sleSrc || !sleDst)
|
||||
{
|
||||
// If the check exists this should never occur.
|
||||
@@ -201,7 +202,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
auto const sleTrustLine =
|
||||
ctx.view.read(keylet::trustLine(dstId, issuerId, currency));
|
||||
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
|
||||
auto const sleIssuer = AccountRootEntryR(issuerId, ctx.view);
|
||||
if (!sleIssuer)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Can't receive IOUs from "
|
||||
@@ -253,7 +254,7 @@ CheckCash::preclaim(PreclaimContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
},
|
||||
[&](MPTIssue const& issue) -> TER {
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(issuerId));
|
||||
auto const sleIssuer = AccountRootEntryR(issuerId, ctx.view);
|
||||
if (!sleIssuer)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Can't receive MPTs from "
|
||||
@@ -406,23 +407,18 @@ CheckCash::doApply()
|
||||
optDeliverMin ? maxDeliverMin() : ctx_.tx.getFieldAmount(sfAmount)};
|
||||
|
||||
auto applyViewContext = ApplyViewContext({.view = psb, .tx = ctx_.tx});
|
||||
auto const sponsorSle = getTxReserveSponsor(applyViewContext);
|
||||
auto sponsorSle = getTxReserveSponsor(applyViewContext);
|
||||
if (!sponsorSle)
|
||||
return sponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
// Check reserve. Return destination account SLE if enough reserve,
|
||||
// otherwise return nullptr.
|
||||
auto checkDstReserve = [&]() -> SLE::pointer {
|
||||
auto sleDst = psb.peek(keylet::account(accountID_));
|
||||
auto sleDst = AccountRootEntryW(accountID_, psb, j_);
|
||||
|
||||
// Can the account cover the trust line's or MPT reserve?
|
||||
if (auto const ret = checkReserve(
|
||||
applyViewContext,
|
||||
sleDst,
|
||||
preFeeBalance_,
|
||||
*sponsorSle,
|
||||
{.ownerCountDelta = 1},
|
||||
j_);
|
||||
if (auto const ret = sleDst.checkReserve(
|
||||
applyViewContext, preFeeBalance_, *sponsorSle, {.ownerCountDelta = 1});
|
||||
!isTesSuccess(ret))
|
||||
{
|
||||
JLOG(j_.trace()) << "Trust line does not exist. "
|
||||
@@ -430,7 +426,7 @@ CheckCash::doApply()
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return sleDst;
|
||||
return sleDst.mutableRawSle();
|
||||
};
|
||||
|
||||
std::optional<Keylet> trustLineKey;
|
||||
@@ -486,8 +482,10 @@ CheckCash::doApply()
|
||||
Issue(currency, accountID_), // limit of zero
|
||||
0, // quality in
|
||||
0, // quality out
|
||||
*sponsorSle, // sponsor
|
||||
viewJ); // journal
|
||||
sponsorSle->has_value() // sponsor
|
||||
? (*sponsorSle)->mutableRawSle()
|
||||
: SLE::pointer{},
|
||||
viewJ); // journal
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter;
|
||||
@@ -533,8 +531,14 @@ CheckCash::doApply()
|
||||
if (sleDst == nullptr)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
if (auto const err =
|
||||
checkCreateMPT(psb, mptID, accountID_, *sponsorSle, 0, j_);
|
||||
if (auto const err = checkCreateMPT(
|
||||
psb,
|
||||
mptID,
|
||||
accountID_,
|
||||
sponsorSle->has_value() ? (*sponsorSle)->mutableRawSle()
|
||||
: SLE::pointer{},
|
||||
0,
|
||||
j_);
|
||||
!isTesSuccess(err))
|
||||
{
|
||||
return err;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
@@ -79,7 +80,7 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
AccountID const dstId{ctx.tx[sfDestination]};
|
||||
AccountID const srcId{ctx.tx[sfAccount]};
|
||||
auto const sleDst = ctx.view.read(keylet::account(dstId));
|
||||
auto const sleDst = AccountRootEntryR(dstId, ctx.view);
|
||||
if (!sleDst)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Destination account does not exist.";
|
||||
@@ -187,15 +188,15 @@ CheckCreate::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
CheckCreate::doApply()
|
||||
{
|
||||
auto const sle = view().peek(keylet::account(accountID_));
|
||||
auto sle = AccountRootEntryW(accountID_, view(), ctx_.journal);
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// A check counts against the reserve of the issuing account, but we
|
||||
// check the starting balance because we want to allow dipping into the
|
||||
// reserve to pay fees.
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(), sle, preFeeBalance_, {.ownerCountDelta = 1}, ctx_.journal);
|
||||
if (auto const ret =
|
||||
sle.checkReserve(ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
// Note that we use the value from the sequence or ticket as the
|
||||
@@ -220,7 +221,6 @@ CheckCreate::doApply()
|
||||
|
||||
view().insert(sleCheck);
|
||||
|
||||
auto viewJ = ctx_.registry.get().getJournal("View");
|
||||
// If it's not a self-send (and it shouldn't be), add Check to the
|
||||
// destination's owner directory.
|
||||
if (dstAccountId != accountID_)
|
||||
@@ -251,7 +251,7 @@ CheckCreate::doApply()
|
||||
}
|
||||
// If we succeeded, the new entry counts against the creator's reserve.
|
||||
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, viewJ);
|
||||
sle.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCheck);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
@@ -87,8 +88,8 @@ CredentialAccept::doApply()
|
||||
AccountID const issuer{ctx_.tx[sfIssuer]};
|
||||
|
||||
// Both exist as credential object exist itself (checked in preclaim)
|
||||
auto const sleSubject = view().peek(keylet::account(accountID_));
|
||||
auto const sleIssuer = view().peek(keylet::account(issuer));
|
||||
auto sleSubject = AccountRootEntryW(accountID_, view(), j_);
|
||||
auto sleIssuer = AccountRootEntryW(issuer, view());
|
||||
|
||||
if (!sleSubject || !sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
@@ -97,13 +98,8 @@ CredentialAccept::doApply()
|
||||
if (!txSponsorSle)
|
||||
return txSponsorSle.error(); // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(),
|
||||
sleSubject,
|
||||
preFeeBalance_,
|
||||
*txSponsorSle,
|
||||
{.ownerCountDelta = 1},
|
||||
j_);
|
||||
if (auto const ret = sleSubject.checkReserve(
|
||||
ctx_.getApplyViewContext(), preFeeBalance_, *txSponsorSle, {.ownerCountDelta = 1});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
@@ -130,7 +126,7 @@ CredentialAccept::doApply()
|
||||
removeSponsorFromLedgerEntry(sleCred);
|
||||
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sleSubject, 1, j_);
|
||||
sleSubject.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
|
||||
view().update(sleCred);
|
||||
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h> // IWYU pragma: keep
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
@@ -84,7 +85,7 @@ CredentialCreate::preclaim(PreclaimContext const& ctx)
|
||||
auto const credType(ctx.tx[sfCredentialType]);
|
||||
auto const subject = ctx.tx[sfSubject];
|
||||
|
||||
auto const subjectSle = ctx.view.read(keylet::account(subject));
|
||||
auto const subjectSle = AccountRootEntryR(subject, ctx.view);
|
||||
|
||||
if (!subjectSle)
|
||||
{
|
||||
@@ -134,12 +135,12 @@ CredentialCreate::doApply()
|
||||
sleCred->setFieldU32(sfExpiration, *optExp);
|
||||
}
|
||||
|
||||
auto const sleIssuer = view().peek(keylet::account(accountID_));
|
||||
auto sleIssuer = AccountRootEntryW(accountID_, view(), j_);
|
||||
if (!sleIssuer)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(), sleIssuer, preFeeBalance_, {.ownerCountDelta = 1}, j_);
|
||||
if (auto const ret = sleIssuer.checkReserve(
|
||||
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
@@ -159,7 +160,7 @@ CredentialCreate::doApply()
|
||||
return tecDIR_FULL;
|
||||
sleCred->setFieldU64(sfIssuerNode, *page);
|
||||
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sleIssuer, 1, j_);
|
||||
sleIssuer.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sleCred);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/SponsorHelpers.h>
|
||||
@@ -52,7 +53,7 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
|
||||
if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount])))
|
||||
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||||
|
||||
auto const sleAuthorize = ctx.view.read(keylet::account(ctx.tx[sfAuthorize]));
|
||||
auto const sleAuthorize = AccountRootEntryR(ctx.tx[sfAuthorize], ctx.view);
|
||||
if (!sleAuthorize)
|
||||
return tecNO_TARGET;
|
||||
|
||||
@@ -72,7 +73,7 @@ DelegateSet::preclaim(PreclaimContext const& ctx)
|
||||
TER
|
||||
DelegateSet::doApply()
|
||||
{
|
||||
auto const sleOwner = ctx_.view().peek(keylet::account(accountID_));
|
||||
auto sleOwner = AccountRootEntryW(accountID_, ctx_.view(), ctx_.journal);
|
||||
if (!sleOwner)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
@@ -98,12 +99,8 @@ DelegateSet::doApply()
|
||||
if (permissions.empty())
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (auto const ret = checkReserve(
|
||||
ctx_.getApplyViewContext(),
|
||||
sleOwner,
|
||||
preFeeBalance_,
|
||||
{.ownerCountDelta = 1},
|
||||
ctx_.journal);
|
||||
if (auto const ret = sleOwner.checkReserve(
|
||||
ctx_.getApplyViewContext(), preFeeBalance_, {.ownerCountDelta = 1});
|
||||
!isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
@@ -133,7 +130,7 @@ DelegateSet::doApply()
|
||||
(*sle)[sfDestinationNode] = *destPage;
|
||||
|
||||
ctx_.view().insert(sle);
|
||||
increaseOwnerCount(ctx_.getApplyViewContext(), sleOwner, 1, ctx_.journal);
|
||||
sleOwner.increaseOwnerCount(ctx_.getApplyViewContext(), 1);
|
||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
@@ -170,7 +167,7 @@ DelegateSet::deleteDelegate(ApplyView& view, SLE::Ref sle, beast::Journal j)
|
||||
}
|
||||
|
||||
// Only the delegating account's owner count was incremented on creation
|
||||
auto const sleOwner = view.peek(keylet::account(delegator));
|
||||
auto sleOwner = AccountRootEntryW(delegator, view);
|
||||
if (!sleOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -119,7 +120,7 @@ AMMBid::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
for (auto const& account : ctx.tx.getFieldArray(sfAuthAccounts))
|
||||
{
|
||||
if (!ctx.view.read(keylet::account(account[sfAccount])))
|
||||
if (!AccountRootEntryR(account[sfAccount], ctx.view))
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Bid: Invalid Account.";
|
||||
return terNO_ACCOUNT;
|
||||
@@ -208,7 +209,7 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa
|
||||
auto validOwner = [&](AccountID const& account) {
|
||||
// Valid range is 0-19 but the tailing slot pays MinSlotPrice
|
||||
// and doesn't refund so the check is < instead of <= to optimize.
|
||||
return timeSlot && *timeSlot < kTailingSlot && sb.read(keylet::account(account));
|
||||
return timeSlot && *timeSlot < kTailingSlot && AccountRootEntryR(account, sb);
|
||||
};
|
||||
|
||||
auto updateSlot = [&](std::uint32_t fee, Number const& minPrice, Number const& burn) -> TER {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/utility/Zero.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
@@ -102,11 +103,11 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
auto const asset = ctx.tx[sfAsset];
|
||||
auto const asset2 = ctx.tx[sfAsset2];
|
||||
auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
|
||||
auto const sleIssuer = AccountRootEntryR(ctx.tx[sfAccount], ctx.view);
|
||||
if (!sleIssuer)
|
||||
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||||
|
||||
if (!ctx.view.read(keylet::account(ctx.tx[sfHolder])))
|
||||
if (!AccountRootEntryR(ctx.tx[sfHolder], ctx.view))
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
|
||||
@@ -178,7 +179,7 @@ AMMClawback::applyGuts(Sandbox& sb)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const ammAccount = (*ammSle)[sfAccount];
|
||||
auto const accountSle = sb.read(keylet::account(ammAccount));
|
||||
auto const accountSle = AccountRootEntryR(ammAccount, sb);
|
||||
if (!accountSle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
@@ -133,7 +134,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
if (asset.holds<MPTIssue>() || isXRP(asset))
|
||||
return false;
|
||||
|
||||
if (auto const issuerAccount = view.read(keylet::account(asset.getIssuer())))
|
||||
if (auto const issuerAccount = AccountRootEntryR(asset.getIssuer(), view))
|
||||
return !issuerAccount->isFlag(lsfDefaultRipple);
|
||||
|
||||
return false;
|
||||
@@ -173,7 +174,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
auto isLPToken = [&](STAmount const& amount) -> bool {
|
||||
if (auto const sle = ctx.view.read(keylet::account(amount.asset().getIssuer())))
|
||||
if (auto const sle = AccountRootEntryR(amount.asset().getIssuer(), ctx.view))
|
||||
return sle->isFieldPresent(sfAMMID);
|
||||
return false;
|
||||
};
|
||||
@@ -237,7 +238,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
||||
[&](Issue const& issue) -> TER {
|
||||
if (isXRP(issue))
|
||||
return tesSUCCESS;
|
||||
auto const sle = ctx.view.read(keylet::account(issue.account));
|
||||
auto const sle = AccountRootEntryR(issue.account, ctx.view);
|
||||
if (!sle)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
if (sle->isFlag(lsfAllowTrustLineClawback))
|
||||
|
||||
@@ -89,7 +89,7 @@ applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& accountID, beast::Jou
|
||||
auto const lpTokensNew = ammLPHolds(sb, *ammSle, accountID, ctx.journal);
|
||||
std::optional<STAmount> minTokens;
|
||||
std::size_t minPos{0};
|
||||
AccountID minAccount;
|
||||
AccountID minAccount{0};
|
||||
std::uint32_t minFee{0};
|
||||
STArray updatedVoteSlots;
|
||||
Number num{0};
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
|
||||
#include <xrpl/ledger/helpers/TokenHelpers.h>
|
||||
@@ -344,7 +344,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
||||
if (!ammSle)
|
||||
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
|
||||
auto const ammAccountID = (*ammSle)[sfAccount];
|
||||
auto const accountSle = sb.read(keylet::account(ammAccountID));
|
||||
auto const accountSle = AccountRootEntryR(ammAccountID, sb);
|
||||
if (!accountSle)
|
||||
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
|
||||
auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
|
||||
@@ -691,16 +691,15 @@ AMMWithdraw::withdraw(
|
||||
reserveHandling == ReserveHandling::IgnoreReserve)
|
||||
return tesSUCCESS;
|
||||
|
||||
auto sleAccount = view.peek(keylet::account(account));
|
||||
auto sleAccount = AccountRootEntryW(account, view, journal);
|
||||
if (!sleAccount)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const balance = (*sleAccount)[sfBalance]->xrp();
|
||||
// See also TrustSet::doApply() and MPTokenAuthorize::authorize()
|
||||
XRPAmount const reserve(
|
||||
(ownerCount(sleAccount, journal) < 2)
|
||||
? XRPAmount(beast::kZero)
|
||||
: accountReserve(view, sleAccount, journal, {.ownerCountDelta = 1}));
|
||||
(sleAccount.ownerCount() < 2) ? XRPAmount(beast::kZero)
|
||||
: sleAccount.reserve({.ownerCountDelta = 1}));
|
||||
|
||||
auto const balanceAdj = std::max(priorBalance, balance);
|
||||
if (balanceAdj < reserve)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <xrpl/tx/transactors/dex/OfferCancel.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/OfferHelpers.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -33,7 +35,7 @@ OfferCancel::preclaim(PreclaimContext const& ctx)
|
||||
auto const id = ctx.tx[sfAccount];
|
||||
auto const offerSequence = ctx.tx[sfOfferSequence];
|
||||
|
||||
auto const sle = ctx.view.read(keylet::account(id));
|
||||
auto const sle = AccountRootEntryR(id, ctx.view);
|
||||
if (!sle)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
@@ -54,7 +56,7 @@ OfferCancel::doApply()
|
||||
{
|
||||
auto const offerSequence = ctx_.tx[sfOfferSequence];
|
||||
|
||||
auto const sle = view().read(keylet::account(accountID_));
|
||||
auto const sle = AccountRootEntryW(accountID_, view());
|
||||
if (!sle)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/ledger/Sandbox.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||
@@ -181,7 +181,7 @@ OfferCreate::preclaim(PreclaimContext const& ctx)
|
||||
|
||||
auto const cancelSequence = ctx.tx[~sfOfferSequence];
|
||||
|
||||
auto const sleCreator = ctx.view.read(keylet::account(id));
|
||||
auto const sleCreator = AccountRootEntryR(id, ctx.view);
|
||||
if (!sleCreator)
|
||||
return terNO_ACCOUNT;
|
||||
|
||||
@@ -288,15 +288,14 @@ OfferCreate::checkAcceptAsset(
|
||||
// Only valid for custom currencies
|
||||
XRPL_ASSERT(!isXRP(asset), "xrpl::OfferCreate::checkAcceptAsset : input is not XRP");
|
||||
|
||||
auto const issuerAccount = view.read(keylet::account(asset.getIssuer()));
|
||||
auto const issuerAccount = AccountRootEntryR(asset.getIssuer(), view);
|
||||
|
||||
if (!issuerAccount)
|
||||
{
|
||||
JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: "
|
||||
<< to_string(asset.getIssuer());
|
||||
|
||||
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_ACCOUNT}
|
||||
: TER{tecNO_ISSUER};
|
||||
return ((flags & TapRetry) != 0u) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER};
|
||||
}
|
||||
|
||||
// An account cannot create a trustline to itself, so no line can exist
|
||||
@@ -319,8 +318,7 @@ OfferCreate::checkAcceptAsset(
|
||||
{
|
||||
JLOG(j.debug()) << "delay: can't receive IOUs from issuer with "
|
||||
"DisallowIncomingTrustline set";
|
||||
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_LINE}
|
||||
: TER{tecNO_LINE};
|
||||
return ((flags & TapRetry) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,8 +326,7 @@ OfferCreate::checkAcceptAsset(
|
||||
{
|
||||
if (!trustLine)
|
||||
{
|
||||
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_LINE}
|
||||
: TER{tecNO_LINE};
|
||||
return ((flags & TapRetry) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE};
|
||||
}
|
||||
|
||||
// Entries have a canonical representation, determined by a
|
||||
@@ -345,8 +342,7 @@ OfferCreate::checkAcceptAsset(
|
||||
JLOG(j.debug()) << "delay: can't receive IOUs from "
|
||||
"issuer without auth.";
|
||||
|
||||
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_AUTH}
|
||||
: TER{tecNO_AUTH};
|
||||
return ((flags & TapRetry) != 0u) ? TER{terNO_AUTH} : TER{tecNO_AUTH};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,14 +707,14 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
// Not XRP or MPT
|
||||
if (!saTakerPays.integral())
|
||||
{
|
||||
auto const sle = sb.read(keylet::account(uPaysIssuerID));
|
||||
auto const sle = AccountRootEntryR(uPaysIssuerID, sb);
|
||||
if (sle && sle->isFieldPresent(sfTickSize))
|
||||
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
|
||||
}
|
||||
// Not XRP or MPT
|
||||
if (!saTakerGets.integral())
|
||||
{
|
||||
auto const sle = sb.read(keylet::account(uGetsIssuerID));
|
||||
auto const sle = AccountRootEntryR(uGetsIssuerID, sb);
|
||||
if (sle && sle->isFieldPresent(sfTickSize))
|
||||
uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
|
||||
}
|
||||
@@ -888,12 +884,12 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
return {tesSUCCESS, true};
|
||||
}
|
||||
|
||||
auto const sleCreator = sb.peek(keylet::account(accountID_));
|
||||
auto sleCreator = AccountRootEntryW(accountID_, sb, viewJ);
|
||||
if (!sleCreator)
|
||||
return {tefINTERNAL, false};
|
||||
|
||||
{
|
||||
XRPAmount const reserve = accountReserve(sb, sleCreator, viewJ, {.ownerCountDelta = 1});
|
||||
XRPAmount const reserve = sleCreator.reserve({.ownerCountDelta = 1});
|
||||
if (preFeeBalance_ < reserve)
|
||||
{
|
||||
// If we are here, the signing account had an insufficient reserve
|
||||
@@ -927,7 +923,8 @@ OfferCreate::applyGuts(Sandbox& sb, Sandbox& sbCancel)
|
||||
}
|
||||
|
||||
// Update owner count.
|
||||
increaseOwnerCount(sb, sleCreator, {}, 1, viewJ);
|
||||
std::optional<AccountRootEntryW> noSponsor;
|
||||
sleCreator.increaseOwnerCount(noSponsor, 1);
|
||||
|
||||
JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.asset()) << " : "
|
||||
<< to_string(saTakerGets.asset())
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
@@ -46,7 +47,7 @@ DIDDelete::deleteSLE(ApplyView& view, SLE::pointer sle, AccountID const owner, b
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto const sleOwner = view.peek(keylet::account(owner));
|
||||
auto sleOwner = AccountRootEntryW(owner, view);
|
||||
if (!sleOwner)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyView.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/ledger/entries/AccountRootEntry.h>
|
||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -65,15 +66,14 @@ DIDSet::preflight(PreflightContext const& ctx)
|
||||
static TER
|
||||
addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner)
|
||||
{
|
||||
auto const sleAccount = ctx.view().peek(keylet::account(owner));
|
||||
auto sleAccount = AccountRootEntryW(owner, ctx.view(), ctx.journal);
|
||||
if (!sleAccount)
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Check reserve availability for new object creation
|
||||
{
|
||||
auto const balance = STAmount((*sleAccount)[sfBalance]).xrp();
|
||||
auto const reserve =
|
||||
accountReserve(ctx.view(), sleAccount, ctx.journal, {.ownerCountDelta = 1});
|
||||
auto const reserve = sleAccount.reserve({.ownerCountDelta = 1});
|
||||
|
||||
if (balance < reserve)
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
@@ -90,8 +90,9 @@ addSLE(ApplyContext& ctx, SLE::Ref sle, AccountID const& owner)
|
||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||
(*sle)[sfOwnerNode] = *page;
|
||||
}
|
||||
increaseOwnerCount(ctx.view(), sleAccount, {}, 1, ctx.journal);
|
||||
ctx.view().update(sleAccount);
|
||||
std::optional<AccountRootEntryW> noSponsor;
|
||||
sleAccount.increaseOwnerCount(noSponsor, 1);
|
||||
sleAccount.update();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
Loaded 100 of 288 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user