Compare commits

..
Author SHA1 Message Date
Mayukha Vadari 0c5aa85b59 Document isAuthorized return value 2026-10-05 15:46:29 +01:00
Mayukha Vadari 55b2ff998b refactor: Move requireAuth MPToken check onto MPTokenEntry
Add the const member MPTokenEntry::isAuthorized(), which returns the
lsfMPTAuthorized check, and use it in requireAuth(view, MPTIssue,
account, ...). The free requireAuth() stays: the rest of its body reads
the issuance, the issuer account, the vault and the domain, and the
MPToken may not exist.

Pure refactor, no behavior change.
2026-10-05 15:46:21 +01:00
Mayukha Vadari 01a21f5fb8 Document MPTokenEntry::create parameters 2026-10-05 15:45:59 +01:00
Mayukha Vadari 86bc544d7d refactor: Move createMPToken onto MPTokenEntry
Replace the free createMPToken() with the static factory
MPTokenEntry<ApplyView>::create(). The body is unchanged: peek, owner
directory insert, newSLE(), fields, sponsor, insert(). Update the
callers in checkCreateMPT(), AMMCreate and EscrowHelpers.h.

Pure refactor, no behavior change.
2026-10-05 15:45:49 +01:00
Mayukha Vadari 5e2e5bf4e8 Document isFrozen return value 2026-10-05 15:45:25 +01:00
Mayukha Vadari 7f0ce98820 Include Keylet.h in the SLEBase test
(cherry picked from commit 3cca9d3266)
2026-10-05 15:45:19 +01:00
Mayukha Vadari 1b8074cb41 test: Drop extern template declarations from SLEBase test
An explicit instantiation declaration stops clang from emitting the
inline constructors of the header-only entries at -O0, so the debug
builds fail to link xrpl_tests (undefined AccountRootEntry constructors).
Naming each class in a static_assert already instantiates its definition,
which is all the exhaustiveness check needs.

(cherry picked from commit 611415e1f9)
2026-10-05 15:45:19 +01:00
Mayukha Vadari 11a9ae0eb6 refactor: Move isFrozen MPToken branch onto MPTokenEntry
Split the ltMPTOKEN branch of isFrozen(view, account, SLE const&,
depth) into a const member MPTokenEntry::isFrozen(account, depth). The
free dispatcher stays and calls the member for an MPToken SLE; its
MPTokenIssuance branch is unchanged. accountHolds() and
ammAccountHolds() already hold an MPTokenEntryR, so they call the member
directly.

The member body goes to the new src/libxrpl/ledger/entries/
MPTokenEntry.cpp with explicit instantiation.

Pure refactor, no behavior change.
2026-10-05 15:45:18 +01:00
Mayukha Vadari ac380ea18b test: Declare, not define, entry instantiations in SLEBase test
The exhaustive per-entry-type check in the SLEBase gtest used explicit
instantiation definitions. Once an entry class gets out-of-line member
bodies in its own .cpp (with the explicit instantiation definition
there), the test would instantiate the class a second time, and MSVC
rejects it with C4661 because those member bodies are not visible in
the test translation unit. Explicit instantiation declarations still
instantiate the class definition, which is all the check needs.

(cherry picked from commit 8b07abc3cd)
2026-10-05 15:45:18 +01:00
Mayukha Vadari b1903290a4 Document isIndividualFrozen return value 2026-10-05 15:44:53 +01:00
Mayukha Vadari a057e3de26 refactor: Move isIndividualFrozen onto MPTokenEntry
Replace the free isIndividualFrozen(MPTokenEntryR const&) overload with
a const member MPTokenEntry::isIndividualFrozen(). The
(view, account, MPTIssue) free function stays and now calls the member.

Pure refactor, no behavior change.
2026-10-05 13:57:32 +01:00
Mayukha Vadari a65d559516 refactor: Use MPTokenEntry in ConfidentialMPTHolderKeyUpdate
Develop added ConfidentialMPTHolderKeyUpdate (#8266) after this PR was
opened. Migrate its MPToken read and peek to MPTokenEntryR and
MPTokenEntryW so the PR still covers every MPToken pointer site.

Pure refactor: no behavior change.
2026-10-05 13:57:03 +01:00
Mayukha Vadari 2d1818ed97 refactor: Use MPTokenEntry everywhere
Replace raw MPToken (ltMPTOKEN) SLE pointers with MPTokenEntryR and
MPTokenEntryW in the ledger helpers and transactors. Helpers that take
an MPToken SLE (isIndividualFrozen, deleteAMMMPToken, and the
static proof helpers in ConfidentialMPTConvertBack and
ConfidentialMPTSend) now take the wrapper instead. They stay free
functions.

MPTokenIssuance pointers, and parameters that accept either an
MPToken or an MPTokenIssuance (isFrozen, assetOfHolding, the
MPTokenIssuanceSet target SLE), are unchanged.

Pure refactor: no behavior change.
2026-10-05 13:32:46 +01:00
132 changed files with 1542 additions and 1830 deletions

No files matched your search

-5
View File
@@ -30,11 +30,6 @@ Version 3.5.0 is not yet released.
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive a message with `type` `mptTransaction` for each validated transaction whose metadata affects a subscribed issuance; the message has the same fields as the `transactions` stream. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
### Bugfixes in 3.5.0
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
## XRP Ledger server version 3.4.0
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
Generated
+6 -6
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1791130267,
"narHash": "sha256-1sjwcQMcgAbmiip/VivBSiK8p2bTkQGOvCuO7vnwfx4=",
"lastModified": 1781173989,
"narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9013764fcc0ea99fa16cf7aa7decf8a5c3889dd2",
"rev": "8c91a71d13451abc40eb9dae8910f972f979852f",
"type": "github"
},
"original": {
@@ -47,11 +47,11 @@
]
},
"locked": {
"lastModified": 1791189933,
"narHash": "sha256-Y8eN7ki0Urx4Ojf4w1xSpWg8uk/3lJqK+E0Oth77rH0=",
"lastModified": 1784611586,
"narHash": "sha256-OfqgY+0hp/zseZB7uyH0U8kIDPS4scZZCyAurEplvG0=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "e60029353d0c48d216bc4b065168ccd4079c166f",
"rev": "14f58845249f3552a89b07772626b8d3c632fa86",
"type": "github"
},
"original": {
+56 -147
View File
@@ -1,31 +1,29 @@
#pragma once
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/beast/type_name.h>
#include <boost/core/type_name.hpp>
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace xrpl {
/**
* Manages all counted object types.
*
* Counters register themselves on a lock-free intrusive list maintained
* by this object when constructed. Because counters are never destroyed
* or removed, the ABA problem does not apply.
*
* The registry is iterable as a forward range.
*/
class CountedObjects
{
public:
static CountedObjects&
getInstance() noexcept;
using Entry = std::pair<std::string, int>;
using List = std::vector<Entry>;
[[nodiscard]] List
getCounts(int minimumThreshold) const;
public:
/**
* Implementation for @ref CountedObject.
@@ -35,147 +33,68 @@ public:
class Counter
{
public:
Counter(std::string name) noexcept;
Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
{
// Insert ourselves at the front of the lock-free linked list
CountedObjects& instance = CountedObjects::getInstance();
Counter* head = nullptr;
// Counters are intrusive list nodes whose addresses are published
// in the registry; they must never be copied or moved. The atomic
// members already force this, but we make it explicit.
Counter(Counter const&) = delete;
Counter&
operator=(Counter const&) = delete;
Counter(Counter&&) = delete;
Counter&
operator=(Counter&&) = delete;
do
{
head = instance.head_.load();
next_ = head;
} while (instance.head_.exchange(this) != head);
std::uint32_t
++instance.count_;
}
~Counter() noexcept = default;
int
increment() noexcept
{
auto const newCount = count_.fetch_add(1, std::memory_order::relaxed) + 1;
XRPL_ASSERT(newCount != 0, "xrpl::CountedObjects::Counter::increment : no overflow");
auto maxCount = maxCount_.load(std::memory_order::relaxed);
while (newCount > maxCount &&
!maxCount_.compare_exchange_weak(maxCount, newCount, std::memory_order::relaxed))
{
}
return newCount;
return ++count_;
}
std::uint32_t
int
decrement() noexcept
{
auto const prev = count_.fetch_sub(1, std::memory_order::relaxed);
XRPL_ASSERT(prev != 0, "xrpl::CountedObjects::Counter::decrement : no underflow");
return prev - 1;
return --count_;
}
[[nodiscard]] std::uint32_t
count() const noexcept
[[nodiscard]] int
getCount() const noexcept
{
return count_.load(std::memory_order::relaxed);
return count_.load();
}
[[nodiscard]] std::uint32_t
max() const noexcept
[[nodiscard]] Counter*
getNext() const noexcept
{
return std::max(
count_.load(std::memory_order::relaxed),
maxCount_.load(std::memory_order::relaxed));
return next_;
}
[[nodiscard]] std::string const&
name() const noexcept
getName() const noexcept
{
return name_;
}
private:
friend class CountedObjects;
Counter* next_ = nullptr;
std::atomic<std::uint32_t> count_ = 0;
std::atomic<std::uint32_t> maxCount_ = 0;
std::string const name_;
std::atomic<int> count_;
Counter* next_;
};
class Iterator
{
public:
using value_type = Counter const;
using reference = value_type&;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
explicit Iterator(Counter* c = nullptr) noexcept : current_(c)
{
}
reference
operator*() const noexcept
{
return *current_;
}
pointer
operator->() const noexcept
{
return current_;
}
Iterator&
operator++() noexcept
{
current_ = current_->next_;
return *this;
}
Iterator
operator++(int) noexcept
{
auto tmp = *this;
++*this;
return tmp;
}
bool
operator==(Iterator const&) const noexcept = default;
private:
Counter* current_;
};
constexpr CountedObjects() noexcept = default;
[[nodiscard]] auto
begin() const noexcept
{
return Iterator{head_.load(std::memory_order::acquire)};
}
[[nodiscard]] auto
end() const noexcept
{
return Iterator{};
}
private:
std::atomic<Counter*> head_ = nullptr;
CountedObjects() noexcept;
~CountedObjects() noexcept = default;
private:
std::atomic<int> count_;
std::atomic<Counter*> head_;
};
/** The global counted object registry. */
inline constinit CountedObjects gCountedObjects;
inline CountedObjects::Counter::Counter(std::string name) noexcept
: next_(gCountedObjects.head_.load(std::memory_order::relaxed)), name_(std::move(name))
{
while (!gCountedObjects.head_.compare_exchange_weak(
next_, this, std::memory_order::release, std::memory_order::relaxed))
;
}
//------------------------------------------------------------------------------
/**
@@ -184,37 +103,27 @@ inline CountedObjects::Counter::Counter(std::string name) noexcept
* Derived classes have their instances counted automatically. This is used
* for reporting purposes.
*
* The constructors are private and `Object` is befriended so that the
* CRTP parameter must be the deriving class itself: a copy-paste error
* like `class B : public CountedObject<A>` fails to compile instead of
* silently polluting A's count.
*
* @note This class has no move operations by design: a derived class's
* move constructor falls back to the copy constructor for this
* base, so the newly created instance is counted. This keeps the
* invariant that count is the number of outstanding subobjects.
*
* @warning Counted objects constructed during dynamic initialization of
* other translation units may have their increments discarded when
* counter itself is dynamically initialized. Do not create counted
* objects before main() begins.
*
* @ingroup basics
*/
template <class Object>
requires std::is_class_v<Object>
class CountedObject
{
static inline CountedObjects::Counter counter{boost::core::type_name<Object>()};
private:
static auto&
getCounter() noexcept
{
static CountedObjects::Counter kC{beast::typeName<Object>()};
return kC;
}
CountedObject() noexcept
{
counter.increment();
getCounter().increment();
}
CountedObject(CountedObject const&) noexcept
{
counter.increment();
getCounter().increment();
}
CountedObject&
@@ -223,7 +132,7 @@ class CountedObject
public:
~CountedObject() noexcept
{
counter.decrement();
getCounter().decrement();
}
friend Object;
+2 -3
View File
@@ -3,11 +3,11 @@
#pragma once
#include <xrpl/basics/ByteUtilities.h>
#include <xrpl/beast/type_name.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <boost/align.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/core/type_name.hpp>
#include <boost/predef.h>
#include <algorithm>
@@ -16,7 +16,6 @@
#include <cstring>
#include <mutex>
#include <stdexcept>
#include <typeinfo>
#include <vector>
#if BOOST_OS_LINUX
@@ -336,7 +335,7 @@ public:
}) != cfg.end())
{
throw std::runtime_error(
"SlabAllocatorSet<" + boost::core::type_name<Type>() + ">: duplicate slab size");
"SlabAllocatorSet<" + beast::typeName<Type>() + ">: duplicate slab size");
}
for (auto const& c : cfg)
+2 -3
View File
@@ -1,8 +1,7 @@
#pragma once
#include <xrpl/basics/sanitizers.h>
#include <boost/core/type_name.hpp>
#include <xrpl/beast/type_name.h>
#include <exception>
#include <string>
@@ -56,7 +55,7 @@ Throw(Args&&... args)
std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception.");
E e(std::forward<Args>(args)...);
logThrow("Throwing exception of type " + boost::core::type_name<E>() + ": " + e.what());
logThrow(std::string("Throwing exception of type " + beast::typeName<E>() + ": ") + e.what());
throw std::move(e);
}
-200
View File
@@ -1,200 +0,0 @@
#pragma once
#include <concepts>
#include <type_traits>
#include <utility>
namespace xrpl {
/**
* Opt-in bitwise operators for scoped enumerations.
*
* Provides `&`, `|`, `^`, `~` and the corresponding compound assignment forms
* for any scoped enumeration with an unsigned underlying type that opts in by
* specializing @ref OptIn:
*
* @code
* namespace xrpl {
*
* enum class MyFlags : std::uint32_t { a = 1, b = 2, c = 4 };
*
* template <>
* struct enum_bitops::OptIn<MyFlags> : std::true_type {};
*
* } // namespace xrpl
* @endcode
*
* The operators are constexpr and noexcept, and return the enumeration type.
*
* @par Where the specialization may be declared
* In namespace xrpl, or at global scope with full qualification, and after
* the enumeration is defined but before the operators are first used. This
* cannot be done in a nested namespace or at class scope; for enumerations
* nested in a class this means after the class definition.
*
* @par Where the operators are found
* The operators are declared in namespace xrpl, so argument-dependent lookup
* will find them only for enumerations declared in xrpl or nested in a class
* declared directly in xrpl. Enumerations in a nested namespace will only be
* found from code inside xrpl, and only if no enclosing scope declares an
* operator of the same name; elsewhere they require using-declarations.
*/
namespace enum_bitops {
/**
* Types that may be opted in to the bitwise operators.
*
* Satisfied by a scoped enumeration whose underlying type is an unsigned
* integer type.
*
* @tparam T The type to test.
*/
template <typename T>
concept Eligible = std::is_scoped_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>;
/**
* Opt-in switch for the bitwise operators.
*
* The primary template derives from std::false_type. Specialize it to derive
* from std::true_type to enable the operators for an @ref Eligible enumeration.
*
* @tparam T The scoped enumeration to opt in.
*/
template <Eligible T>
struct OptIn : std::false_type
{
};
/**
* Enumerations for which the bitwise operators are enabled.
*
* Satisfied when @p T satisfies @ref Eligible and @ref OptIn has been
* specialized for it to derive from std::true_type.
*
* @tparam T The type to test.
*/
template <typename T>
concept Candidate = Eligible<T> && OptIn<T>::value;
} // namespace enum_bitops
// These are declared in xrpl, not in enum_bitops, so that argument-dependent
// lookup finds them for enumerations whose associated namespace is xrpl.
/**
* @name Bitwise operators for opted-in scoped enumerations
*
* Each operator applies the corresponding built-in operator to the
* underlying values and converts the result back to the enumeration type.
* Both operands must have the same enumeration type; there is no implicit
* conversion to or from the underlying type.
*/
/** @{ */
/**
* Bitwise AND.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in both @p lhs and @p rhs.
*/
template <enum_bitops::Candidate T>
constexpr T
operator&(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) & std::to_underlying(rhs));
}
/**
* Bitwise OR.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in @p lhs, in @p rhs, or in both.
*/
template <enum_bitops::Candidate T>
constexpr T
operator|(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) | std::to_underlying(rhs));
}
/**
* Bitwise exclusive OR.
*
* @param lhs The left operand.
* @param rhs The right operand.
* @return The bits set in exactly one of @p lhs and @p rhs.
*/
template <enum_bitops::Candidate T>
constexpr T
operator^(T lhs, T rhs) noexcept
{
return static_cast<T>(std::to_underlying(lhs) ^ std::to_underlying(rhs));
}
/**
* Bitwise complement.
*
* The result has every bit of the underlying type that is clear in
* @p val, including bits that no enumerator names. It is intended for
* clearing flags, as in `flags & ~flag`.
*
* @param val The operand.
* @return The complement of @p val.
*/
template <enum_bitops::Candidate T>
constexpr T
operator~(T val) noexcept
{
return static_cast<T>(~std::to_underlying(val));
}
/**
* Bitwise AND assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator&=(T& lhs, T rhs) noexcept
{
lhs = lhs & rhs;
return lhs;
}
/**
* Bitwise OR assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator|=(T& lhs, T rhs) noexcept
{
lhs = lhs | rhs;
return lhs;
}
/**
* Bitwise exclusive OR assignment.
*
* @param lhs The value to modify.
* @param rhs The right operand.
* @return A reference to @p lhs.
*/
template <enum_bitops::Candidate T>
constexpr T&
operator^=(T& lhs, T rhs) noexcept
{
lhs = lhs ^ rhs;
return lhs;
}
/** @} */
} // namespace xrpl
+46 -151
View File
@@ -2,212 +2,107 @@
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
#include <concepts>
#include <cstdint>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
namespace xrpl {
/** Every value of @p Src can be represented by @p Dest.
// safe_cast adds compile-time checks to a static_cast to ensure that
// the destination can hold all values of the source. This is particularly
// handy when the source or destination is an enumeration type.
Given two integral types, the cast is safe when the destination can
hold every possible value of the source.
template <class Src, class Dest>
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
(std::is_signed_v<Src> || std::is_unsigned_v<Dest>) &&
(std::is_signed_v<Src> != std::is_signed_v<Dest> ? sizeof(Dest) > sizeof(Src)
: sizeof(Dest) >= sizeof(Src));
Comparing the bounds requires care: we use @c std::cmp_less_equal and
@c std::cmp_greater_equal; the plain relational operators would apply
arithmetic conversions, resulting in incorrect results when comparing
across signedness.
Because @c std::cmp_* requires standard signed or unsigned integer
arguments, which excludes character types and bool, we first widen
all type bounds to the maximum-width integer type while preserving
signedness.
@note Extended integer types, like __int128 on gcc, cannot be safely
widened to a standard integer type, so the concept will reject
them.
*/
template <typename Src, typename Dest>
concept SafeToCast = std::is_integral_v<Src> && std::is_integral_v<Dest> && []() consteval {
using WideSrc = std::conditional_t<std::is_signed_v<Src>, std::intmax_t, std::uintmax_t>;
using WideDest = std::conditional_t<std::is_signed_v<Dest>, std::intmax_t, std::uintmax_t>;
// Note: this guard must be an evaluated branch, and not a
// static_assert. The lambda body is outside the immediate
// context, so a substitution-time failure here would be a
// hard error rather than leaving the concept unsatisfied.
if constexpr (sizeof(Src) > sizeof(WideSrc) || sizeof(Dest) > sizeof(WideDest))
{
return false;
}
else
{
return std::cmp_less_equal(
static_cast<WideDest>(std::numeric_limits<Dest>::min()),
static_cast<WideSrc>(std::numeric_limits<Src>::min())) &&
std::cmp_greater_equal(
static_cast<WideDest>(std::numeric_limits<Dest>::max()),
static_cast<WideSrc>(std::numeric_limits<Src>::max()));
}
}();
/** Compile-time-checked static_cast that rejects non-value preserving casts.
@note There is deliberately no enum-to-enum overload, and no overload
returning the underlying type of an enum. For the latter, use
@c std::to_underlying.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{
static_assert(
SafeToCast<Src, Dest>, "This cast is not value-preserving. Please use unsafeCast instead.");
std::is_signed_v<Dest> || std::is_unsigned_v<Src>, "Cannot cast signed to unsigned");
constexpr unsigned kNotSame = std::is_signed_v<Dest> != std::is_signed_v<Src>;
static_assert(
sizeof(Dest) >= sizeof(Src) + kNotSame,
"Destination is too small to hold all values of source");
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
{
return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
template <class Dest, class Src>
constexpr Dest
safeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
{
return safeCast<Dest>(std::to_underlying(s));
return safeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
/** @} */
/** Integral-to-integral cast that is known to be narrowing or sign-erasing.
// unsafe_cast explicitly flags a static_cast as not necessarily able to hold
// all values of the source. It includes a compile-time check so that if
// underlying types become safe, it can be converted to a safe_cast.
This explicitly flags a conversion that can lose information for some
values of the source type, where the call site accepts that loss (or
truncation is the intended behavior).
The compile-time check ensures the cast remains "unsafe": if the types
involved later change such that the conversion becomes inherently
value-preserving, the static assertion fires with instructions to
migrate the call site to @ref safeCast.
If the conversion's safety depends on a runtime precondition rather
than on the types, or varies across instantiations of generic code,
use @ref checkedCast instead.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
{
static_assert(
!SafeToCast<Src, Dest>, "This cast is value-preserving. Please use safeCast instead.");
!SafeToCast<Src, Dest>,
"Only unsafe if casting signed to unsigned or "
"destination is too small");
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
{
return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
template <class Dest, class Src>
constexpr Dest
unsafeCast(Src s) noexcept
{
return unsafeCast<Dest>(std::to_underlying(s));
}
/** @} */
/** Integral-to-integral cast when the caller has performed a bounds check.
This documents that a runtime precondition or external invariant, which
is not necessarily visible to the compiler, guarantees that the requested
conversion is value-preserving for the values that can actually occur.
This is primarily meant for generic code, where the same expression may
be value-preserving for one instantiation but not for another, making
both @ref safeCast and @ref unsafeCast unusable.
Unlike @ref safeCast and @ref unsafeCast, this imposes no static check
on the type relationship: the caller's claim is about runtime values,
not about types.
*/
/** @{ */
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return static_cast<Dest>(s);
}
template <typename Dest, typename Src>
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return static_cast<Dest>(checkedCast<std::underlying_type_t<Dest>>(s));
}
template <typename Dest, typename Src>
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
constexpr Dest
checkedCast(Src s) noexcept
{
return checkedCast<Dest>(std::to_underlying(s));
return unsafeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
/** @} */
/** Downcast within a class hierarchy, verified in debug builds.
Performs a static_cast down a hierarchy, but in debug builds verifies
via dynamic_cast that the object's dynamic type actually permits the
downcast. Both build modes execute the same conversion; debug builds
merely add the check.
The pointer form passes null through unchanged, as dynamic_cast does.
@note The check requires @p Src to be polymorphic; in release builds
an invalid downcast is undefined behavior on use, exactly as
with a bare static_cast.
*/
/** @{ */
template <class Dest, class Src>
requires(
std::is_pointer_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_pointer_t<Dest>, Src> &&
(std::is_const_v<std::remove_pointer_t<Dest>> || !std::is_const_v<Src>) &&
(std::is_volatile_v<std::remove_pointer_t<Dest>> || !std::is_volatile_v<Src>))
requires std::is_pointer_v<Dest>
inline Dest
safeDowncast(Src* s) noexcept
{
XRPL_ASSERT(
s == nullptr || dynamic_cast<Dest>(s) != nullptr, "xrpl::safeDowncast : valid downcast");
#ifdef NDEBUG
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
#else
auto* result = dynamic_cast<Dest>(s);
XRPL_ASSERT(result != nullptr, "xrpl::safeDowncast : pointer downcast is valid");
return result;
#endif
}
template <class Dest, class Src>
requires(
std::is_lvalue_reference_v<Dest> && std::is_polymorphic_v<Src> &&
std::derived_from<std::remove_reference_t<Dest>, Src>)
requires std::is_lvalue_reference_v<Dest>
inline Dest
safeDowncast(Src& s) noexcept
{
return *safeDowncast<std::add_pointer_t<std::remove_reference_t<Dest>>>(std::addressof(s));
#ifndef NDEBUG
XRPL_ASSERT(
dynamic_cast<std::add_pointer_t<std::remove_reference_t<Dest>>>(&s) != nullptr,
"xrpl::safeDowncast : reference downcast is valid");
#endif
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
}
/** @} */
} // namespace xrpl
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <cstdlib>
#include <string>
#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
#include <cxxabi.h>
#endif
namespace beast {
template <typename T>
std::string
typeName()
{
using TR = std::remove_reference_t<T>;
std::string name = typeid(TR).name();
#ifndef _MSC_VER
if (auto s = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr))
{
name = s;
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
std::free(s);
}
#endif
if (std::is_const_v<TR>)
name += " const";
if (std::is_volatile_v<TR>)
name += " volatile";
if (std::is_lvalue_reference_v<T>)
{
name += "&";
}
else if (std::is_rvalue_reference_v<T>)
{
name += "&&";
}
return name;
}
} // namespace beast
+109 -36
View File
@@ -2,24 +2,26 @@
#pragma once
#include <compare>
#include <concepts>
namespace beast {
/**
* Zero allows classes to offer efficient comparisons to zero.
*
* Zero is a struct to allow classes to efficiently compare with zero without
* requiring an rvalue construction.
*
* It's often the case that we have classes which combine a number and a unit.
* In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
* like t > 1 or t != 1 do not. Comparing against kZero expresses exactly that,
* without constructing a T.
* like t > 1 or t != 1 do not.
*
* A type T participates if either `t.signum()` or an unqualified `signum(t)`
* found by argument-dependent lookup returns an integer that is negative,
* zero, or positive according to the sign of t. Both `t == kZero` and
* `kZero == t` work, as do all six relational operators in either order.
* The class Zero allows such comparisons to be easily made.
*
* The comparing class T either needs to have a method called signum() which
* returns a positive number, 0, or a negative; or there needs to be a signum
* function which resolves in the namespace which takes an instance of T and
* returns a positive, zero or negative number.
*/
struct Zero
{
explicit Zero() = default;
@@ -28,44 +30,115 @@ struct Zero
inline constexpr Zero kZero{};
/**
* Default implementation of signum: call the member function.
* Default implementation of signum calls the method on the class.
*/
template <class T>
requires requires(T const& t) {
{ t.signum() } -> std::integral;
}
[[nodiscard]] constexpr auto
signum(T const& t) noexcept(noexcept(t.signum()))
template <typename T>
auto
signum(T const& t)
{
return t.signum();
}
namespace detail {
namespace detail::zero_helper {
/**
* A type with a usable signum: either the member-based default above, or a
* `signum(t)` overload in T's own namespace, found by ADL. A user overload
* that is a better match than the template wins, as usual.
*/
// For argument dependent lookup to function properly, calls to signum must
// be made from a namespace that does not include overloads of the function..
template <class T>
concept HasSignum = requires(T const& t) {
{ signum(t) } -> std::integral;
};
} // namespace detail
template <detail::HasSignum T>
[[nodiscard]] constexpr bool
operator==(T const& t, Zero) noexcept(noexcept(signum(t)))
auto
callSignum(T const& t)
{
return signum(t) == 0;
return signum(t);
}
template <detail::HasSignum T>
[[nodiscard]] constexpr std::strong_ordering
operator<=>(T const& t, Zero) noexcept(noexcept(signum(t)))
} // namespace detail::zero_helper
// Handle operators where T is on the left side using signum.
template <typename T>
bool
operator==(T const& t, Zero)
{
return signum(t) <=> 0;
return detail::zero_helper::callSignum(t) == 0;
}
template <typename T>
bool
operator!=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) != 0;
}
template <typename T>
bool
operator<(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) < 0;
}
template <typename T>
bool
operator>(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) > 0;
}
template <typename T>
bool
operator>=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) >= 0;
}
template <typename T>
bool
operator<=(T const& t, Zero)
{
return detail::zero_helper::callSignum(t) <= 0;
}
// Handle operators where T is on the right side by
// reversing the operation, so that T is on the left side.
template <typename T>
bool
operator==(Zero, T const& t)
{
return t == kZero;
}
template <typename T>
bool
operator!=(Zero, T const& t)
{
return t != kZero;
}
template <typename T>
bool
operator<(Zero, T const& t)
{
return t > kZero;
}
template <typename T>
bool
operator>(Zero, T const& t)
{
return t < kZero;
}
template <typename T>
bool
operator>=(Zero, T const& t)
{
return t <= kZero;
}
template <typename T>
bool
operator<=(Zero, T const& t)
{
return t >= kZero;
}
} // namespace beast
+53 -12
View File
@@ -1,7 +1,7 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/enum_bitops.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/OwnerCounts.h>
#include <xrpl/ledger/ReadView.h>
@@ -22,39 +22,80 @@
namespace xrpl {
enum class ApplyFlags : std::uint32_t {
None = 0x00,
// Bitwise flag enum with existing operator overloads
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum ApplyFlags : std::uint32_t {
TapNone = 0x00,
// This is a local transaction with the
// fail_hard flag set.
FailHard = 0x10,
TapFailHard = 0x10,
// This is not the transaction's last pass
// Transaction can be retried, soft failures allowed
Retry = 0x20,
TapRetry = 0x20,
// Transaction came from a privileged source
Unlimited = 0x400,
TapUnlimited = 0x400,
// Transaction is executing as part of a batch
Batch = 0x800,
TapBatch = 0x800,
// Transaction shouldn't be applied
// Signatures shouldn't be checked
DryRun = 0x1000,
TapDryRun = 0x1000,
// Transaction is being preflighted as the payload of a
// TransactionProposalCreate. Its signatures are collected on-ledger
// afterward, so signature-presence checks (e.g. Batch signer matching)
// are skipped at proposal-creation time (On-Chain Cosigner spec
// §5.3.1.2).
Proposal = 0x2000
TapProposal = 0x2000
};
template <>
struct enum_bitops::OptIn<ApplyFlags> : std::true_type
constexpr ApplyFlags
operator|(ApplyFlags const& lhs, ApplyFlags const& rhs)
{
};
return safeCast<ApplyFlags>(
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) |
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
}
static_assert((TapFailHard | TapRetry) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
static_assert((TapRetry | TapFailHard) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
constexpr ApplyFlags
operator&(ApplyFlags const& lhs, ApplyFlags const& rhs)
{
return safeCast<ApplyFlags>(
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) &
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
}
static_assert((TapFailHard & TapRetry) == TapNone, "ApplyFlags operator &");
static_assert((TapRetry & TapFailHard) == TapNone, "ApplyFlags operator &");
constexpr ApplyFlags
operator~(ApplyFlags const& flags)
{
return safeCast<ApplyFlags>(~safeCast<std::underlying_type_t<ApplyFlags>>(flags));
}
static_assert(~TapRetry == safeCast<ApplyFlags>(0xFFFFFFDFu), "ApplyFlags operator ~");
inline ApplyFlags
operator|=(ApplyFlags& lhs, ApplyFlags const& rhs)
{
lhs = lhs | rhs;
return lhs;
}
inline ApplyFlags
operator&=(ApplyFlags& lhs, ApplyFlags const& rhs)
{
lhs = lhs & rhs;
return lhs;
}
//------------------------------------------------------------------------------
@@ -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>;
@@ -8,8 +8,12 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
@@ -47,6 +51,74 @@ public:
: Base(keylet::mptoken(mptokenKey), view, j)
{
}
/**
* Returns true if this MPToken carries the individual-lock flag
* (lsfMPTLocked).
*
* @warning This checks only the raw per-holder lock bit. It does not
* perform the transitive vault pseudo-account check. Use isFrozen() to
* decide whether the holder may send or receive tokens.
*
* @return true if lsfMPTLocked is set on this MPToken
*/
[[nodiscard]] bool
isIndividualFrozen() const
{
return (*this)->isFlag(lsfMPTLocked);
}
/**
* Returns true if the issuer authorized this MPToken (lsfMPTAuthorized).
*
* This is only the MPToken-side part of requireAuth(). It does not check
* whether the issuance requires authorization, the issuance's domain, or
* the pseudo-account and vault-share rules.
*
* @return true if lsfMPTAuthorized is set on this MPToken
*/
[[nodiscard]] bool
isAuthorized() const
{
return (*this)->isFlag(lsfMPTAuthorized);
}
/**
* Returns true if @p account cannot send or receive tokens of this
* MPToken's issuance because a freeze applies: the issuance is globally
* locked, this MPToken is individually locked, or (for a vault share)
* the vault pseudo-account's underlying asset is frozen.
*
* The issuance is read once for the global-freeze and vault checks.
*
* @param account The holder of this MPToken.
* @param depth Current recursion depth for the vault-share walk.
* @return true if @p account is frozen out of this MPToken's issuance
*/
[[nodiscard]] bool
isFrozen(AccountID const& account, std::uint8_t depth = 0) const;
/**
* Creates the MPToken of @p account for @p mptIssuanceID: links it into
* the owner directory, sets its fields and sponsor, and inserts it.
*
* Does not change the owner count.
*
* @param view The apply view to create the MPToken in.
* @param mptIssuanceID The issuance this MPToken is for.
* @param account The holder to create the MPToken for.
* @param sponsorSle The sponsor to record on the MPToken, or nullptr.
* @param flags The flags to set on the new MPToken.
* @return tesSUCCESS, or tecDIR_FULL if the owner directory is full
*/
[[nodiscard]] static TER
create(
ApplyView& view,
MPTID const& mptIssuanceID,
AccountID const& account,
SLE::Ref sponsorSle,
std::uint32_t flags)
requires Base::kIsWritable;
};
using MPTokenEntryR = MPTokenEntry<ReadView>;
+2 -1
View File
@@ -4,6 +4,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -213,7 +214,7 @@ escrowUnlockApplyHelper<MPTIssue>(
!isTesSuccess(ret))
return ret;
if (auto const ter = createMPToken(ctx.view, mptID, receiver, *sponsorSle, 0);
if (auto const ter = MPTokenEntryW::create(ctx.view, mptID, receiver, *sponsorSle, 0);
!isTesSuccess(ter))
{
return ter; // LCOV_EXCL_LINE
@@ -47,9 +47,6 @@ isGlobalFrozen(SLE const& issuanceSle);
[[nodiscard]] bool
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue);
[[nodiscard]] bool
isIndividualFrozen(SLE const& mptSle);
/**
* Returns true if @p account cannot send or receive tokens of @p mptIssue
* because a freeze applies. This is the complete check callers should use
@@ -282,14 +279,6 @@ unlockEscrowMPT(
STAmount const& grossAmount,
beast::Journal j);
TER
createMPToken(
ApplyView& view,
MPTID const& mptIssuanceID,
AccountID const& account,
SLE::Ref sponsorSle,
std::uint32_t const flags);
TER
checkCreateMPT(
xrpl::ApplyView& view,
@@ -4,6 +4,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/IOUAmount.h>
@@ -275,13 +276,12 @@ deleteAMMTrustLine(
beast::Journal j);
/**
* Delete AMMs MPToken. The passed `sle` must be obtained from a prior
* call to view.peek().
* Delete AMMs MPToken.
*/
[[nodiscard]] TER
deleteAMMMPToken(
ApplyView& view,
SLE::pointer sleMPT,
MPTokenEntryW& sleMPT,
AccountID const& ammAccountID,
beast::Journal j);
+16
View File
@@ -101,6 +101,22 @@ operator<<(std::ostream& os, AccountID const& x)
return os;
}
/**
* Initialize the global cache used to map AccountID to base58 conversions.
*
* The cache is optional and need not be initialized. But because conversion
* is expensive (it requires a SHA-256 operation) in most cases the overhead
* of the cache is worth the benefit.
*
* @param count The number of entries the cache should accommodate. Zero will
* disable the cache, releasing any memory associated with it.
*
* @note The function will only initialize the cache the first time it is
* invoked. Subsequent invocations do nothing.
*/
void
initAccountIdCache(std::size_t count);
} // namespace xrpl
//------------------------------------------------------------------------------
+2 -2
View File
@@ -1,10 +1,10 @@
#pragma once
#include <xrpl/basics/contract.h>
#include <xrpl/beast/type_name.h>
#include <xrpl/protocol/SOTemplate.h>
#include <boost/container/flat_map.hpp>
#include <boost/core/type_name.hpp>
#include <algorithm>
#include <cstddef>
@@ -84,7 +84,7 @@ public:
* Derived classes will load the object with all the known formats.
*/
private:
KnownFormats() : name_(boost::core::type_name<Derived>())
KnownFormats() : name_(beast::typeName<Derived>())
{
}
+51 -56
View File
@@ -2,7 +2,6 @@
#include <xrpl/basics/CountedObject.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/enum_bitops.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h>
@@ -16,7 +15,6 @@
#include <cstdint>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
@@ -24,8 +22,18 @@ namespace xrpl {
class STPathElement final : public CountedObject<STPathElement>
{
unsigned int type_;
AccountID accountID_;
PathAsset assetID_;
AccountID issuerID_;
bool isOffer_;
std::size_t hashValue_;
public:
enum class Type : std::uint8_t {
// Bitwise values (typeCurrency | typeMPT)
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
enum Type {
TypeNone = 0x00,
TypeAccount = 0x01, // Rippling through an account (vs taking an offer).
TypeCurrency = 0x10, // Currency follows.
@@ -37,8 +45,6 @@ public:
// Combination of all types.
};
using enum Type;
STPathElement();
STPathElement(STPathElement const&) = default;
STPathElement&
@@ -56,42 +62,42 @@ public:
bool forceAsset = false);
STPathElement(
Type type,
unsigned int uType,
AccountID const& account,
PathAsset const& asset,
AccountID const& issuer);
[[nodiscard]] Type
getNodeType() const noexcept;
[[nodiscard]] std::uint32_t
getNodeType() const;
[[nodiscard]] bool
isOffer() const noexcept;
isOffer() const;
[[nodiscard]] bool
isAccount() const noexcept;
isAccount() const;
[[nodiscard]] bool
hasIssuer() const noexcept;
hasIssuer() const;
[[nodiscard]] bool
hasCurrency() const noexcept;
hasCurrency() const;
[[nodiscard]] bool
hasMPT() const noexcept;
hasMPT() const;
[[nodiscard]] bool
hasAsset() const noexcept;
hasAsset() const;
[[nodiscard]] bool
isNone() const noexcept;
isNone() const;
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
// class of offers.
[[nodiscard]] AccountID const&
getAccountID() const noexcept;
getAccountID() const;
[[nodiscard]] PathAsset const&
getPathAsset() const noexcept;
getPathAsset() const;
[[nodiscard]] Currency const&
getCurrency() const;
@@ -100,35 +106,17 @@ public:
getMPTID() const;
[[nodiscard]] AccountID const&
getIssuerID() const noexcept;
getIssuerID() const;
[[nodiscard]] bool
isType(Type pe) const noexcept;
isType(Type const& pe) const;
friend bool
operator==(STPathElement const& lhs, STPathElement const& rhs) noexcept
{
return lhs.isType(TypeAccount) == rhs.isType(TypeAccount) &&
lhs.hashValue_ == rhs.hashValue_ && lhs.accountID_ == rhs.accountID_ &&
lhs.assetID_ == rhs.assetID_ && lhs.issuerID_ == rhs.issuerID_;
}
bool
operator==(STPathElement const& t) const;
private:
static std::size_t
getHash(STPathElement const& element);
Type type_;
AccountID accountID_;
PathAsset assetID_;
AccountID issuerID_;
bool isOffer_;
std::size_t hashValue_;
};
template <>
struct enum_bitops::OptIn<STPathElement::Type> : std::true_type
{
};
template <class Hasher>
@@ -136,7 +124,7 @@ void
hash_append(Hasher& h, STPathElement const& e) noexcept
{
using beast::hash_append;
hash_append(h, e.isType(STPathElement::TypeAccount));
hash_append(h, (e.getNodeType() & STPathElement::TypeAccount) != 0u);
hash_append(h, e.getAccountID());
hash_append(h, e.getPathAsset());
hash_append(h, e.getIssuerID());
@@ -416,11 +404,11 @@ inline STPathElement::STPathElement(
}
inline STPathElement::STPathElement(
Type type,
unsigned int uType,
AccountID const& account,
PathAsset const& asset,
AccountID const& issuer)
: type_(type)
: type_(uType)
, accountID_(account)
, assetID_(asset)
, issuerID_(issuer)
@@ -435,56 +423,56 @@ inline STPathElement::STPathElement(
hashValue_ = getHash(*this);
}
inline STPathElement::Type
STPathElement::getNodeType() const noexcept
inline std::uint32_t
STPathElement::getNodeType() const
{
return type_;
}
inline bool
STPathElement::isOffer() const noexcept
STPathElement::isOffer() const
{
return isOffer_;
}
inline bool
STPathElement::isAccount() const noexcept
STPathElement::isAccount() const
{
return !isOffer();
}
inline bool
STPathElement::isType(Type pe) const noexcept
STPathElement::isType(Type const& pe) const
{
return (type_ & pe) != STPathElement::TypeNone;
return (type_ & pe) != 0u;
}
inline bool
STPathElement::hasIssuer() const noexcept
STPathElement::hasIssuer() const
{
return isType(STPathElement::TypeIssuer);
}
inline bool
STPathElement::hasCurrency() const noexcept
STPathElement::hasCurrency() const
{
return isType(STPathElement::TypeCurrency);
}
inline bool
STPathElement::hasMPT() const noexcept
STPathElement::hasMPT() const
{
return isType(STPathElement::TypeMpt);
}
inline bool
STPathElement::hasAsset() const noexcept
STPathElement::hasAsset() const
{
return isType(STPathElement::TypeAsset);
}
inline bool
STPathElement::isNone() const noexcept
STPathElement::isNone() const
{
return getNodeType() == STPathElement::TypeNone;
}
@@ -492,13 +480,13 @@ STPathElement::isNone() const noexcept
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
// class of offers.
inline AccountID const&
STPathElement::getAccountID() const noexcept
STPathElement::getAccountID() const
{
return accountID_;
}
inline PathAsset const&
STPathElement::getPathAsset() const noexcept
STPathElement::getPathAsset() const
{
return assetID_;
}
@@ -516,11 +504,18 @@ STPathElement::getMPTID() const
}
inline AccountID const&
STPathElement::getIssuerID() const noexcept
STPathElement::getIssuerID() const
{
return issuerID_;
}
inline bool
STPathElement::operator==(STPathElement const& t) const
{
return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hashValue_ == t.hashValue_ &&
accountID_ == t.accountID_ && assetID_ == t.assetID_ && issuerID_ == t.issuerID_;
}
// ------------ STPath ------------
inline STPath::STPath(std::vector<STPathElement> p) : path_(std::move(p))
-1
View File
@@ -392,7 +392,6 @@ JSS(max_ledger); // in/out: LedgerCleaner
JSS(max_queue_size); // out: TxQ
JSS(max_spend_drops); // out: AccountInfo
JSS(max_spend_drops_total); // out: AccountInfo
JSS(maximum);
JSS(mean); // out: get_aggregate_price
JSS(median); // out: get_aggregate_price
JSS(median_fee); // out: TxQ
+1 -2
View File
@@ -51,8 +51,7 @@ public:
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()})
: ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal)
{
XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
}
std::reference_wrapper<ServiceRegistry> registry;
+8 -11
View File
@@ -59,11 +59,10 @@ public:
, parentBatchId(parentBatchId)
, j(j)
{
XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::Batch, "Batch apply flag should be set");
XRPL_ASSERT((flags & TapBatch) == TapBatch, "Batch apply flag should be set");
XRPL_ASSERT_IF(
(flags & ApplyFlags::Proposal) != ApplyFlags::None,
(flags & ApplyFlags::DryRun) != ApplyFlags::None,
(flags & TapProposal) != TapNone,
(flags & TapDryRun) != TapNone,
"xrpl::PreflightContext : proposal preflight implies dry run");
}
@@ -75,11 +74,10 @@ public:
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: registry(registry), tx(tx), rules(std::move(rules)), flags(flags), j(j)
{
XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
XRPL_ASSERT_IF(
(flags & ApplyFlags::Proposal) != ApplyFlags::None,
(flags & ApplyFlags::DryRun) != ApplyFlags::None,
(flags & TapProposal) != TapNone,
(flags & TapDryRun) != TapNone,
"xrpl::PreflightContext : proposal preflight implies dry run");
}
@@ -118,7 +116,7 @@ public:
, j(j)
{
XRPL_ASSERT(
parentBatchId.has_value() == ((flags & ApplyFlags::Batch) == ApplyFlags::Batch),
parentBatchId.has_value() == ((flags & TapBatch) == TapBatch),
"Parent Batch ID should be set if batch apply flag is set");
}
@@ -131,8 +129,7 @@ public:
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
: PreclaimContext(registry, view, preflightResult, tx, flags, std::nullopt, j)
{
XRPL_ASSERT(
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
}
PreclaimContext&
+1 -1
View File
@@ -41,7 +41,7 @@ struct ApplyResult
inline bool
isTecClaimHardFail(TER ter, ApplyFlags flags)
{
return isTecClaim(ter) && ((flags & ApplyFlags::Retry) == ApplyFlags::None);
return isTecClaim(ter) && ((flags & TapRetry) == 0u);
}
/**
+73 -73
View File
@@ -2,144 +2,144 @@ Detected OS: macos (Darwin arm64)
Core build tools:
✅ cmake
cmake version 4.4.3
/nix/store/q85csxf4s4shx89zif097h1ql4ax1rrz-cmake-4.4.3/bin/cmake
cmake version 4.1.2
/nix/store/gvabsb4yqb5xsqzqph54rijnn4zpihnp-cmake-4.1.2/bin/cmake
✅ conan
Conan version 2.32.0
/nix/store/921jqsgbilixmr3xchj95si0jl8pi13z-conan-2.32.0/bin/conan
Conan version 2.28.1
/nix/store/9jiyxmkpwmn6dcqs0765s83riw3l5ail-conan-2.28.1/bin/conan
✅ git
git version 2.55.0
/nix/store/gw7c7m0dwca5lg4152x9lpmxpj171bzw-git-2.55.0/bin/git
git version 2.54.0
/nix/store/a14yxcqvv9x2l9mllgpirzhvz93pgprg-git-2.54.0/bin/git
✅ python3
Python 3.14.7
/nix/store/0kcws5blwbdyxqxjd1w8hjipz61xny3k-python3-3.14.7/bin/python3.14
Python 3.13.13
/nix/store/ygxqin6ydzjfawywqpp5pal8wv6sf5bh-python3-3.13.13/bin/python3.13
Development tooling:
✅ ccache
ccache version 4.13.6
/nix/store/ydr5nlzxp1djb5256y0zzqa87vz55gsk-ccache-4.13.6/bin/ccache
/nix/store/57davyvs6p6dkrl3svzwg1ph18wsy4cz-ccache-4.13.6/bin/ccache
✅ clang
clang version 22.1.8
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang
clang version 22.1.7
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang
✅ clang-22
clang version 22.1.8
/nix/store/w826sqb98nnym6kzackg829664iqdlxj-clang-22/bin/clang-22
clang version 22.1.7
/nix/store/rbap7zqq7mw00fyqa02p5rj7gqjp4w5i-clang-22/bin/clang-22
✅ clang++
clang version 22.1.8
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang++
clang version 22.1.7
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang++
✅ clang++-22
clang version 22.1.8
/nix/store/wnwyw3yfqrhpxij65bl3pa1x65q2zg3m-clang++-22/bin/clang++-22
clang version 22.1.7
/nix/store/v9haf787f7bcz0mq1sad4bpyx21pj6li-clang++-22/bin/clang++-22
✅ ClangBuildAnalyzer
ClangBuildAnalyzer 1.6.0
/nix/store/c7jjnw29ra78jdlzjww2m374izhy05n8-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
/nix/store/4l50ds9fa2mkvh7wg8qzrlbmjs12sb8l-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
✅ curl
curl 8.22.0 (aarch64-apple-darwin25.6.0) libcurl/8.22.0 OpenSSL/3.5.8 zlib/1.3.2 libssh2/1.11.1 nghttp2/1.70.0 mit-krb5/1.22.2
/nix/store/pq1h17abqy6q5y6k5dn9qj0fwb89flba-curl-8.22.0-bin/bin/curl
curl 8.20.0 (aarch64-apple-darwin25.3.0) libcurl/8.20.0 OpenSSL/3.6.2 zlib/1.3.2 libssh2/1.11.1 nghttp2/1.69.0 mit-krb5/1.22.1
/nix/store/kclq0czaxvsgh4ym9ld7b6iwy50l1snk-curl-8.20.0-bin/bin/curl
✅ file
file-5.48
/nix/store/y44vpziwbzfm8020dzlqqbybyyy7lb6g-file-5.48/bin/file
file-5.47
/nix/store/dax63li7wwcbqxxkkgzc4g2rx7d4w86x-file-5.47/bin/file
✅ less
less 710 (PCRE2 regular expressions)
/nix/store/3yn3dx5fwg5i0inqqzwnxq7f6lwhpjqa-less-710/bin/less
less 692 (PCRE2 regular expressions)
/nix/store/lvr16y75r1pdxpdv0aph5ak2yd0hkvqm-less-692/bin/less
✅ make
GNU Make 4.4.1
/nix/store/8l4wgxmhdxfvc9g18y76bnxl1ghgcp7n-gnumake-4.4.1/bin/make
/nix/store/8wwiw8pwyhrkzyq28hqzxfl4z84lks81-gnumake-4.4.1/bin/make
✅ netstat
present
/nix/store/57fqvgdav8lrmzndkwm7qrgksl01yj23-network_cmds-741.100.2/bin/netstat
/nix/store/qsd1kzqb0ahrk433vmyl245gp623j19s-network_cmds-730.80.3/bin/netstat
✅ ninja
1.13.2
/nix/store/infm4vlx71q83bqz0k559w1gn4b0rq4i-ninja-1.13.2/bin/ninja
/nix/store/bqykhrblarkj4fl0hz2mf8ngwfv6x6bz-ninja-1.13.2/bin/ninja
✅ perl
v5.42.3
/nix/store/7rvr6qjfzk2ybzn1imjw6c6qq1n1a9cr-perl-5.42.3/bin/perl
v5.42.0
/nix/store/js13ri9fvm0ajk1fpd3acigys2a9whdv-perl-5.42.0/bin/perl
✅ pkg-config
0.29.2
/nix/store/nhgylvj98klqggy54h7gbc3b2ia4a00y-pkg-config-wrapper-0.29.2/bin/pkg-config
/nix/store/lzrwr375jqhhbca116kja96xf1md83l8-pkg-config-wrapper-0.29.2/bin/pkg-config
✅ vim
VIM - Vi IMproved 9.2 (2026 Feb 14, compiled Jan 01 1980 00:00:00)
/nix/store/akaa9kqm3b8rlkv61kvsqr2l2j70rvxb-vim-9.2.1001/bin/vim
/nix/store/6vbkykg92w603c0sw3mkk7p7mfaawbns-vim-9.2.0389/bin/vim
✅ zip
Zip 3.0
/nix/store/dvawr36npnad4xa6dxi2ss0fw44aa82r-zip-3.0/bin/zip
/nix/store/z6ph729vcakbvz3wh8ln1wk6mi06w487-zip-3.0/bin/zip
✅ clang-apply-replacements
clang-apply-replacements version 22.1.8
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-apply-replacements
clang-apply-replacements version 22.1.7
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-apply-replacements
✅ clang-apply-replacements-22
clang-apply-replacements version 22.1.8
/nix/store/w4bz034k4l0w719rrmbwnqcgyjbswy81-clang-apply-replacements-22/bin/clang-apply-replacements-22
clang-apply-replacements version 22.1.7
/nix/store/m3ii69rca4077lf4wlk7m3jcag1fs577-clang-apply-replacements-22/bin/clang-apply-replacements-22
✅ clang-format
clang-format version 22.1.8
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-format
clang-format version 22.1.7
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-format
✅ clang-format-22
clang-format version 22.1.8
/nix/store/w7z346l0r8y36b6lc5i7jzj3w0581l2y-clang-format-22/bin/clang-format-22
clang-format version 22.1.7
/nix/store/4fawqy6ngqcsqd2ygyyzm93q0xy3f5gs-clang-format-22/bin/clang-format-22
✅ clang-tidy
LLVM version 22.1.8
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-tidy
LLVM version 22.1.7
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-tidy
✅ clang-tidy-22
LLVM version 22.1.8
/nix/store/0lv25qd3ddgjm3ndn70q61jfr6krlkl7-clang-tidy-22/bin/clang-tidy-22
LLVM version 22.1.7
/nix/store/jqw4280saixaxxihwdba9ldm2fsm6dr3-clang-tidy-22/bin/clang-tidy-22
✅ dot
dot - graphviz version 15.1.1 (0)
/nix/store/mc99a6bpbk2waym2cnw6ifmxli4ndb4x-graphviz-15.1.1/bin/dot
dot - graphviz version 12.2.1 (0)
/nix/store/ijb4fbnqa6wzlpqnhb6q9knqpf7qqn5z-graphviz-12.2.1/bin/dot
✅ doxygen
1.17.0
/nix/store/kz43yaj0wg6fx6cbrcmi2442z1ramy0c-doxygen-1.17.0/bin/doxygen
1.16.1
/nix/store/kbryjdpq9jizjb0ws0nzbf2h2ymbdiwm-doxygen-1.16.1/bin/doxygen
✅ gcovr
gcovr 8.4
/nix/store/czj1vxsizrbba280gg22cys40qxkqjgr-python3.14-gcovr-8.4/bin/gcovr
/nix/store/wn8jiyh9p0bybs96s4163qp3k8vfmczx-python3.13-gcovr-8.4/bin/gcovr
✅ gh
gh version 2.102.0 (2026-09-30)
/nix/store/s8bahl6zssvry93ihivfmk1l6c58byrr-gh-2.102.0/bin/gh
gh version 2.94.0 (nixpkgs)
/nix/store/fhnpw0hs0gjms1ha6ap02jq7rx13gkbp-gh-2.94.0/bin/gh
✅ git-cliff
git-cliff 2.14.2
/nix/store/k4m5yk0qb7hfslwsq1zjajz1d94j12bj-git-cliff-2.14.2/bin/git-cliff
git-cliff 2.13.1
/nix/store/cy0wwhgxa7yvrz97zydbq6sqmixc90fq-git-cliff-2.13.1/bin/git-cliff
✅ git-lfs
git-lfs/3.8.0 (3.8.0; darwin arm64; go 1.26.8)
/nix/store/wzixjr3yb40a13m2q654hm3bdjn6sal3-git-lfs-3.8.0/bin/git-lfs
git-lfs/3.7.1 (3.7.1; darwin arm64; go 1.26.3)
/nix/store/k9r7zjfjplqa4d5s71cqvf2iv73jd9mc-git-lfs-3.7.1/bin/git-lfs
✅ gpg
gpg (GnuPG) 2.4.9
/nix/store/0ldfwnwx385i5kh7cyvxx4mkvgjhs5rh-gnupg-2.4.9/bin/gpg
/nix/store/cgh6iwzz5jgx9z5whka4vgj210i6npc6-gnupg-2.4.9/bin/gpg
✅ pre-commit
pre-commit 4.6.2
/nix/store/ivh54xypg9qvd3ha9lis0i8p4nkf8n7a-pre-commit-4.6.2/bin/pre-commit
pre-commit 4.5.1
/nix/store/z3cca68620w0w10f090szgzdnmh1waf2-pre-commit-4.5.1/bin/pre-commit
✅ run-clang-tidy
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
/nix/store/3xss1mm3mh1hzcmg182na6ki1p5rnxf1-run-clang-tidy/bin/run-clang-tidy
/nix/store/4x28x911z2f9y7adqlh3qspp4a16dig7-run-clang-tidy/bin/run-clang-tidy
✅ run-clang-tidy-22
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
/nix/store/w68r9z07hcq4fwbfyq3yvc7qxm8aqfbl-run-clang-tidy-22/bin/run-clang-tidy-22
/nix/store/x8iymrh76sk5q91ryg5pa7i32s6gfh34-run-clang-tidy-22/bin/run-clang-tidy-22
Rust toolchain:
✅ cargo
cargo 1.97.1 (c980f4866 2026-06-30)
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/cargo
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/cargo
✅ cargo-audit
cargo-audit-audit 0.22.2
/nix/store/7jgrwj557l91940aj0ag4i924ima1cqn-cargo-audit-0.22.2/bin/cargo-audit
cargo-audit-audit 0.22.1
/nix/store/snwkga2f5gyf404h7mmp9wriwxb8v65f-cargo-audit-0.22.1/bin/cargo-audit
✅ cargo-llvm-cov
cargo-llvm-cov 0.9.0
/nix/store/5q1h285z8zzkw2qxig9a0b1avcsk1cf0-cargo-llvm-cov-0.9.0/bin/cargo-llvm-cov
cargo-llvm-cov 0.8.5
/nix/store/fpiqdh91gwyxalqp409ynm0s0g086w7w-cargo-llvm-cov-0.8.5/bin/cargo-llvm-cov
✅ cargo-nextest
cargo-nextest 0.9.146
/nix/store/69v3hx3k597hcxd2zy7yad7fg0aw5yxd-cargo-nextest-0.9.146/bin/cargo-nextest
cargo-nextest 0.9.137
/nix/store/ylz7m947mhkgsp6i7611id3s3gcd58nq-cargo-nextest-0.9.137/bin/cargo-nextest
✅ clippy-driver
clippy 0.1.97 (8bab26f4f6 2026-07-14)
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/clippy-driver
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/clippy-driver
✅ rust-analyzer
rust-analyzer 1.97.1 (8bab26f4 2026-07-14)
/nix/store/30rbyk8gx7v53fxqhs4pp0fvxfjycspr-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
/nix/store/j6apc5pmd0giy15da9p650r8zklslmvi-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
✅ rust-nightly
rustc 1.101.0-nightly (282215592 2026-10-04)
/nix/store/a9552rf3pylcnkijc1z0hf1lcnv50djq-rust-nightly/bin/rust-nightly
rustc 1.99.0-nightly (87e5904f5 2026-07-20)
/nix/store/fqpjz4l0nsnji8b2pz57mnj0akbp6hcl-rust-nightly/bin/rust-nightly
✅ rustc
rustc 1.97.1 (8bab26f4f 2026-07-14)
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/rustc
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/rustc
✅ rustfmt
rustfmt 1.9.0-stable (8bab26f4f6 2026-07-14)
/nix/store/0jmdipyn8266j9kqx2xxm2qkxlrlrjfc-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
/nix/store/5ymwgr9jqjz7zzbmj0j5vqbwcd3kp0vm-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
+1 -2
View File
@@ -21,8 +21,7 @@ let
)
);
toolchain =
if pkgs.stdenv.hostPlatform.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
toolchain = if pkgs.stdenv.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
in
{
default = pkgs.buildEnv {
+6 -7
View File
@@ -20,16 +20,15 @@ let
# Custom-glibc stdenvs, matching the CI environment. darwin has no custom
# glibc, so there they fall back to the plain nixpkgs stdenvs.
customGccStdenv = if pkgs.stdenv.hostPlatform.isLinux then linux.gccStdenv else plainGccStdenv;
customClangStdenv =
if pkgs.stdenv.hostPlatform.isLinux then linux.clangStdenv else plainClangStdenv;
customGccStdenv = if pkgs.stdenv.isLinux then linux.gccStdenv else plainGccStdenv;
customClangStdenv = if pkgs.stdenv.isLinux then linux.clangStdenv else plainClangStdenv;
# gcov matching each gcc shell, so `-Dcoverage=ON` builds work in the shell.
plainGcov = mkGcov {
name = "plain";
cc = gccPackage.cc;
};
customGccGcov = if pkgs.stdenv.hostPlatform.isLinux then linux.gcov else plainGcov;
customGccGcov = if pkgs.stdenv.isLinux then linux.gcov else plainGcov;
# Whole directory: init.sh locates the profiles relative to itself.
conanDir = ../conan;
@@ -52,7 +51,7 @@ let
# Not sdkEnv: a shell's stdenv already sets that up. Prepended so the stub
# beats the nixpkgs libresolv this shell's tooling drags in.
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin (
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.isDarwin (
pkgs.lib.concatLines (
pkgs.lib.mapAttrsToList (
name: value: ''export ${name}="${value} ''${${name}:-}"''
@@ -127,7 +126,7 @@ let
in
rec {
# macOS: Nix Clang. Linux: Nix GCC.
default = if pkgs.stdenv.hostPlatform.isDarwin then clang else gcc;
default = if pkgs.stdenv.isDarwin then clang else gcc;
# gcc/clang use the custom-glibc toolchain, matching CI. On darwin there is no
# custom glibc, so they fall back to the plain nixpkgs toolchain.
@@ -172,7 +171,7 @@ rec {
# The *-plain shells (stock nixpkgs toolchain) exist only on Linux: on darwin
# gcc/clang are already plain, so these would be redundant and are omitted, which
# makes `nix develop .#gcc-plain` fail there rather than silently aliasing gcc.
// pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
// pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
gcc-plain = makeShell {
shellName = "gcc-plain";
stdenv = plainGccStdenv;
+39
View File
@@ -0,0 +1,39 @@
#include <xrpl/basics/CountedObject.h>
#include <algorithm>
namespace xrpl {
CountedObjects&
CountedObjects::getInstance() noexcept
{
static CountedObjects kInstance;
return kInstance;
}
CountedObjects::CountedObjects() noexcept : count_(0), head_(nullptr)
{
}
CountedObjects::List
CountedObjects::getCounts(int minimumThreshold) const
{
List counts;
// When other operations are concurrent, the count
// might be temporarily less than the actual count.
counts.reserve(count_.load());
for (auto* ctr = head_.load(); ctr != nullptr; ctr = ctr->getNext())
{
if (ctr->getCount() >= minimumThreshold)
counts.emplace_back(ctr->getName(), ctr->getCount());
}
std::ranges::sort(counts);
return counts;
}
} // namespace xrpl
+1 -2
View File
@@ -11,7 +11,6 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/LedgerTiming.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
#include <xrpl/nodestore/NodeObject.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Fees.h>
@@ -561,7 +560,7 @@ Ledger::setup()
try
{
if (auto const sle = FeeSettingsEntryR(*this))
if (auto const sle = read(keylet::feeSettings()))
{
bool oldFees = false;
bool newFees = false;
+10 -8
View File
@@ -9,7 +9,6 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/LedgerHashesEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -362,16 +361,17 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
if (int const diff = ledger.seq() - seq; diff <= 256)
{
// Within 256...
LedgerHashesEntryR const hashIndex(ledger, journal);
auto const hashIndex = ledger.read(keylet::skip());
if (hashIndex)
{
XRPL_ASSERT(
hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.seq() - 1),
"xrpl::hashOfSeq : matching ledger sequence");
if (auto const hash = hashIndex.hashAt(diff - 1))
return hash;
STVector256 vec = hashIndex->getFieldV256(sfHashes);
if (vec.size() >= diff)
return vec[vec.size() - diff];
JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " ("
<< hashIndex->getFieldV256(sfHashes).size() << "," << diff << ")";
<< vec.size() << "," << diff << ")";
}
else
{
@@ -387,14 +387,16 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
}
// in skiplist
LedgerHashesEntryR const hashIndex(keylet::skip(seq), ledger, journal);
auto const hashIndex = ledger.read(keylet::skip(seq));
if (hashIndex)
{
auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
XRPL_ASSERT(lastSeq >= seq, "xrpl::hashOfSeq : minimum last ledger");
XRPL_ASSERT((lastSeq & 0xff) == 0, "xrpl::hashOfSeq : valid last ledger");
if (auto const hash = hashIndex.hashAt((lastSeq - seq) >> 8))
return hash;
auto const diff = (lastSeq - seq) >> 8;
STVector256 vec = hashIndex->getFieldV256(sfHashes);
if (vec.size() > diff)
return vec[vec.size() - diff - 1];
}
JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " error";
return std::nullopt;
@@ -0,0 +1,78 @@
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstdint>
namespace xrpl {
template <typename ViewT>
bool
MPTokenEntry<ViewT>::isFrozen(AccountID const& account, std::uint8_t depth) const
{
auto const& view = this->readView();
auto const& sle = **this;
XRPL_ASSERT(sle[sfAccount] == account, "xrpl::MPTokenEntry::isFrozen : valid MPToken holder");
MPTID const mptID = sle[sfMPTokenIssuanceID];
auto const issuanceSle = view.read(keylet::mptokenIssuance(mptID));
if ((issuanceSle && isGlobalFrozen(*issuanceSle)) || sle.isFlag(lsfMPTLocked))
return true;
if (issuanceSle)
return isVaultPseudoAccountFrozen(view, account, *issuanceSle, depth);
return isVaultPseudoAccountFrozen(view, account, MPTIssue{mptID}, depth);
}
template <typename ViewT>
TER
MPTokenEntry<ViewT>::create(
ApplyView& view,
MPTID const& mptIssuanceID,
AccountID const& account,
SLE::Ref sponsorSle,
std::uint32_t flags)
requires Base::kIsWritable
{
MPTokenEntry mptoken(mptIssuanceID, account, view);
auto const ownerNode =
view.dirInsert(keylet::ownerDir(account), mptoken.keylet(), describeOwnerDir(account));
if (!ownerNode)
return tecDIR_FULL; // LCOV_EXCL_LINE
mptoken.newSLE();
(*mptoken)[sfAccount] = account;
(*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
(*mptoken)[sfFlags] = flags;
(*mptoken)[sfOwnerNode] = *ownerNode;
addSponsorToLedgerEntry(mptoken.mutableRawSle(), sponsorSle);
mptoken.insert();
return tesSUCCESS;
}
template class MPTokenEntry<ReadView>;
template class MPTokenEntry<ApplyView>;
} // namespace xrpl
+5 -4
View File
@@ -11,7 +11,7 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AMMCore.h>
@@ -633,8 +633,8 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const
// Get the actual AMM balance without factoring in the balance hook
return asset.visit(
[&](MPTIssue const& issue) {
if (auto const sle = view.read(keylet::mptoken(issue, ammAccountID));
sle && !isFrozen(view, ammAccountID, *sle))
if (auto const sle = MPTokenEntryR(issue, ammAccountID, view);
sle && !sle.isFrozen(ammAccountID))
return STAmount{issue, (*sle)[sfMPTAmount]};
return STAmount{asset};
},
@@ -725,7 +725,8 @@ deleteAMMMPTokens(Sandbox& sb, AccountID const& ammAccountID, beast::Journal j)
// LCOV_EXCL_STOP
}
return {deleteAMMMPToken(sb, sleItem, ammAccountID, j), SkipEntry::No};
MPTokenEntryW sleMpt(sleItem->key(), sb, j);
return {deleteAMMMPToken(sb, sleMpt, ammAccountID, j), SkipEntry::No};
}
if (nodeType == ltRIPPLE_STATE)
{
+35 -85
View File
@@ -7,9 +7,9 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -58,19 +58,11 @@ isGlobalFrozen(SLE const& issuanceSle)
bool
isIndividualFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
{
if (auto const sle = view.read(keylet::mptoken(mptIssue.getMptID(), account)))
return isIndividualFrozen(*sle);
if (auto const sle = MPTokenEntryR(mptIssue.getMptID(), account, view))
return sle.isIndividualFrozen();
return false;
}
bool
isIndividualFrozen(SLE const& mptSle)
{
XRPL_ASSERT(mptSle.getType() == ltMPTOKEN, "xrpl::isIndividualFrozen : MPToken SLE");
return mptSle.isFlag(lsfMPTLocked);
}
bool
isFrozen(
ReadView const& view,
@@ -91,18 +83,9 @@ isFrozen(ReadView const& view, AccountID const& account, SLE const& sle, std::ui
if (sle.getType() == ltMPTOKEN)
{
XRPL_ASSERT(sle[sfAccount] == account, "xrpl::isFrozen : valid MPToken holder");
MPTID const mptID = sle[sfMPTokenIssuanceID];
auto const issuanceSle = view.read(keylet::mptokenIssuance(mptID));
if ((issuanceSle && isGlobalFrozen(*issuanceSle)) || isIndividualFrozen(sle))
return true;
if (issuanceSle)
return isVaultPseudoAccountFrozen(view, account, *issuanceSle, depth);
return isVaultPseudoAccountFrozen(view, account, MPTIssue{mptID}, depth);
// The entry needs an owning pointer, so this copies the SLE. Callers
// that already hold an MPTokenEntry call its isFrozen() directly.
return MPTokenEntryR(std::make_shared<SLE const>(sle), view).isFrozen(account, depth);
}
MPTIssue const mptIssue{sle[sfSequence], sle[sfIssuer]};
@@ -188,7 +171,7 @@ addEmptyHolding(
// still rejected before the "MPToken already exists" short circuit.
if (mpt->isFlag(lsfMPTLocked))
return tefINTERNAL; // LCOV_EXCL_LINE
if (ctx.view.peek(keylet::mptoken(mptID, accountID)))
if (MPTokenEntryW(mptID, accountID, ctx.view))
return tecDUPLICATE;
if (accountID == mptIssue.getIssuer())
return tesSUCCESS;
@@ -220,8 +203,7 @@ authorizeMPToken(
// - delete the MPToken
if ((flags & tfMPTUnauthorize) != 0u)
{
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
auto const sleMpt = ctx.view.peek(mptokenKey);
MPTokenEntryW sleMpt(mptIssuanceID, account, ctx.view, journal);
if (!sleMpt || (*sleMpt)[sfMPTAmount] != 0 ||
(ctx.view.rules().enabled(fixCleanup3_1_3) &&
(*sleMpt)[~sfLockedAmount].valueOr(0) != 0))
@@ -231,9 +213,9 @@ authorizeMPToken(
keylet::ownerDir(account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false))
return tecINTERNAL; // LCOV_EXCL_LINE
decreaseOwnerCountForObject(ctx.view, sleAcct, sleMpt, 1, journal);
decreaseOwnerCountForObject(ctx.view, sleAcct, sleMpt.mutableRawSle(), 1, journal);
ctx.view.erase(sleMpt);
sleMpt.erase();
return tesSUCCESS;
}
@@ -274,19 +256,21 @@ authorizeMPToken(
// LCOV_EXCL_STOP
}
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
auto mptoken = std::make_shared<SLE>(mptokenKey);
if (auto ter = dirLink(ctx.view, account, mptoken))
MPTokenEntryW mptoken(mptIssuanceID, account, ctx.view, journal);
mptoken.newSLE();
// dirLink takes a non-const SLE::pointer&, so pass a copy of the pointer.
auto mptokenSle = mptoken.mutableRawSle();
if (auto ter = dirLink(ctx.view, account, mptokenSle))
return ter; // LCOV_EXCL_LINE
(*mptoken)[sfAccount] = account;
(*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
(*mptoken)[sfFlags] = 0;
ctx.view.insert(mptoken);
mptoken.insert();
// Update owner count.
increaseOwnerCount(ctx.view, sleAcct, sponsorSle, 1, journal);
addSponsorToLedgerEntry(mptoken, sponsorSle);
addSponsorToLedgerEntry(mptoken.mutableRawSle(), sponsorSle);
return tesSUCCESS;
}
@@ -301,7 +285,7 @@ authorizeMPToken(
if (account != (*sleMptIssuance)[sfIssuer])
return tecINTERNAL; // LCOV_EXCL_LINE
auto const sleMpt = ctx.view.peek(keylet::mptoken(mptIssuanceID, *holderID));
MPTokenEntryW sleMpt(mptIssuanceID, *holderID, ctx.view, journal);
if (!sleMpt)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -324,7 +308,7 @@ authorizeMPToken(
if (flagsIn != flagsOut)
sleMpt->setFieldU32(sfFlags, flagsOut);
ctx.view.update(sleMpt);
sleMpt.update();
return tesSUCCESS;
}
@@ -340,7 +324,7 @@ removeEmptyHolding(
// a token does exist, it will get deleted. If not, return success.
bool const accountIsIssuer = accountID == mptIssue.getIssuer();
auto const& mptID = mptIssue.getMptID();
auto const mptoken = ctx.view.peek(keylet::mptoken(mptID, accountID));
MPTokenEntryW mptoken(mptID, accountID, ctx.view, journal);
if (!mptoken)
return accountIsIssuer ? (TER)tesSUCCESS : (TER)tecOBJECT_NOT_FOUND;
// Unlike a trust line, if the account is the issuer, and the token has a
@@ -436,8 +420,7 @@ requireAuth(
}
}
auto const mptokenID = keylet::mptoken(mptID.key, account);
auto const sleToken = view.read(mptokenID);
MPTokenEntryR const sleToken(mptID.key, account, view);
// if account has no MPToken, fail
if (!sleToken && (authType == AuthType::StrongAuth || authType == AuthType::Legacy))
@@ -470,8 +453,7 @@ requireAuth(
return tesSUCCESS;
// mptoken must be authorized if issuance enabled requireAuth
if (sleIssuance->isFlag(lsfMPTRequireAuth) &&
(!sleToken || !sleToken->isFlag(lsfMPTAuthorized)))
if (sleIssuance->isFlag(lsfMPTRequireAuth) && (!sleToken || !sleToken.isAuthorized()))
return tecNO_AUTH;
return tesSUCCESS; // Note: sleToken might be null
@@ -496,8 +478,7 @@ enforceMPTokenAuthorization(
if (account == sleIssuance->at(sfIssuer))
return tefINTERNAL; // LCOV_EXCL_LINE
auto const keylet = keylet::mptoken(mptIssuanceID, account);
auto const sleToken = ctx.view.read(keylet); // NOTE: might be null
MPTokenEntryR const sleToken(mptIssuanceID, account, ctx.view, j); // NOTE: might not exist
auto const maybeDomainID = sleIssuance->at(~sfDomainID);
bool expired = false;
bool const authorizedByDomain = [&]() -> bool {
@@ -513,7 +494,7 @@ enforceMPTokenAuthorization(
return false;
}();
if (!authorizedByDomain && sleToken == nullptr)
if (!authorizedByDomain && !sleToken)
{
// Could not find MPToken and won't create one, could be either of:
//
@@ -536,14 +517,14 @@ enforceMPTokenAuthorization(
// We found an MPToken, but sfDomainID is not set, so this is a classic
// MPToken which requires authorization by the token issuer.
XRPL_ASSERT(
sleToken != nullptr && !maybeDomainID.has_value(),
sleToken.exists() && !maybeDomainID.has_value(),
"xrpl::enforceMPTokenAuthorization : found MPToken");
if (sleToken->isFlag(lsfMPTAuthorized))
return tesSUCCESS;
return tecNO_AUTH;
}
if (authorizedByDomain && sleToken != nullptr)
if (authorizedByDomain && sleToken.exists())
{
// Found an MPToken, authorized by the domain. Ignore authorization flag
// lsfMPTAuthorized because it is meaningless. Return tesSUCCESS
@@ -557,7 +538,7 @@ enforceMPTokenAuthorization(
// Could not find MPToken but there should be one because we are
// authorized by domain. Proceed to create it, then return tesSUCCESS
XRPL_ASSERT(
maybeDomainID.has_value() && sleToken == nullptr,
maybeDomainID.has_value() && !sleToken.exists(),
"xrpl::enforceMPTokenAuthorization : new MPToken for domain");
if (auto const err = authorizeMPToken(
ctx,
@@ -734,8 +715,7 @@ lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount,
// 1. Decrease the MPT Holder MPTAmount
// 2. Increase the MPT Holder EscrowedAmount
{
auto const mptokenID = keylet::mptoken(mptID.key, sender);
auto sle = view.peek(mptokenID);
MPTokenEntryW sle(mptID.key, sender, view, j);
if (!sle)
{ // LCOV_EXCL_START
JLOG(j.error()) << "lockEscrowMPT: MPToken not found for " << sender;
@@ -774,7 +754,7 @@ lockEscrowMPT(ApplyView& view, AccountID const& sender, STAmount const& amount,
sle->setFieldU64(sfLockedAmount, pay);
}
view.update(sle);
sle.update();
}
// 1. Increase the Issuance EscrowedAmount
@@ -865,8 +845,7 @@ unlockEscrowMPT(
if (issuer != receiver)
{
// Increase the MPT Holder MPTAmount
auto const mptokenID = keylet::mptoken(mptID.key, receiver);
auto sle = view.peek(mptokenID);
MPTokenEntryW sle(mptID.key, receiver, view, j);
if (!sle)
{ // LCOV_EXCL_START
JLOG(j.error()) << "unlockEscrowMPT: MPToken not found for " << receiver;
@@ -885,7 +864,7 @@ unlockEscrowMPT(
} // LCOV_EXCL_STOP
(*sle)[sfMPTAmount] += delta;
view.update(sle);
sle.update();
}
else
{
@@ -912,8 +891,7 @@ unlockEscrowMPT(
return tecINTERNAL;
} // LCOV_EXCL_STOP
// Decrease the MPT Holder EscrowedAmount
auto const mptokenID = keylet::mptoken(mptID.key, sender);
auto sle = view.peek(mptokenID);
MPTokenEntryW sle(mptID.key, sender, view, j);
if (!sle)
{ // LCOV_EXCL_START
JLOG(j.error()) << "unlockEscrowMPT: MPToken not found for " << sender;
@@ -946,7 +924,7 @@ unlockEscrowMPT(
{
sle->setFieldU64(sfLockedAmount, newLocked);
}
view.update(sle);
sle.update();
// Note: The gross amount is the amount that was locked, the net
// amount is the amount that is being unlocked. The difference is the fee
@@ -970,35 +948,6 @@ unlockEscrowMPT(
return tesSUCCESS;
}
TER
createMPToken(
ApplyView& view,
MPTID const& mptIssuanceID,
AccountID const& account,
SLE::Ref sponsorSle,
std::uint32_t const flags)
{
auto const mptokenKey = keylet::mptoken(mptIssuanceID, account);
auto const ownerNode =
view.dirInsert(keylet::ownerDir(account), mptokenKey, describeOwnerDir(account));
if (!ownerNode)
return tecDIR_FULL; // LCOV_EXCL_LINE
auto mptoken = std::make_shared<SLE>(mptokenKey);
(*mptoken)[sfAccount] = account;
(*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID;
(*mptoken)[sfFlags] = flags;
(*mptoken)[sfOwnerNode] = *ownerNode;
addSponsorToLedgerEntry(mptoken, sponsorSle);
view.insert(mptoken);
return tesSUCCESS;
}
TER
checkCreateMPT(
xrpl::ApplyView& view,
@@ -1015,7 +964,8 @@ checkCreateMPT(
auto const mptokenID = keylet::mptoken(mptIssuanceID.key, holder);
if (!view.exists(mptokenID))
{
if (auto const err = createMPToken(view, mptIssue.getMptID(), holder, sponsorSle, flags);
if (auto const err =
MPTokenEntryW::create(view, mptIssue.getMptID(), holder, sponsorSle, flags);
!isTesSuccess(err))
{
return err;
@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -846,7 +847,7 @@ deleteAMMTrustLine(
TER
deleteAMMMPToken(
ApplyView& view,
SLE::pointer sleMpt,
MPTokenEntryW& sleMpt,
AccountID const& ammAccountID,
beast::Journal j)
{
@@ -854,7 +855,7 @@ deleteAMMMPToken(
keylet::ownerDir(ammAccountID), (*sleMpt)[sfOwnerNode], sleMpt->key(), false))
return tefBAD_LEDGER; // LCOV_EXCL_LINE
view.erase(sleMpt);
sleMpt.erase();
return tesSUCCESS;
}
+7 -9
View File
@@ -7,6 +7,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -436,10 +437,9 @@ accountHolds(
return view.balanceHookMPT(issuer, mptIssue, available);
}
auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account));
MPTokenEntryR const sleMpt(mptIssue.getMptID(), account, view, j);
if (!sleMpt ||
(zeroIfFrozen == FreezeHandling::ZeroIfFrozen && isFrozen(view, account, *sleMpt)))
if (!sleMpt || (zeroIfFrozen == FreezeHandling::ZeroIfFrozen && sleMpt.isFrozen(account)))
{
amount.clear(mptIssue);
}
@@ -1238,15 +1238,14 @@ directSendNoFeeMPT(
}
else
{
auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
if (auto sle = view.peek(mptokenID))
if (auto sle = MPTokenEntryW(mptID.key, uSenderID, view, j))
{
auto const senderBalance = sle->getFieldU64(sfMPTAmount);
if (senderBalance < amt)
return tecINSUFFICIENT_FUNDS;
view.creditHookMPT(uSenderID, uReceiverID, saAmount, (*sle)[sfMPTAmount], available);
(*sle)[sfMPTAmount] = senderBalance - amt;
view.update(sle);
sle.update();
}
else
{
@@ -1268,8 +1267,7 @@ directSendNoFeeMPT(
}
else
{
auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
if (auto sle = view.peek(mptokenID))
if (auto sle = MPTokenEntryW(mptID.key, uReceiverID, view, j))
{
if (view.rules().enabled(featureMPTokensV2))
{
@@ -1280,7 +1278,7 @@ directSendNoFeeMPT(
}
view.creditHookMPT(uSenderID, uReceiverID, saAmount, (*sle)[sfMPTAmount], available);
(*sle)[sfMPTAmount] += amt;
view.update(sle);
sle.update();
}
else
{
+2 -1
View File
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
@@ -210,7 +211,7 @@ isSoleShareholder(ReadView const& view, AccountID const& account, SLE::ConstRef
auto const shareMPTID =
makeMptID(issuance->getFieldU32(sfSequence), issuance->getAccountID(sfIssuer));
auto const sleToken = view.read(keylet::mptoken(shareMPTID, account));
MPTokenEntryR const sleToken(shareMPTID, account, view);
if (!sleToken)
return false; // LCOV_EXCL_LINE
@@ -317,8 +317,8 @@ public:
}
else
{
status = checkedCast<Status>(
safeCast<int>(Status::CustomCode) + safeCast<int>(getStatus.code()));
status = static_cast<Status>(
static_cast<int>(Status::CustomCode) + unsafeCast<int>(getStatus.code()));
JLOG(journal.error()) << getStatus.ToString();
}
+61 -114
View File
@@ -8,8 +8,6 @@
#include <xrpl/protocol/digest.h>
#include <xrpl/protocol/tokens.h>
#include <algorithm>
#include <array>
#include <atomic>
#include <cstdint>
#include <cstring>
@@ -21,136 +19,85 @@
namespace xrpl {
namespace {
namespace detail {
/** If true, we implement a cache for account ID lookups.
This is generally a good idea and can improve performance, especially
when serving client requests.
The cache may generate warnings under some sanitizers; if you want to
disable caching, set this variable to false.
/**
* Caches the base58 representations of AccountIDs
*/
constexpr bool cacheAccountIDs = true;
/** Represents a cached entry for the given AccountID.
We keep the size and alignment of the structure at 64 bytes to
make sure it fits within a cache line and avoid false sharing.
*/
struct alignas(64) CachedAccountID
class AccountIdCache
{
/** The sequence lock for the entry.
private:
struct CachedAccountID
{
AccountID id;
char encoding[40] = {0};
};
An even value means that the data is stable, whereas an odd
value indicates the data is being actively modified.
// The actual cache
std::vector<CachedAccountID> cache_;
We use a 32-bit value to ensure that the counter will never
return to an observed value during a read window.
*/
std::atomic<std::uint32_t> seq{0};
// We use a hash function designed to resist algorithmic complexity attacks
HardenedHash<> hasher_;
/** The account id. */
AccountID id{};
// 64 spinlocks, packed into a single 64-bit value
std::atomic<std::uint64_t> locks_ = 0;
/** The length of the cached encoding.
public:
AccountIdCache(std::size_t count) : cache_(count)
{
// This is non-binding, but we try to avoid wasting memory that
// is caused by overallocation.
cache_.shrink_to_fit();
}
This field disambiguates collisions between ACCOUNT_ZERO and
never-written-to slots, since they would otherwise share the
same encoded bits.
*/
std::uint8_t len = 0;
std::string
toBase58(AccountID const& id)
{
auto const index = hasher_(id) % cache_.size();
/** The actual string encoding. */
char encoding[39] = {0};
PackedSpinlock sl(locks_, index % 64);
{
std::scoped_lock const lock(sl);
// The check against the first character of the encoding ensures
// that we don't mishandle the case of the all-zero account:
if (cache_[index].encoding[0] != 0 && cache_[index].id == id)
return cache_[index].encoding;
}
auto ret = encodeBase58Token(TokenType::AccountID, id.data(), id.size());
XRPL_ASSERT(ret.size() <= 38, "xrpl::detail::AccountIdCache : maximum result size");
{
std::scoped_lock const lock(sl);
cache_[index].id = id;
std::strcpy(cache_[index].encoding, ret.c_str());
}
return ret;
}
};
static_assert(sizeof(CachedAccountID) == 64 && alignof(CachedAccountID) == 64);
} // namespace detail
// The actual cache to use: a simple, direct-mapped, best-effort construct.
//
// We do not use a hardened hash for this because an AccountID is already
// just cryptographically generated (i.e. uniformly distributed) bits and
// there is no amplification, since collisions do not carry an additional
// cost.
//
// The size is a "nice" power of 2 because it makes the calculation of an
// index easier.
constinit std::array<CachedAccountID, 65536> gAccountIdCache{};
static std::unique_ptr<detail::AccountIdCache> gAccountIdCache;
} // namespace
void
initAccountIdCache(std::size_t count)
{
if (!gAccountIdCache && count != 0)
gAccountIdCache = std::make_unique<detail::AccountIdCache>(count);
}
std::string
toBase58(AccountID const& v)
{
// Since the input bits are generated by a cryptographically secure
// hash construct, we can just pick any subset of them, no muss, no
// fuss.
auto const index = [&v]() -> std::uint16_t {
auto const data = v.data();
if (gAccountIdCache)
return gAccountIdCache->toBase58(v);
return static_cast<std::uint16_t>(data[0]) | (static_cast<std::uint16_t>(data[1]) << 8);
}();
if constexpr (cacheAccountIDs)
{
auto& slot = gAccountIdCache[index];
// This is the optimistic, store-free read path: a collision with a
// concurrent writer will be detected because the seq recheck fails
// and we fall through to the slow path.
//
// These unsynchronized reads of the slot fields are, formally, data
// races and, technically speaking, result in undefined behavior and
// a tool like TSAN might flag them. These should not have an impact
// on the correctness of the returned data: the sequence lock checks
// ensure we discard any data that was in flux.
if (auto const s0 = slot.seq.load(std::memory_order::acquire);
(s0 & 1) == 0 && slot.len != 0 && slot.id == v)
{
char buf[38];
auto const len = std::min<std::size_t>(slot.len, sizeof(buf));
std::memcpy(buf, slot.encoding, len);
// Prevents the loads above from sinking below the recheck; the
// acquire on s0 constrains later operations, not earlier ones.
std::atomic_thread_fence(std::memory_order::acquire);
// Everything read from the slot — the gates above and the bytes in
// buf — precedes this recheck and is validated by it. No slot access
// may follow it.
if (slot.seq.load(std::memory_order::relaxed) == s0)
return std::string(buf, len);
}
}
auto ret = encodeBase58Token(TokenType::AccountID, v.data(), v.size());
XRPL_ASSERT(!ret.empty() && ret.size() <= 38, "xrpl::toBase58 : maximum result size");
if constexpr (cacheAccountIDs)
{
auto& slot = gAccountIdCache[index];
// The CAS to an odd value excludes other writers; if it fails, another
// thread wrote or is writing into this slot. That is not a problem: we
// just don't store our result into the cache this time.
if (auto s0 = slot.seq.load(std::memory_order::relaxed); (s0 & 1) == 0 &&
slot.seq.compare_exchange_strong(s0, s0 + 1, std::memory_order::acquire))
{
slot.id = v;
slot.len = static_cast<std::uint8_t>(ret.size());
std::copy_n(ret.data(), ret.size(), slot.encoding);
// The acquire on the CAS above keeps the writes from becoming visible
// before the seqlock transitions to an odd value, which means readers
// can detect an in-progress write. This release publishes them before
// the seqlock transitions to an even value.
slot.seq.store(s0 + 2, std::memory_order::release);
}
}
return ret;
return encodeBase58Token(TokenType::AccountID, v.data(), v.size());
}
template <>
+45 -73
View File
@@ -5,7 +5,6 @@
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/hash/uhash.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/json/json_value.h>
@@ -18,13 +17,9 @@
#include <xrpl/protocol/jss.h>
#include <algorithm>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
@@ -41,10 +36,10 @@ STPathElement::getHash(STPathElement const& element)
// important. We don't even really need to fully hash the whole
// base_uint here, as a few bytes would do for our use.
//
// The note above is only true because the result of this function is only
// used by the STPathElement equality operator as a fast-reject prefilter,
// ahead of the field comparisons that then decide the answer. Do not use
// it to key a container.
// The note above is only true because the result of this function reaches
// nothing but STPathElement::operator==, where it is a fast-reject
// prefilter ahead of the field comparisons that decide the answer. Do not
// use it to key a container.
for (auto const x : element.getAccountID())
hashAccount += (hashAccount * 257) ^ x;
@@ -111,38 +106,12 @@ STPathSet::operator=(STPathSet const& other)
STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
{
using enum STPathElement::Type;
auto parsePathElementType = [](std::underlying_type_t<STPathElement::Type> byte) noexcept
-> std::optional<STPathElement::Type> {
if (byte == std::to_underlying(TypeBoundary))
return TypeBoundary;
constexpr auto knownBits = static_cast<std::underlying_type_t<STPathElement::Type>>(
std::to_underlying(TypeAccount) | std::to_underlying(TypeCurrency) |
std::to_underlying(TypeIssuer) | std::to_underlying(TypeMpt));
if ((byte & ~knownBits) != 0)
return std::nullopt;
return static_cast<STPathElement::Type>(byte);
};
std::vector<STPathElement> path;
std::optional<STPathElement::Type> type;
do
for (;;)
{
type = parsePathElementType(sit.get8());
int const iType = sit.get8();
if (!type)
{
JLOG(debugLog().error()) << "Bad path element in pathset";
Throw<std::runtime_error>("bad path element");
}
if (type == TypeNone || type == TypeBoundary)
if (iType == STPathElement::TypeNone || iType == STPathElement::TypeBoundary)
{
if (path.empty())
{
@@ -153,11 +122,28 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
// Move rather than converting the vector to an STPath by copy.
value_.emplace_back(std::move(path));
path.clear();
if (iType == STPathElement::TypeNone)
return;
}
else if ((iType & ~STPathElement::TypeAll) != 0)
{
JLOG(debugLog().error()) << "Bad path element " << iType << " in pathset";
Throw<std::runtime_error>("bad path element");
}
else
{
bool const hasCurrency = ((*type & TypeCurrency) == TypeCurrency);
bool const hasMPT = ((*type & TypeMpt) == TypeMpt);
auto const hasAccount = (iType & STPathElement::TypeAccount) != 0u;
auto const hasCurrency = (iType & STPathElement::TypeCurrency) != 0u;
auto const hasIssuer = (iType & STPathElement::TypeIssuer) != 0u;
auto const hasMPT = (iType & STPathElement::TypeMpt) != 0u;
AccountID account;
PathAsset asset;
AccountID issuer;
if (hasAccount)
account = sit.get160();
if (hasCurrency && hasMPT)
{
@@ -165,26 +151,18 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
Throw<std::runtime_error>("bad path element: MPT and Currency");
}
AccountID account;
AccountID issuer;
if ((*type & TypeAccount) == TypeAccount)
account = sit.get160();
PathAsset asset;
if (hasCurrency)
asset = Currency::fromRaw(sit.get160());
if (hasMPT)
asset = sit.get192();
if ((*type & TypeIssuer) == TypeIssuer)
if (hasIssuer)
issuer = sit.get160();
path.emplace_back(account, asset, issuer, hasCurrency || hasMPT);
}
} while (type != TypeNone);
}
}
STBase*
@@ -238,22 +216,24 @@ STPath::getJson(JsonOptions) const
for (auto const& it : path_)
{
json::Value elem(json::ValueType::Object);
auto const iType = it.getNodeType();
elem[jss::type] = safeCast<std::uint32_t>(it.getNodeType());
elem[jss::type] = iType;
if (it.isType(STPathElement::TypeAccount))
if ((iType & STPathElement::TypeAccount) != 0u)
elem[jss::account] = to_string(it.getAccountID());
XRPL_ASSERT(
!(it.hasCurrency() && it.hasMPT()),
((iType & STPathElement::TypeCurrency) == 0u) ||
((iType & STPathElement::TypeMpt) == 0u),
"xrpl::STPath::getJson : not type Currency and MPT");
if (it.hasCurrency())
if ((iType & STPathElement::TypeCurrency) != 0u)
elem[jss::currency] = to_string(it.getCurrency());
if (it.hasMPT())
if ((iType & STPathElement::TypeMpt) != 0u)
elem[jss::mpt_issuance_id] = to_string(it.getMPTID());
if (it.hasIssuer())
if ((iType & STPathElement::TypeIssuer) != 0u)
elem[jss::issuer] = to_string(it.getIssuerID());
ret.append(elem);
@@ -285,42 +265,34 @@ STPathSet::add(Serializer& s) const
XRPL_ASSERT(getFName().fieldType == STI_PATHSET, "xrpl::STPathSet::add : valid field type");
bool first = true;
// Workaround, since `Serializer` does not accept strongly-typed enums
// yet. When support is added, the static_assert will trigger, and the
// lambda can be removed entirely.
auto const toByte = [](std::same_as<STPathElement::Type> auto t) noexcept {
static_assert(
!requires(Serializer& ser) { ser.add8(t); },
"Serializer::add8 now accepts STPathElement::Type directly; remove toByte.");
return std::to_underlying(t);
};
for (auto const& spPath : value_)
{
if (!first)
s.add8(toByte(STPathElement::TypeBoundary));
s.add8(STPathElement::TypeBoundary);
for (auto const& speElement : spPath)
{
s.add8(toByte(speElement.getNodeType()));
int const iType = speElement.getNodeType();
if (speElement.isType(STPathElement::TypeAccount))
s.add8(iType);
if ((iType & STPathElement::TypeAccount) != 0u)
s.addBitString(speElement.getAccountID());
if (speElement.hasMPT())
if ((iType & STPathElement::TypeMpt) != 0u)
s.addBitString(speElement.getMPTID());
if (speElement.hasCurrency())
if ((iType & STPathElement::TypeCurrency) != 0u)
s.addBitString(speElement.getCurrency());
if (speElement.hasIssuer())
if ((iType & STPathElement::TypeIssuer) != 0u)
s.addBitString(speElement.getIssuerID());
}
first = false;
}
s.add8(toByte(STPathElement::TypeNone));
s.add8(STPathElement::TypeNone);
}
} // namespace xrpl
+3 -4
View File
@@ -38,7 +38,7 @@ ApplyContext::ApplyContext(
, parentBatchId_(parentBatchId)
{
XRPL_ASSERT(
parentBatchId.has_value() == ((flags_ & ApplyFlags::Batch) == ApplyFlags::Batch),
parentBatchId.has_value() == ((flags_ & TapBatch) == TapBatch),
"Parent Batch ID should be set if batch apply flag is set");
view_.emplace(&base_, flags_);
}
@@ -53,14 +53,13 @@ std::optional<TxMeta>
ApplyContext::apply(TER ter)
{
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
return view_->apply(
base_, tx, ter, parentBatchId_, (flags_ & ApplyFlags::DryRun) != ApplyFlags::None, journal);
return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & TapDryRun) != 0u, journal);
}
void
ApplyContext::addOrderBook(Book const& book)
{
if ((flags_ & ApplyFlags::DryRun) == ApplyFlags::None)
if ((flags_ & TapDryRun) == TapNone)
registry.get().getOrderBookDB().addOrderBook(book);
}
+7 -9
View File
@@ -134,7 +134,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 +510,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 +521,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;
@@ -952,8 +951,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
@@ -1124,7 +1122,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 =
@@ -1606,7 +1604,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 +1684,7 @@ Transactor::operator()()
// Once we call apply, we will no longer be able to look at view()
metadata = ctx_.apply(result);
if ((ctx_.flags() & ApplyFlags::DryRun) != ApplyFlags::None)
if ((ctx_.flags() & TapDryRun) != 0u)
return logger(result, false, std::move(metadata));
return logger(result, canApply, std::move(metadata));
+2 -2
View File
@@ -215,7 +215,7 @@ applyBatchTransactions(
auto applyOneTransaction = [&registry, &j, &parentBatchId, &batchView](STTx const& tx) {
OpenView perTxBatchView(kBatchView, batchView);
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, ApplyFlags::Batch, j);
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, TapBatch, j);
XRPL_ASSERT(
ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)),
"Inner transaction should not be applied");
@@ -274,7 +274,7 @@ applyTransaction(
{
// Returns false if the transaction has need not be retried.
if (retryAssured)
flags = flags | ApplyFlags::Retry;
flags = flags | TapRetry;
JLOG(j.debug()) << "TXN " << txn.getTransactionID() << (retryAssured ? "/retry" : "/final");
+2 -4
View File
@@ -346,8 +346,7 @@ preflight(
// builds; re-check the same invariant here so a release build can't
// silently skip a proposed transaction's signature-presence checks
// outside of a dry run.
if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
{
// LCOV_EXCL_START
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
@@ -378,8 +377,7 @@ preflight(
PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
// See the comment in the other preflight() overload above.
if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
{
// LCOV_EXCL_START
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
+1 -1
View File
@@ -985,7 +985,7 @@ std::optional<std::variant<Quality, AMMOffer<TIn, TOut>>>
BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
{
// This can be simplified (and sped up) if directories are never empty.
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
BookTip bt(sb, book_);
auto const lobQuality = bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
// Multi-path offer generates an offer with the quality
+27 -13
View File
@@ -57,8 +57,10 @@ checkNear(IOUAmount const& expected, IOUAmount const& actual)
static bool
isXRPAccount(STPathElement const& pe)
{
return pe.getNodeType() == STPathElement::TypeAccount && isXRP(pe.getAccountID());
}
if (pe.getNodeType() != STPathElement::TypeAccount)
return false;
return isXRP(pe.getAccountID());
};
static std::pair<TER, std::unique_ptr<Step>>
toStep(
@@ -69,8 +71,11 @@ toStep(
{
auto& j = ctx.j;
if (ctx.isFirst && e1->isAccount() && e1->hasCurrency() && e1->getPathAsset().isXRP())
if (ctx.isFirst && e1->isAccount() &&
((e1->getNodeType() & STPathElement::TypeCurrency) != 0u) && e1->getPathAsset().isXRP())
{
return makeXrpEndpointStep(ctx, e1->getAccountID());
}
if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
return makeXrpEndpointStep(ctx, e2->getAccountID());
@@ -112,9 +117,15 @@ toStep(
// LCOV_EXCL_STOP
}
XRPL_ASSERT(e2->hasAsset() || e2->hasIssuer(), "xrpl::toStep : currency or issuer");
PathAsset const outAsset = e2->hasAsset() ? e2->getPathAsset() : curAsset;
auto const outIssuer = e2->hasIssuer() ? e2->getIssuerID() : curAsset.getIssuer();
XRPL_ASSERT(
(e2->getNodeType() & STPathElement::TypeAsset) ||
(e2->getNodeType() & STPathElement::TypeIssuer),
"xrpl::toStep : currency or issuer");
PathAsset const outAsset =
((e2->getNodeType() & STPathElement::TypeAsset) != 0u) ? e2->getPathAsset() : curAsset;
auto const outIssuer = ((e2->getNodeType() & STPathElement::TypeIssuer) != 0u)
? e2->getIssuerID()
: curAsset.getIssuer();
if (isXRP(curAsset) && outAsset.isXRP())
{
@@ -186,15 +197,16 @@ toStrand(
for (std::size_t i = 0; i < path.size(); ++i)
{
auto const& pe = path[i];
auto const t = pe.getNodeType();
if ((pe.getNodeType() & ~STPathElement::TypeAll) != STPathElement::TypeNone || pe.isNone())
if (((t & ~STPathElement::TypeAll) != 0u) || (t == 0u))
return {temBAD_PATH, Strand{}};
bool const hasAccount = pe.isType(STPathElement::TypeAccount);
bool const hasIssuer = pe.hasIssuer();
bool const hasCurrency = pe.hasCurrency();
bool const hasMPT = pe.hasMPT();
bool const hasAsset = pe.hasAsset();
bool const hasAccount = (t & STPathElement::TypeAccount) != 0u;
bool const hasIssuer = (t & STPathElement::TypeIssuer) != 0u;
bool const hasCurrency = (t & STPathElement::TypeCurrency) != 0u;
bool const hasMPT = (t & STPathElement::TypeMpt) != 0u;
bool const hasAsset = (t & STPathElement::TypeAsset) != 0u;
if (hasAccount && (hasIssuer || hasCurrency))
return {temBAD_PATH, Strand{}};
@@ -238,7 +250,9 @@ toStrand(
}();
// Currency or MPT
auto hasAsset = [](STPathElement const& pe) { return pe.hasAsset(); };
auto hasAsset = [](STPathElement const pe) {
return pe.getNodeType() & STPathElement::TypeAsset;
};
std::vector<STPathElement> normPath;
// reserve enough for the path, the implied source, destination,
@@ -187,8 +187,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};
}
}
+2 -1
View File
@@ -5,6 +5,7 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -330,7 +331,7 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
return err;
}
if (auto const err = createMPToken(sb, mptID, accountId, {}, flags);
if (auto const err = MPTokenEntryW::create(sb, mptID, accountId, {}, flags);
!isTesSuccess(err))
return err;
// Don't adjust AMM owner count.
@@ -295,8 +295,7 @@ OfferCreate::checkAcceptAsset(
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};
}
}
@@ -3,7 +3,6 @@
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -95,12 +94,13 @@ EscrowCancel::preclaim(PreclaimContext const& ctx)
if (ctx.view.rules().enabled(featureTokenEscrow))
{
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
EscrowEntryR const escrow(ctx.tx[sfOwner], seqProxy, ctx.view);
if (!escrow)
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy);
auto const slep = ctx.view.read(k);
if (!slep)
return tecNO_TARGET;
AccountID const account = (*escrow)[sfAccount];
STAmount const amount = (*escrow)[sfAmount];
AccountID const account = (*slep)[sfAccount];
STAmount const amount = (*slep)[sfAmount];
if (!isXRP(amount))
{
@@ -120,8 +120,9 @@ TER
EscrowCancel::doApply()
{
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
EscrowEntryW escrow(ctx_.tx[sfOwner], seqProxy, ctx_.view(), j_);
if (!escrow)
auto const k = keylet::escrow(ctx_.tx[sfOwner], seqProxy);
auto const slep = ctx_.view().peek(k);
if (!slep)
{
if (ctx_.view().rules().enabled(featureTokenEscrow))
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -132,19 +133,19 @@ EscrowCancel::doApply()
auto const now = ctx_.view().header().parentCloseTime;
// No cancel time specified: can't execute at all.
if (!(*escrow)[~sfCancelAfter])
if (!(*slep)[~sfCancelAfter])
return tecNO_PERMISSION;
// Too soon: can't execute before the cancel time.
if (!after(now, (*escrow)[sfCancelAfter]))
if (!after(now, (*slep)[sfCancelAfter]))
return tecNO_PERMISSION;
AccountID const account = (*escrow)[sfAccount];
AccountID const account = (*slep)[sfAccount];
// Remove escrow from owner directory
{
auto const page = (*escrow)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, escrow.key(), true))
auto const page = (*slep)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
@@ -154,10 +155,9 @@ EscrowCancel::doApply()
}
// Remove escrow from recipient's owner directory, if present.
if (auto const optPage = (*escrow)[~sfDestinationNode]; optPage)
if (auto const optPage = (*slep)[~sfDestinationNode]; optPage)
{
if (!ctx_.view().dirRemove(
keylet::ownerDir((*escrow)[sfDestination]), *optPage, escrow.key(), true))
if (!ctx_.view().dirRemove(keylet::ownerDir((*slep)[sfDestination]), *optPage, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -167,13 +167,13 @@ EscrowCancel::doApply()
}
auto const sle = ctx_.view().peek(keylet::account(account));
STAmount const amount = escrow->getFieldAmount(sfAmount);
STAmount const amount = slep->getFieldAmount(sfAmount);
// The return can re-create a holding the owner deleted while the escrow
// was pending; the removed escrow must not be counted against its reserve.
bool const recycleReserve = ctx_.view().rules().enabled(fixCleanup3_4_0);
if (recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), sle, escrow.mutableRawSle(), 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal);
// Transfer amount back to the owner
if (isXRP(amount))
@@ -192,7 +192,7 @@ EscrowCancel::doApply()
return escrowUnlockApplyHelper<T>(
ctx_.getApplyViewContext(),
kParityRate,
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : escrow.mutableRawSle(),
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : slep,
preFeeBalance_,
amount,
issuer,
@@ -206,9 +206,9 @@ EscrowCancel::doApply()
return ret; // LCOV_EXCL_LINE
// Remove escrow from issuers owner directory, if present.
if (auto const optPage = (*escrow)[~sfIssuerNode]; optPage)
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
{
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, escrow.key(), true))
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -219,10 +219,10 @@ EscrowCancel::doApply()
}
if (!recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), sle, escrow.mutableRawSle(), 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal);
// Remove escrow from ledger
escrow.erase();
ctx_.view().erase(slep);
return tesSUCCESS;
}
@@ -7,7 +7,6 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -482,31 +481,30 @@ EscrowCreate::doApply()
// Create escrow in ledger. Note that we use the value from the
// sequence or ticket. For more explanation see comments in SeqProxy.h.
EscrowEntryW escrow(accountID_, ctx_.tx.getSeqProxy(), ctx_.view(), j_);
Keylet const escrowKeylet = escrow.keylet();
escrow.newSLE();
(*escrow)[sfAmount] = amount;
(*escrow)[sfAccount] = accountID_;
(*escrow)[~sfCondition] = ctx_.tx[~sfCondition];
(*escrow)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
(*escrow)[sfDestination] = ctx_.tx[sfDestination];
(*escrow)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
(*escrow)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
(*escrow)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
Keylet const escrowKeylet = keylet::escrow(accountID_, ctx_.tx.getSeqProxy());
auto const slep = std::make_shared<SLE>(escrowKeylet);
(*slep)[sfAmount] = amount;
(*slep)[sfAccount] = accountID_;
(*slep)[~sfCondition] = ctx_.tx[~sfCondition];
(*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
(*slep)[sfDestination] = ctx_.tx[sfDestination];
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
{
(*escrow)[sfSequence] = ctx_.tx.getSeqProxy().value();
(*slep)[sfSequence] = ctx_.tx.getSeqProxy().value();
}
if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
{
auto const xferRate = transferRate(ctx_.view(), amount);
if (xferRate != kParityRate)
(*escrow)[sfTransferRate] = xferRate.value;
(*slep)[sfTransferRate] = xferRate.value;
}
escrow.insert();
ctx_.view().insert(slep);
// Add escrow to sender's owner directory
{
@@ -514,7 +512,7 @@ EscrowCreate::doApply()
keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*escrow)[sfOwnerNode] = *page;
(*slep)[sfOwnerNode] = *page;
}
// If it's not a self-send, add escrow to recipient's owner directory.
@@ -525,7 +523,7 @@ EscrowCreate::doApply()
ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*escrow)[sfDestinationNode] = *page;
(*slep)[sfDestinationNode] = *page;
}
// IOU escrow objects are added to the issuer's owner directory to help
@@ -538,7 +536,7 @@ EscrowCreate::doApply()
ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
if (!page)
return tecDIR_FULL; // LCOV_EXCL_LINE
(*escrow)[sfIssuerNode] = *page;
(*slep)[sfIssuerNode] = *page;
}
// Deduct owner's balance
@@ -561,7 +559,7 @@ EscrowCreate::doApply()
// increment owner count
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), escrow.mutableRawSle());
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
ctx_.view().update(sle);
return tesSUCCESS;
}
@@ -9,7 +9,6 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/EscrowEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/EscrowHelpers.h>
@@ -212,12 +211,13 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
if (ctx.view.rules().enabled(featureTokenEscrow))
{
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
EscrowEntryR const escrow(ctx.tx[sfOwner], seqProxy, ctx.view);
if (!escrow)
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy);
auto const slep = ctx.view.read(k);
if (!slep)
return tecNO_TARGET;
AccountID const dest = (*escrow)[sfDestination];
STAmount const amount = (*escrow)[sfAmount];
AccountID const dest = (*slep)[sfDestination];
STAmount const amount = (*slep)[sfAmount];
if (!isXRP(amount))
{
@@ -237,8 +237,9 @@ TER
EscrowFinish::doApply()
{
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
EscrowEntryW escrow(ctx_.tx[sfOwner], seqProxy, ctx_.view(), j_);
if (!escrow)
auto const k = keylet::escrow(ctx_.tx[sfOwner], seqProxy);
auto const slep = ctx_.view().peek(k);
if (!slep)
{
if (ctx_.view().rules().enabled(featureTokenEscrow))
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -251,11 +252,11 @@ EscrowFinish::doApply()
auto const now = ctx_.view().header().parentCloseTime;
// Too soon: can't execute before the finish time
if ((*escrow)[~sfFinishAfter] && !after(now, (*escrow)[sfFinishAfter]))
if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
return tecNO_PERMISSION;
// Too late: can't execute after the cancel time
if ((*escrow)[~sfCancelAfter] && after(now, (*escrow)[sfCancelAfter]))
if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
return tecNO_PERMISSION;
// Check cryptocondition fulfillment
@@ -295,7 +296,7 @@ EscrowFinish::doApply()
return tecCRYPTOCONDITION_ERROR;
// Check against condition in the ledger entry:
auto const cond = (*escrow)[~sfCondition];
auto const cond = (*slep)[~sfCondition];
// If a condition wasn't specified during creation,
// one shouldn't be included now.
@@ -312,7 +313,7 @@ EscrowFinish::doApply()
}
// NOTE: Escrow payments cannot be used to fund accounts.
AccountID const destID = (*escrow)[sfDestination];
AccountID const destID = (*slep)[sfDestination];
auto const sled = ctx_.view().peek(keylet::account(destID));
if (!sled)
return tecNO_DST;
@@ -322,12 +323,12 @@ EscrowFinish::doApply()
!isTesSuccess(err))
return err;
AccountID const account = (*escrow)[sfAccount];
AccountID const account = (*slep)[sfAccount];
// Remove escrow from owner directory
{
auto const page = (*escrow)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, escrow.key(), true))
auto const page = (*slep)[sfOwnerNode];
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
@@ -337,9 +338,9 @@ EscrowFinish::doApply()
}
// Remove escrow from recipient's owner directory, if present.
if (auto const optPage = (*escrow)[~sfDestinationNode])
if (auto const optPage = (*slep)[~sfDestinationNode])
{
if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, escrow.key(), true))
if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -354,9 +355,9 @@ EscrowFinish::doApply()
bool const recycleReserve =
ctx_.view().rules().enabled(featureSponsor) || ctx_.view().rules().enabled(fixCleanup3_4_0);
if (recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), account, escrow.mutableRawSle(), 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
STAmount const amount = escrow->getFieldAmount(sfAmount);
STAmount const amount = slep->getFieldAmount(sfAmount);
// Transfer amount to destination
if (isXRP(amount))
{
@@ -367,8 +368,8 @@ EscrowFinish::doApply()
if (!ctx_.view().rules().enabled(featureTokenEscrow))
return temDISABLED; // LCOV_EXCL_LINE
Rate lockedRate = escrow->isFieldPresent(sfTransferRate)
? xrpl::Rate(escrow->getFieldU32(sfTransferRate))
Rate lockedRate = slep->isFieldPresent(sfTransferRate)
? xrpl::Rate(slep->getFieldU32(sfTransferRate))
: kParityRate;
auto const issuer = amount.getIssuer();
bool const createAsset = destID == accountID_;
@@ -391,9 +392,9 @@ EscrowFinish::doApply()
return ret;
// Remove escrow from issuers owner directory, if present.
if (auto const optPage = (*escrow)[~sfIssuerNode]; optPage)
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
{
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, escrow.key(), true))
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
@@ -406,10 +407,10 @@ EscrowFinish::doApply()
ctx_.view().update(sled);
if (!recycleReserve)
decreaseOwnerCountForObject(ctx_.view(), account, escrow.mutableRawSle(), 1, ctx_.journal);
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
// Remove escrow from ledger
escrow.erase();
ctx_.view().erase(slep);
return tesSUCCESS;
}
@@ -91,8 +91,7 @@ LoanSet::preflight(PreflightContext const& ctx)
// A proposed LoanSet is stored unsigned; its CounterpartySignature is
// collected on-ledger afterward, so its absence here is expected, not an
// error (On-Chain Cosigner spec §5.3.1.2).
if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig &&
(ctx.flags & ApplyFlags::Proposal) == ApplyFlags::None)
if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig && (ctx.flags & TapProposal) == 0)
{
JLOG(ctx.j.warn()) << "LoanSet transaction must have a CounterpartySignature.";
return temBAD_SIGNER;
@@ -54,8 +54,8 @@ TransactionProposalCreate::preflight(PreflightContext const& ctx)
try
{
STTx const stx{STObject{proposedTx}};
auto const inner = xrpl::preflight(
ctx.registry, ctx.rules, stx, ApplyFlags::DryRun | ApplyFlags::Proposal, ctx.j);
auto const inner =
xrpl::preflight(ctx.registry, ctx.rules, stx, TapDryRun | TapProposal, ctx.j);
if (!isTesSuccess(inner.ter))
{
JLOG(ctx.j.debug()) << "TransactionProposalCreate: proposed txn "
@@ -205,7 +205,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx)
// is collected on-ledger afterward, so its absence here is expected, not
// an error (On-Chain Cosigner spec §5.3.1.2).
if (isAccountReserveSponsorship && !ctx.tx.isFieldPresent(sfSponsorSignature) &&
(ctx.flags & ApplyFlags::Proposal) == ApplyFlags::None)
(ctx.flags & TapProposal) == 0)
{
JLOG(ctx.j.debug()) << "preflight: account sponsorship requires sfSponsorSignature";
return temMALFORMED;
+2 -3
View File
@@ -359,8 +359,7 @@ Batch::preflight(PreflightContext const& ctx)
// TapBatch — PreflightContext with a parentBatchId requires it.
// LoanSet already short-circuits on tfInnerBatchTxn; it is also in
// kDisabledTxTypes, so it never reaches this call.
ApplyFlags const innerFlags =
ApplyFlags::Batch | (ctx.flags & (ApplyFlags::Proposal | ApplyFlags::DryRun));
ApplyFlags const innerFlags = TapBatch | (ctx.flags & (TapProposal | TapDryRun));
if (auto const preflightResult =
xrpl::preflight(ctx.registry, ctx.rules, parentBatchId, stx, innerFlags, ctx.j);
!isTesSuccess(preflightResult.ter))
@@ -432,7 +431,7 @@ Batch::preflightSigValidated(PreflightContext const& ctx)
// A proposed Batch is stored unsigned; its BatchSigners are collected
// on-ledger afterward, so the signer-presence match belongs to submission
// time, not proposal creation (On-Chain Cosigner spec §5.3.1.2).
if ((ctx.flags & ApplyFlags::Proposal) != ApplyFlags::None)
if ((ctx.flags & TapProposal) != 0)
return tesSUCCESS;
auto const parentBatchId = ctx.tx.getTransactionID();
+7 -6
View File
@@ -8,7 +8,6 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/AmendmentTable.h>
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h>
@@ -261,14 +260,16 @@ Change::applyAmendment()
TER
Change::applyFee()
{
FeeSettingsEntryW feeObject(view());
auto const k = keylet::feeSettings();
SLE::pointer feeObject = view().peek(k);
if (!feeObject)
{
feeObject.newSLE();
feeObject.insert();
feeObject = std::make_shared<SLE>(k);
view().insert(feeObject);
}
auto set = [](FeeSettingsEntryW& feeObject, STTx const& tx, auto const& field) {
auto set = [](SLE::pointer& feeObject, STTx const& tx, auto const& field) {
feeObject->at(field) = tx[field];
};
if (view().rules().enabled(featureXRPFees))
@@ -290,7 +291,7 @@ Change::applyFee()
set(feeObject, ctx_.tx, sfReserveIncrement);
}
feeObject.update();
view().update(feeObject);
JLOG(j_.warn()) << "Fees have been changed";
return tesSUCCESS;
@@ -4,6 +4,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -94,7 +95,7 @@ ConfidentialMPTClawback::preclaim(PreclaimContext const& ctx)
return tecNO_PERMISSION;
// Check holder's MPToken
auto const sleHolderMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, holder));
MPTokenEntryR const sleHolderMPToken(mptIssuanceID, holder, ctx.view);
if (!sleHolderMPToken)
return tecOBJECT_NOT_FOUND;
@@ -133,7 +134,7 @@ ConfidentialMPTClawback::doApply()
auto const holder = ctx_.tx[sfHolder];
auto sleIssuance = view().peek(keylet::mptokenIssuance(mptIssuanceID));
auto sleHolderMPToken = view().peek(keylet::mptoken(mptIssuanceID, holder));
MPTokenEntryW sleHolderMPToken(mptIssuanceID, holder, view(), j_);
if (!sleIssuance || !sleHolderMPToken)
{
@@ -228,7 +229,7 @@ ConfidentialMPTClawback::doApply()
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleIssuance)[sfOutstandingAmount] = oldOA - clawAmount;
view().update(sleHolderMPToken);
sleHolderMPToken.update();
view().update(sleIssuance);
return tesSUCCESS;
@@ -6,6 +6,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
@@ -108,7 +109,7 @@ ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx)
if (requiresAuditor != hasAuditor)
return tecNO_PERMISSION;
auto const sleMptoken = ctx.view.read(keylet::mptoken(issuanceID, account));
MPTokenEntryR const sleMptoken(issuanceID, account, ctx.view);
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
@@ -225,7 +226,7 @@ ConfidentialMPTConvert::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
MPTokenEntryW sleMptoken(mptIssuanceID, accountID_, view(), j_);
if (!sleMptoken)
{
// LCOV_EXCL_START
@@ -377,7 +378,7 @@ ConfidentialMPTConvert::doApply()
}
view().update(sleIssuance);
view().update(sleMptoken);
sleMptoken.update();
return tesSUCCESS;
}
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
@@ -71,7 +72,7 @@ static TER
verifyProofs(
STTx const& tx,
std::shared_ptr<SLE const> const& issuance,
std::shared_ptr<SLE const> const& mptoken)
MPTokenEntryR const& mptoken)
{
if (!mptoken->isFieldPresent(sfHolderEncryptionKey))
{
@@ -187,7 +188,7 @@ ConfidentialMPTConvertBack::preclaim(PreclaimContext const& ctx)
// LCOV_EXCL_STOP
}
auto const sleMptoken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
MPTokenEntryR const sleMptoken(mptIssuanceID, account, ctx.view);
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
@@ -244,7 +245,7 @@ ConfidentialMPTConvertBack::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
MPTokenEntryW sleMptoken(mptIssuanceID, accountID_, view(), j_);
if (!sleMptoken)
{
// LCOV_EXCL_START
@@ -334,7 +335,7 @@ ConfidentialMPTConvertBack::doApply()
incrementConfidentialVersion(*sleMptoken);
view().update(sleIssuance);
view().update(sleMptoken);
sleMptoken.update();
return tesSUCCESS;
}
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -116,7 +117,7 @@ ConfidentialMPTHolderKeyUpdate::preclaim(PreclaimContext const& ctx)
if (!sleIssuance->isFlag(lsfMPTCanHoldConfidentialBalance))
return tecNO_PERMISSION;
auto const sleMptoken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
MPTokenEntryR const sleMptoken(mptIssuanceID, account, ctx.view);
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
@@ -150,7 +151,7 @@ TER
ConfidentialMPTHolderKeyUpdate::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
MPTokenEntryW sleMptoken(mptIssuanceID, accountID_, view(), j_);
if (!sleMptoken)
{
// LCOV_EXCL_START
@@ -166,7 +167,7 @@ ConfidentialMPTHolderKeyUpdate::doApply()
// The holder revokes their pending recovery authorization; the
// current key and balances are left untouched.
sleMptoken->makeFieldAbsent(sfRecoveryKey);
view().update(sleMptoken);
sleMptoken.update();
return tesSUCCESS;
}
@@ -197,7 +198,7 @@ ConfidentialMPTHolderKeyUpdate::doApply()
// LCOV_EXCL_STOP
}
view().update(sleMptoken);
sleMptoken.update();
return tesSUCCESS;
}
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Indexes.h>
@@ -59,8 +60,7 @@ ConfidentialMPTMergeInbox::preclaim(PreclaimContext const& ctx)
// LCOV_EXCL_STOP
}
auto const sleMptoken =
ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], ctx.tx[sfAccount]));
MPTokenEntryR const sleMptoken(ctx.tx[sfMPTokenIssuanceID], ctx.tx[sfAccount], ctx.view);
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
@@ -88,7 +88,7 @@ TER
ConfidentialMPTMergeInbox::doApply()
{
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
MPTokenEntryW sleMptoken(mptIssuanceID, accountID_, view(), j_);
if (!sleMptoken)
{
// LCOV_EXCL_START
@@ -147,7 +147,7 @@ ConfidentialMPTMergeInbox::doApply()
incrementConfidentialVersion(*sleMptoken);
view().update(sleMptoken);
sleMptoken.update();
return tesSUCCESS;
}
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -131,7 +132,7 @@ ConfidentialMPTMirrorUpdate::preclaim(PreclaimContext const& ctx)
return tecNO_TARGET;
// In either issuer or holder mode, check the existence of the MPToken object.
auto const sleMptoken = ctx.view.read(keylet::mptoken(mptIssuanceID, holderID));
MPTokenEntryR const sleMptoken(mptIssuanceID, holderID, ctx.view);
if (!sleMptoken)
return tecOBJECT_NOT_FOUND;
@@ -224,7 +225,7 @@ ConfidentialMPTMirrorUpdate::doApply()
auto const holder = ctx_.tx[~sfHolder];
auto const holderID = holder.value_or(accountID_);
auto sleMptoken = view().peek(keylet::mptoken(mptIssuanceID, holderID));
MPTokenEntryW sleMptoken(mptIssuanceID, holderID, view(), j_);
if (!sleMptoken)
{
// LCOV_EXCL_START
@@ -250,7 +251,7 @@ ConfidentialMPTMirrorUpdate::doApply()
setAuditorMirrorEpoch(*sleIssuance, *sleMptoken);
}
view().update(sleMptoken);
sleMptoken.update();
return tesSUCCESS;
}
@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/ConfidentialTransfer.h>
@@ -100,8 +101,8 @@ namespace detail {
static TER
verifySendProofs(
PreclaimContext const& ctx,
std::shared_ptr<SLE const> const& sleSenderMPToken,
std::shared_ptr<SLE const> const& sleDestinationMPToken,
MPTokenEntryR const& sleSenderMPToken,
MPTokenEntryR const& sleDestinationMPToken,
std::shared_ptr<SLE const> const& sleIssuance)
{
// Sanity check
@@ -222,7 +223,7 @@ ConfidentialMPTSend::preclaim(PreclaimContext const& ctx)
}
// Check sender's MPToken existence
auto const sleSenderMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
MPTokenEntryR const sleSenderMPToken(mptIssuanceID, account, ctx.view);
if (!sleSenderMPToken)
return tecOBJECT_NOT_FOUND;
@@ -235,7 +236,7 @@ ConfidentialMPTSend::preclaim(PreclaimContext const& ctx)
}
// Check destination's MPToken existence
auto const sleDestinationMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, destination));
MPTokenEntryR const sleDestinationMPToken(mptIssuanceID, destination, ctx.view);
if (!sleDestinationMPToken)
return tecOBJECT_NOT_FOUND;
@@ -305,8 +306,8 @@ ConfidentialMPTSend::doApply()
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
auto const destination = ctx_.tx[sfDestination];
auto sleSenderMPToken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
auto sleDestinationMPToken = view().peek(keylet::mptoken(mptIssuanceID, destination));
MPTokenEntryW sleSenderMPToken(mptIssuanceID, accountID_, view(), j_);
MPTokenEntryW sleDestinationMPToken(mptIssuanceID, destination, view(), j_);
auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
auto const sleDestAcct = view().read(keylet::account(destination));
@@ -469,8 +470,8 @@ ConfidentialMPTSend::doApply()
// increment sender version only; receiver version is not modified by incoming sends
incrementConfidentialVersion(*sleSenderMPToken);
view().update(sleSenderMPToken);
view().update(sleDestinationMPToken);
sleSenderMPToken.update();
sleDestinationMPToken.update();
return tesSUCCESS;
}
@@ -1,6 +1,7 @@
#include <xrpl/tx/transactors/token/MPTokenAuthorize.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/protocol/Feature.h>
@@ -47,8 +48,7 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx)
// `holderID` is NOT used
if (!holderID)
{
SLE::const_pointer const sleMpt =
ctx.view.read(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], accountID));
MPTokenEntryR const sleMpt(ctx.tx[sfMPTokenIssuanceID], accountID, ctx.view);
// There is an edge case where all holders have zero balance, issuance
// is legally destroyed, then outstanding MPT(s) are deleted afterwards.
@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/entries/MPTokenEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -131,7 +132,7 @@ VaultDelete::doApply()
}
// Try to remove MPToken for vault shares for the vault owner if it exists.
if (auto const mptoken = view().peek(keylet::mptoken(shareMPTID, accountID_)))
if (MPTokenEntryW(shareMPTID, accountID_, view(), j_))
{
if (auto const ter =
removeEmptyHolding(applyViewContext, accountID_, MPTIssue(shareMPTID), j_);
+2 -2
View File
@@ -1890,7 +1890,7 @@ private:
auto const flowResult = [&] {
STAmount const deliver(eth(51'000'000));
STAmount smax(btc(61'000'000));
PaymentSandbox sb(env.current().get(), ApplyFlags::None);
PaymentSandbox sb(env.current().get(), TapNone);
STPathSet paths;
auto ipe = [](MPTTester const& iss) {
return STPathElement(
@@ -1928,7 +1928,7 @@ private:
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
if (flowResult.removableOffers.empty())
return false;
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
for (auto const& o : flowResult.removableOffers)
{
if (auto ok = sb.peek(keylet::offer(o)))
+2 -2
View File
@@ -2049,7 +2049,7 @@ private:
auto const flowResult = [&] {
STAmount const deliver(USD(51));
STAmount smax(BTC(61));
PaymentSandbox sb(env.current().get(), ApplyFlags::None);
PaymentSandbox sb(env.current().get(), TapNone);
STPathSet paths;
auto ipe = [](Issue const& iss) {
return STPathElement(
@@ -2087,7 +2087,7 @@ private:
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
if (flowResult.removableOffers.empty())
return false;
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
for (auto const& o : flowResult.removableOffers)
{
if (auto ok = sb.peek(keylet::offer(o)))
+2 -2
View File
@@ -4158,7 +4158,7 @@ private:
auto jtx = env.jt(tx, Seq(1), Fee(10));
env.app().config().features.erase(featureMPTokensV2);
PreflightContext const ctx(
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
auto pf = AMMBid::checkExtraFeatures(ctx);
BEAST_EXPECT(pf == false);
env.app().config().features.insert(featureMPTokensV2);
@@ -4170,7 +4170,7 @@ private:
jtx.jv["Asset2"].removeMember("mpt_issuance_id");
jtx.stx = env.ust(jtx);
PreflightContext const ctx(
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
auto pf = AMMBid::preflight(ctx);
BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
}
+3 -3
View File
@@ -3263,7 +3263,7 @@ private:
auto jtx = env.jt(tx, Seq(1), Fee(baseFee));
env.app().config().features.erase(featureAMM);
PreflightContext const pfCtx(
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
BEAST_EXPECT(pf == temDISABLED);
env.app().config().features.insert(featureAMM);
@@ -3274,7 +3274,7 @@ private:
jtx.jv["TxnSignature"] = "deadbeef";
jtx.stx = env.ust(jtx);
PreflightContext const pfCtx(
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
BEAST_EXPECT(!isTesSuccess(pf));
}
@@ -3285,7 +3285,7 @@ private:
jtx.jv["Asset2"].removeMember("issuer");
jtx.stx = env.ust(jtx);
PreflightContext const pfCtx(
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
}
+1 -1
View File
@@ -594,7 +594,7 @@ public:
stx->at(sfSigningPubKey) = makeSlice(std::string("badkey"));
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
auto const result = xrpl::apply(env.app(), view, *stx, ApplyFlags::None, j);
auto const result = xrpl::apply(env.app(), view, *stx, TapNone, j);
BEAST_EXPECT(result.ter == temBAD_SIGNATURE);
BEAST_EXPECT(!result.applied);
return result.applied;
+6 -11
View File
@@ -436,7 +436,7 @@ class Batch_test : public beast::unit_test::Suite
{
auto jtx = env.jt(pay(alice, bob, XRP(1)), Txflags(tfInnerBatchTxn));
PreflightContext const pfCtx(
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
BEAST_EXPECT(pf == temINVALID_INNER_BATCH);
}
@@ -451,7 +451,7 @@ class Batch_test : public beast::unit_test::Suite
disabledEnv.app(),
*jtx.stx,
disabledEnv.current()->rules(),
ApplyFlags::None,
TapNone,
disabledEnv.journal);
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
BEAST_EXPECT(pf == temINVALID_FLAG);
@@ -462,12 +462,7 @@ class Batch_test : public beast::unit_test::Suite
{
auto jtx = env.jt(pay(alice, bob, XRP(1)));
PreflightContext const pfCtx(
env.app(),
*jtx.stx,
UInt256{1},
env.current()->rules(),
ApplyFlags::Batch,
env.journal);
env.app(), *jtx.stx, UInt256{1}, env.current()->rules(), TapBatch, env.journal);
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
BEAST_EXPECT(pf == temINVALID_INNER_BATCH);
}
@@ -1595,7 +1590,7 @@ class Batch_test : public beast::unit_test::Suite
batch::Sig(std::vector<Reg>(kMaxBatchSigners + 1, bob)));
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
auto const result = xrpl::apply(env.app(), view, *jt.stx, ApplyFlags::None, j);
auto const result = xrpl::apply(env.app(), view, *jt.stx, TapNone, j);
BEAST_EXPECT(!result.applied && result.ter == temARRAY_TOO_LARGE);
return result.applied;
});
@@ -4396,7 +4391,7 @@ class Batch_test : public beast::unit_test::Suite
BEAST_EXPECT(!passesLocalChecks(stx, reason));
BEAST_EXPECT(reason == "Cannot submit pseudo transactions.");
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
auto const result = xrpl::apply(env.app(), view, stx, ApplyFlags::None, j);
auto const result = xrpl::apply(env.app(), view, stx, TapNone, j);
BEAST_EXPECT(!result.applied && result.ter == temINVALID_FLAG);
return result.applied;
});
@@ -5642,7 +5637,7 @@ class Batch_test : public beast::unit_test::Suite
obj.setFieldU32(sfFlags, tfInnerBatchTxn);
});
auto const result = xrpl::apply(env.app(), view, stx, ApplyFlags::None, j);
auto const result = xrpl::apply(env.app(), view, stx, TapNone, j);
// Must NOT be applied — signature was never checked
BEAST_EXPECT(!result.applied);
return false;
+1 -2
View File
@@ -1745,8 +1745,7 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase
(*sle)[sfConfidentialBalanceVersion] = wrappedFrom;
view.rawReplace(sle);
auto const result =
xrpl::apply(env.app(), view, *jt.stx, ApplyFlags::None, env.journal);
auto const result = xrpl::apply(env.app(), view, *jt.stx, TapNone, env.journal);
BEAST_EXPECT(result.ter == tesSUCCESS);
return result.applied;
}));
+3 -2
View File
@@ -901,7 +901,8 @@ struct Credentials_test : public beast::unit_test::Suite
{
testcase("deleteSLE fail, bad SLE.");
auto view = std::make_shared<ApplyViewImpl>(env.current().get(), ApplyFlags::None);
auto view =
std::make_shared<ApplyViewImpl>(env.current().get(), ApplyFlags::TapNone);
auto ter = xrpl::credentials::deleteSLE(*view, {}, env.journal);
BEAST_EXPECT(ter == tecNO_ENTRY);
}
@@ -1124,7 +1125,7 @@ struct Credentials_test : public beast::unit_test::Suite
// Create an ApplyViewImpl on top of the current closed ledger
// and corrupt it by erasing the issuer's account SLE
auto const open = env.current();
ApplyViewImpl av(&*open, ApplyFlags::None);
ApplyViewImpl av(&*open, TapNone);
// Erase the issuer's account to simulate ledger corruption
auto sleIssuer = av.peek(keylet::account(issuer.id()));
+2 -2
View File
@@ -2795,7 +2795,7 @@ struct EscrowToken_test : public beast::unit_test::Suite
auto const seq1 = env.seq(alice);
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
auto sleNew =
std::make_shared<SLE>(keylet::escrow(alice, SeqProxy::rawSequence(seq1)));
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
@@ -3023,7 +3023,7 @@ struct EscrowToken_test : public beast::unit_test::Suite
auto const seq1 = env.seq(alice);
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
auto sleNew =
std::make_shared<SLE>(keylet::escrow(alice, SeqProxy::rawSequence(seq1)));
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
+6 -6
View File
@@ -1302,8 +1302,8 @@ struct Escrow_test : public beast::unit_test::Suite
escrow::kFinishTime(env.now() + 1s),
Seq(1),
Fee(baseFee));
auto const pf = preflight(
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
@@ -1312,8 +1312,8 @@ struct Escrow_test : public beast::unit_test::Suite
{
auto const jtx = env.jt(escrow::cancel("bob", "alice", 3), Seq(1), Fee(baseFee));
auto const pf = preflight(
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
@@ -1322,8 +1322,8 @@ struct Escrow_test : public beast::unit_test::Suite
{
auto const jtx = env.jt(escrow::finish("bob", "alice", 3), Seq(1), Fee(baseFee));
auto const pf = preflight(
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
+1 -1
View File
@@ -134,7 +134,7 @@ createInvalidFeeTx(
bool
applyFeeAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx)
{
auto const res = apply(env.app(), view, tx, ApplyFlags::None, env.journal);
auto const res = apply(env.app(), view, tx, ApplyFlags::TapNone, env.journal);
return isTesSuccess(res.ter);
}
+2 -2
View File
@@ -333,7 +333,7 @@ struct FlowMPT_test : public beast::unit_test::Suite
auto const flowResult = [&] {
STAmount const deliver(usd(5'100));
STAmount smax(btc(61));
PaymentSandbox sb(env.current().get(), ApplyFlags::None);
PaymentSandbox sb(env.current().get(), TapNone);
STPathSet paths;
auto ipe = [](Asset const& asset) {
return STPathElement(
@@ -371,7 +371,7 @@ struct FlowMPT_test : public beast::unit_test::Suite
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
if (flowResult.removableOffers.empty())
return false;
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
for (auto const& o : flowResult.removableOffers)
{
if (auto ok = sb.peek(keylet::offer(o)))
+2 -2
View File
@@ -476,7 +476,7 @@ struct Flow_test : public beast::unit_test::Suite
auto const flowResult = [&] {
STAmount const deliver(usd(51));
STAmount smax(btc(61));
PaymentSandbox sb(env.current().get(), ApplyFlags::None);
PaymentSandbox sb(env.current().get(), TapNone);
STPathSet paths;
auto ipe = [](Issue const& iss) {
return STPathElement(
@@ -514,7 +514,7 @@ struct Flow_test : public beast::unit_test::Suite
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
if (flowResult.removableOffers.empty())
return false;
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
for (auto const& o : flowResult.removableOffers)
{
if (auto ok = sb.peek(keylet::offer(o)))
+1 -1
View File
@@ -59,7 +59,7 @@ public:
if (stx)
{
OpenView accum(&*res);
applyTransaction(env.app(), accum, *stx, false, ApplyFlags::None, env.journal);
applyTransaction(env.app(), accum, *stx, false, TapNone, env.journal);
accum.apply(*res);
}
res->updateSkipList();
+2 -2
View File
@@ -90,7 +90,7 @@ struct LedgerReplay_test : public beast::unit_test::Suite
auto const lastClosedParent = ledgerMaster.getLedgerByHash(lastClosed->header().parentHash);
auto const replayed = buildLedger(
LedgerReplay(lastClosedParent, lastClosed), ApplyFlags::None, env.app(), env.journal);
LedgerReplay(lastClosedParent, lastClosed), TapNone, env.app(), env.journal);
BEAST_EXPECT(replayed->header().hash == lastClosed->header().hash);
}
@@ -122,7 +122,7 @@ struct LedgerReplay_test : public beast::unit_test::Suite
auto const lastClosedParent = ledgerMaster.getLedgerByHash(lastClosed->header().parentHash);
auto const replayed = buildLedger(
LedgerReplay(lastClosedParent, lastClosed), ApplyFlags::None, env.app(), env.journal);
LedgerReplay(lastClosedParent, lastClosed), TapNone, env.app(), env.journal);
BEAST_EXPECT(replayed->header().hash == lastClosed->header().hash);
}
+1 -1
View File
@@ -4059,7 +4059,7 @@ class MPToken_test : public beast::unit_test::Suite
TER expectedTer,
std::optional<std::uint64_t> expectedOutstanding,
std::string const& label) {
ApplyViewImpl av(&*env.current(), ApplyFlags::None);
ApplyViewImpl av(&*env.current(), TapNone);
auto const ter =
accountSendMulti(av, issuer.id(), asset, receivers, env.app().getJournal("View"));
BEAST_EXPECTS(ter == expectedTer, label);
+2 -14
View File
@@ -776,13 +776,7 @@ class NFTokenBurn_test : public beast::unit_test::Suite
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(),
ov,
tx,
tesSUCCESS,
env.current()->fees().base,
ApplyFlags::None,
jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
// Verify that the last page is present and contains one NFT.
auto lastNFTokenPage = ac.view().peek(keylet::nftokenPageMax(alice));
@@ -814,13 +808,7 @@ class NFTokenBurn_test : public beast::unit_test::Suite
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(),
ov,
tx,
tesSUCCESS,
env.current()->fees().base,
ApplyFlags::None,
jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
// Verify that the middle page is present.
auto lastNFTokenPage = ac.view().peek(keylet::nftokenPageMax(alice));
-24
View File
@@ -1404,30 +1404,6 @@ struct PayChan_test : public beast::unit_test::Suite
rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
}
{
// channel_id is not a string.
json::Value args{json::ValueType::Object};
args[jss::amount] = "2000";
args[jss::channel_id] = 2000;
args[jss::key_type] = "secp256k1";
args[jss::passphrase] = "passphrase_can_be_anything";
auto const ra =
env.rpc("json", "channel_authorize", args.toStyledString())[jss::result];
BEAST_EXPECT(ra[jss::error] == "invalidParams");
}
{
// channel_id is not a string.
json::Value args{json::ValueType::Object};
args[jss::public_key] = chan1PkStr;
args[jss::channel_id] = 2000;
args[jss::amount] = "1000";
args[jss::signature] = sig;
auto const rv =
env.rpc("json", "channel_verify", args.toStyledString())[jss::result];
BEAST_EXPECT(rv[jss::error] == "invalidParams");
}
{
// give an ill formed base 58 public key
auto illFormedPk = chan1PkStr.substr(0, chan1PkStr.size() - 1);
+1 -1
View File
@@ -572,7 +572,7 @@ struct PayStrandMPT_test : public beast::unit_test::Suite
inputs.defaultPathsAllowed = true;
try
{
PaymentSandbox sb{env.current().get(), ApplyFlags::None};
PaymentSandbox sb{env.current().get(), TapNone};
{
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
sb,
+2 -3
View File
@@ -768,8 +768,7 @@ struct PayStrand_test : public beast::unit_test::Suite
env,
usd,
std::nullopt,
STPath({STPathElement(
STPathElement::Type::TypeNone, xrpAccount(), xrpCurrency(), xrpAccount())}),
STPath({STPathElement(0, xrpAccount(), xrpCurrency(), xrpAccount())}),
temBAD_PATH);
// The same account can't appear more than once on a path
@@ -1077,7 +1076,7 @@ struct PayStrand_test : public beast::unit_test::Suite
inputs.defaultPathsAllowed = true;
try
{
PaymentSandbox sb{env.current().get(), ApplyFlags::None};
PaymentSandbox sb{env.current().get(), TapNone};
{
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
sb,
+1 -1
View File
@@ -82,7 +82,7 @@ struct PseudoTx_test : public beast::unit_test::Suite
BEAST_EXPECT(!passesLocalChecks(stx, reason));
BEAST_EXPECT(reason == "Cannot submit pseudo transactions.");
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
auto const result = xrpl::apply(env.app(), view, stx, ApplyFlags::None, j);
auto const result = xrpl::apply(env.app(), view, stx, TapNone, j);
BEAST_EXPECT(!result.applied && result.ter == temINVALID);
return result.applied;
});
+10 -11
View File
@@ -103,8 +103,7 @@ struct Regression_test : public beast::unit_test::Suite
auto const jt = env.jt(pay(env.master, "alice", aliceAmount));
OpenView accum(&*next);
auto const result =
xrpl::apply(env.app(), accum, *jt.stx, ApplyFlags::None, env.journal);
auto const result = xrpl::apply(env.app(), accum, *jt.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(result.ter));
BEAST_EXPECT(result.applied);
@@ -127,8 +126,7 @@ struct Regression_test : public beast::unit_test::Suite
OpenView accum(&*next);
auto const result =
xrpl::apply(env.app(), accum, *jt.stx, ApplyFlags::None, env.journal);
auto const result = xrpl::apply(env.app(), accum, *jt.stx, TapNone, env.journal);
BEAST_EXPECT(result.ter == tecINSUFF_FEE);
BEAST_EXPECT(result.applied);
@@ -305,11 +303,12 @@ struct Regression_test : public beast::unit_test::Suite
return digest.asUInt256();
}();
auto const mapCounts = [] {
std::map<std::string, std::uint32_t> result;
for (auto const& c : gCountedObjects)
result.emplace(c.name(), c.count());
auto const mapCounts = [&](CountedObjects::List const& list) {
std::map<std::string, int> result;
for (auto const& e : list)
{
result[e.first] = e.second;
}
return result;
};
@@ -318,11 +317,11 @@ struct Regression_test : public beast::unit_test::Suite
{
auto& cache = env.app().getCachedSLEs();
cache.del(*digest, false); // NOLINT(bugprone-unchecked-optional-access)
auto const beforeCounts = mapCounts();
auto const beforeCounts = mapCounts(CountedObjects::getInstance().getCounts(0));
env(check::cash(alice, bobIndex, check::DeliverMin(XRP(100))), Ter(tecNO_ENTRY));
auto const afterCounts = mapCounts();
auto const afterCounts = mapCounts(CountedObjects::getInstance().getCounts(0));
using namespace std::string_literals;
BEAST_EXPECT(
+8 -10
View File
@@ -915,8 +915,8 @@ public:
sponsor::SponseeAcc(alice),
Seq(1),
Fee(baseFee));
auto const pf = preflight(
env.app(), env.current()->rules(), *jt.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jt.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
@@ -931,8 +931,8 @@ public:
sponsor::SponseeAcc(alice),
Seq(1),
Fee(baseFee));
auto const pf = preflight(
env.app(), env.current()->rules(), *jt.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jt.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
@@ -946,8 +946,8 @@ public:
sponsor::SponseeAcc(alice),
Seq(1),
Fee(baseFee));
auto const pf = preflight(
env.app(), env.current()->rules(), *jt.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jt.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
@@ -2242,8 +2242,7 @@ public:
Seq(env.seq(alice)),
sponsor::As(carol, spfSponsorFee));
auto const result =
xrpl::apply(env.app(), overlay, *jt.stx, ApplyFlags::None, env.journal);
auto const result = xrpl::apply(env.app(), overlay, *jt.stx, TapNone, env.journal);
BEAST_EXPECT(result.ter == tecINSUFF_FEE);
BEAST_EXPECT(result.applied);
@@ -2296,8 +2295,7 @@ public:
Fee(fixFee),
sponsor::As(sponsor, spfSponsorFee));
auto const pf =
preflight(env.app(), overlay.rules(), *jt.stx, ApplyFlags::None, env.journal);
auto const pf = preflight(env.app(), overlay.rules(), *jt.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
auto const pc = preclaim(pf, env.app(), overlay);
BEAST_EXPECT(isTesSuccess(pc.ter));
+1 -1
View File
@@ -252,7 +252,7 @@ class TheoreticalQuality_test : public beast::unit_test::Suite
std::shared_ptr<ReadView const> closed,
std::optional<Quality> const& expectedQ = {})
{
PaymentSandbox const sb(closed.get(), ApplyFlags::None);
PaymentSandbox const sb(closed.get(), TapNone);
AMMContext ammContext(rcp.srcAccount, false);
auto const sendMaxIssue = [&rcp]() -> std::optional<Asset> {
+9 -10
View File
@@ -989,8 +989,7 @@ public:
Env::ParsedResult parsed;
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
auto const result =
xrpl::apply(env.app(), view, *jt.stx, ApplyFlags::None, env.journal);
auto const result = xrpl::apply(env.app(), view, *jt.stx, TapNone, env.journal);
parsed.ter = result.ter;
return result.applied;
});
@@ -2425,8 +2424,8 @@ public:
env.memoize("carol");
{
auto const jtx = env.jt(offerCancel(alice, 3), Seq(5), Fee(10));
auto const pf = preflight(
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(10));
@@ -2437,8 +2436,8 @@ public:
auto usd = alice["USD"];
auto const jtx = env.jt(trust("carol", usd(50000000)), Seq(1), Fee(10));
auto const pf = preflight(
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(10));
@@ -2447,8 +2446,8 @@ public:
{
auto const jtx = env.jt(ticket::create(alice, 1), Seq(1), Fee(10));
auto const pf = preflight(
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
auto const pf =
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
BEAST_EXPECT(isTesSuccess(pf.ter));
BEAST_EXPECT(!pf.consequences.isBlocker());
BEAST_EXPECT(pf.consequences.fee() == drops(10));
@@ -4003,7 +4002,7 @@ public:
// be callable via RPC.)
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
auto const tx = env.jt(noop(alice), Seq(aliceSeq), Fee(openLedgerCost(env)));
auto const result = xrpl::apply(env.app(), view, *tx.stx, ApplyFlags::Unlimited, j);
auto const result = xrpl::apply(env.app(), view, *tx.stx, TapUnlimited, j);
BEAST_EXPECT(isTesSuccess(result.ter) && result.applied);
return result.applied;
});
@@ -4071,7 +4070,7 @@ public:
// be callable via RPC.)
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
auto const tx = env.jt(noop(alice), ticket::Use(tktSeq0 + 1), Fee(openLedgerCost(env)));
auto const result = xrpl::apply(env.app(), view, *tx.stx, ApplyFlags::Unlimited, j);
auto const result = xrpl::apply(env.app(), view, *tx.stx, TapUnlimited, j);
BEAST_EXPECT(isTesSuccess(result.ter) && result.applied);
return result.applied;
});
+1 -2
View File
@@ -122,8 +122,7 @@ InvariantsBase::doInvariantCheck(
OpenView ov{*env.current()};
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
// Invariants normally run in the Transaction's "apply" (operator()) context, and can always
// access global Rules.
@@ -811,13 +811,7 @@ class InvariantsMisc_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(),
ov,
tx,
tesSUCCESS,
env.current()->fees().base,
ApplyFlags::None,
jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleLoan = ac.view().peek(loanKeylet);
@@ -872,13 +866,7 @@ class InvariantsMisc_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(),
ov,
tx,
tesSUCCESS,
env.current()->fees().base,
ApplyFlags::None,
jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleLoan = ac.view().peek(loanKeylet);
@@ -991,13 +979,7 @@ class InvariantsMisc_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(),
ov,
tx,
tesSUCCESS,
env.current()->fees().base,
ApplyFlags::None,
jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleLoan = ac.view().peek(loanKeylet);
@@ -1435,7 +1417,7 @@ class InvariantsMisc_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sle = ac.view().peek(vaultKeylet);
@@ -1502,7 +1484,7 @@ class InvariantsMisc_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
// visitEntry only runs for entries the transaction touched, so
@@ -1559,8 +1541,7 @@ class InvariantsMisc_test : public InvariantsBase
STTx const tx{ttACCOUNT_SET, [](STObject&) {}};
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
FailingTxInvariantCheck failing;
@@ -581,7 +581,7 @@ class InvariantsPseudoAccount_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleBroker = ac.view().peek(brokerKeylet);
@@ -633,7 +633,7 @@ class InvariantsPseudoAccount_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleBroker = ac.view().peek(brokerKeylet);
@@ -681,7 +681,7 @@ class InvariantsPseudoAccount_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleBroker = ac.view().peek(brokerKeylet);
@@ -1088,13 +1088,7 @@ class InvariantsVault_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(),
ov,
tx,
tesSUCCESS,
env.current()->fees().base,
ApplyFlags::None,
jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleLoan = ac.view().peek(loanKeylet);
@@ -1146,7 +1140,7 @@ class InvariantsVault_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleLoan = ac.view().peek(loanKeylet);
@@ -1308,13 +1302,7 @@ class InvariantsVault_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(),
ov,
tx,
tesSUCCESS,
env.current()->fees().base,
ApplyFlags::None,
jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleLoan = ac.view().peek(loanKeylet);
@@ -1369,13 +1357,7 @@ class InvariantsVault_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(),
ov,
tx,
tesSUCCESS,
env.current()->fees().base,
ApplyFlags::None,
jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
auto sleLoan = ac.view().peek(loanKeylet);
@@ -1475,7 +1457,7 @@ class InvariantsVault_test : public InvariantsBase
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
CurrentTransactionRulesGuard const rulesGuard(ov.rules());
{
+2 -3
View File
@@ -1249,8 +1249,7 @@ class LoanBroker_test : public beast::unit_test::Suite
OpenView ov{*env.current()};
test::StreamSink sink{beast::Severity::Warning};
beast::Journal const jlog{sink};
ApplyContext ac{
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, ApplyFlags::None, jlog};
ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
if (auto sleBroker = ac.view().peek(keylet::loanBroker(brokerKeylet.key)))
{
@@ -1263,7 +1262,7 @@ class LoanBroker_test : public beast::unit_test::Suite
// Invoke preclaim against the mutated (ApplyView) view; triggers
// nullptr deref
PreclaimContext const pctx{env.app(), ac.view(), tesSUCCESS, tx, ApplyFlags::None, jlog};
PreclaimContext const pctx{env.app(), ac.view(), tesSUCCESS, tx, TapNone, jlog};
(void)LoanBrokerCoverDeposit::preclaim(pctx);
}
+1 -1
View File
@@ -108,7 +108,7 @@ private:
auto const peek = [keylet, &env, this](std::function<bool(SLE&, SLE&)> fn) -> bool {
return env.app().getOpenLedger().modify(
[&](OpenView& view, beast::Journal j) -> bool {
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
auto vault = sb.peek(keylet::vault(keylet.key));
if (!BEAST_EXPECT(vault))
return false;
+1 -1
View File
@@ -300,7 +300,7 @@ private:
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
for (auto const field : encryptedBalanceFields)
{
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
auto const token = sb.peek(tokenKeylet);
if (!BEAST_EXPECT(token))
return false;
+90
View File
@@ -0,0 +1,90 @@
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/beast/utility/Journal.h>
#include <string>
namespace beast {
class Journal_test : public unit_test::Suite
{
public:
class TestSink : public Journal::Sink
{
private:
int count_{0};
public:
TestSink() : Sink(Severity::Warning, false)
{
}
[[nodiscard]] int
count() const
{
return count_;
}
void
reset()
{
count_ = 0;
}
void
write(Severity level, std::string const&) override
{
if (level >= threshold())
++count_;
}
void
writeAlways(Severity level, std::string const&) override
{
++count_;
}
};
void
run() override
{
TestSink sink;
sink.threshold(Severity::Info);
Journal const j(sink);
j.trace() << " ";
BEAST_EXPECT(sink.count() == 0);
j.debug() << " ";
BEAST_EXPECT(sink.count() == 0);
j.info() << " ";
BEAST_EXPECT(sink.count() == 1);
j.warn() << " ";
BEAST_EXPECT(sink.count() == 2);
j.error() << " ";
BEAST_EXPECT(sink.count() == 3);
j.fatal() << " ";
BEAST_EXPECT(sink.count() == 4);
sink.reset();
sink.threshold(Severity::Debug);
j.trace() << " ";
BEAST_EXPECT(sink.count() == 0);
j.debug() << " ";
BEAST_EXPECT(sink.count() == 1);
j.info() << " ";
BEAST_EXPECT(sink.count() == 2);
j.warn() << " ";
BEAST_EXPECT(sink.count() == 3);
j.error() << " ";
BEAST_EXPECT(sink.count() == 4);
j.fatal() << " ";
BEAST_EXPECT(sink.count() == 5);
}
};
BEAST_DEFINE_TESTSUITE(Journal, beast, beast);
} // namespace beast
@@ -0,0 +1,19 @@
#include <xrpl/beast/clock/basic_seconds_clock.h>
#include <xrpl/beast/unit_test/suite.h>
namespace beast {
class BasicSecondsClock_test : public unit_test::Suite
{
public:
void
run() override
{
BasicSecondsClock::now();
pass();
}
};
BEAST_DEFINE_TESTSUITE(BasicSecondsClock, beast, beast);
} // namespace beast
+196
View File
@@ -0,0 +1,196 @@
#include <xrpl/beast/hash/xxhasher.h>
#include <xrpl/beast/unit_test/suite.h>
#include <cstdint>
namespace beast {
class XXHasher_test : public unit_test::Suite
{
public:
void
testWithoutSeed()
{
testcase("Without seed");
Xxhasher hasher{};
std::string objectToHash{"Hello, xxHash!"};
hasher(objectToHash.data(), objectToHash.size());
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 16042857369214894119ULL);
}
void
testWithSeed()
{
testcase("With seed");
Xxhasher hasher{static_cast<std::uint32_t>(102)};
std::string objectToHash{"Hello, xxHash!"};
hasher(objectToHash.data(), objectToHash.size());
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 14440132435660934800ULL);
}
void
testWithTwoSeeds()
{
testcase("With two seeds");
Xxhasher hasher{static_cast<std::uint32_t>(102), static_cast<std::uint32_t>(103)};
std::string objectToHash{"Hello, xxHash!"};
hasher(objectToHash.data(), objectToHash.size());
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 14440132435660934800ULL);
}
void
testBigObjectWithMultipleSmallUpdatesWithoutSeed()
{
testcase("Big object with multiple small updates without seed");
Xxhasher hasher{};
std::string objectToHash{"Hello, xxHash!"};
for (int i = 0; i < 100; i++)
{
hasher(objectToHash.data(), objectToHash.size());
}
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 15296278154063476002ULL);
}
void
testBigObjectWithMultipleSmallUpdatesWithSeed()
{
testcase("Big object with multiple small updates with seed");
Xxhasher hasher{static_cast<std::uint32_t>(103)};
std::string objectToHash{"Hello, xxHash!"};
for (int i = 0; i < 100; i++)
{
hasher(objectToHash.data(), objectToHash.size());
}
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 17285302196561698791ULL);
}
void
testBigObjectWithSmallAndBigUpdatesWithoutSeed()
{
testcase("Big object with small and big updates without seed");
Xxhasher hasher{};
std::string objectToHash{"Hello, xxHash!"};
std::string bigObject;
for (int i = 0; i < 20; i++)
{
bigObject += "Hello, xxHash!";
}
hasher(objectToHash.data(), objectToHash.size());
hasher(bigObject.data(), bigObject.size());
hasher(objectToHash.data(), objectToHash.size());
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 1865045178324729219ULL);
}
void
testBigObjectWithSmallAndBigUpdatesWithSeed()
{
testcase("Big object with small and big updates with seed");
Xxhasher hasher{static_cast<std::uint32_t>(103)};
std::string objectToHash{"Hello, xxHash!"};
std::string bigObject;
for (int i = 0; i < 20; i++)
{
bigObject += "Hello, xxHash!";
}
hasher(objectToHash.data(), objectToHash.size());
hasher(bigObject.data(), bigObject.size());
hasher(objectToHash.data(), objectToHash.size());
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 16189862915636005281ULL);
}
void
testBigObjectWithOneUpdateWithoutSeed()
{
testcase("Big object with one update without seed");
Xxhasher hasher{};
std::string objectToHash;
for (int i = 0; i < 100; i++)
{
objectToHash += "Hello, xxHash!";
}
hasher(objectToHash.data(), objectToHash.size());
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 15296278154063476002ULL);
}
void
testBigObjectWithOneUpdateWithSeed()
{
testcase("Big object with one update with seed");
Xxhasher hasher{static_cast<std::uint32_t>(103)};
std::string objectToHash;
for (int i = 0; i < 100; i++)
{
objectToHash += "Hello, xxHash!";
}
hasher(objectToHash.data(), objectToHash.size());
BEAST_EXPECT(static_cast<Xxhasher::result_type>(hasher) == 17285302196561698791ULL);
}
void
testOperatorResultTypeDoesNotChangeInternalState()
{
testcase("Operator result type doesn't change the internal state");
{
Xxhasher hasher;
std::string object{"Hello xxhash"};
hasher(object.data(), object.size());
auto xxhashResult1 = static_cast<Xxhasher::result_type>(hasher);
auto xxhashResult2 = static_cast<Xxhasher::result_type>(hasher);
BEAST_EXPECT(xxhashResult1 == xxhashResult2);
}
{
Xxhasher hasher;
std::string object;
for (int i = 0; i < 100; i++)
{
object += "Hello, xxHash!";
}
hasher(object.data(), object.size());
auto xxhashResult1 = hasher.operator Xxhasher::result_type();
auto xxhashResult2 = hasher.operator Xxhasher::result_type();
BEAST_EXPECT(xxhashResult1 == xxhashResult2);
}
}
void
run() override
{
testWithoutSeed();
testWithSeed();
testWithTwoSeeds();
testBigObjectWithMultipleSmallUpdatesWithoutSeed();
testBigObjectWithMultipleSmallUpdatesWithSeed();
testBigObjectWithSmallAndBigUpdatesWithoutSeed();
testBigObjectWithSmallAndBigUpdatesWithSeed();
testBigObjectWithOneUpdateWithoutSeed();
testBigObjectWithOneUpdateWithSeed();
testOperatorResultTypeDoesNotChangeInternalState();
}
};
BEAST_DEFINE_TESTSUITE(XXHasher, beast_core, beast);
} // namespace beast
+1 -1
View File
@@ -1902,7 +1902,7 @@ negUnlSizeTest(
bool
applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
{
auto const res = apply(env.app(), view, tx, ApplyFlags::None, env.journal);
auto const res = apply(env.app(), view, tx, ApplyFlags::TapNone, env.journal);
if (pass)
{
return isTesSuccess(res.ter);
+1 -1
View File
@@ -32,7 +32,7 @@ bumpLastPage(
{
std::expected<void, Error> res{};
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) -> bool {
Sandbox sb(&view, ApplyFlags::None);
Sandbox sb(&view, TapNone);
// Find the root page
auto sleRoot = sb.peek(directory);
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff. Show more